This PR fixes and overeager reuse of specialisation in the code generator. The issue was originally discovered in https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Miscompilation.20.28incorrect.20code.29.20in.20new.20compiler/near/540037917 and occurs because the specialisation cache didnt't take the name of alternatives in pattern matches into account.
135 lines
5.1 KiB
Text
135 lines
5.1 KiB
Text
import Lean
|
|
open Lean Parser
|
|
|
|
/-!
|
|
This test accounts for a specialisation cache bug that was discovered in the Zulip thread
|
|
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Miscompilation.20.28incorrect.20code.29.20in.20new.20compiler/near/540037917
|
|
if the bug is around the eval at the end of the file evaluates to true instead of false.
|
|
-/
|
|
|
|
namespace Veil
|
|
|
|
inductive Mutability where
|
|
| mutable
|
|
| immutable
|
|
| inherit
|
|
deriving Inhabited, BEq, Hashable, Repr
|
|
|
|
inductive StateComponentType where
|
|
| simple (t : TSyntax ``Command.structSimpleBinder)
|
|
| complex (binders : TSyntaxArray ``Term.bracketedBinder) (dom : Term)
|
|
deriving Inhabited, BEq
|
|
|
|
structure StateComponent where
|
|
mutability : Mutability
|
|
name : Name
|
|
type : StateComponentType
|
|
deriving Inhabited, BEq
|
|
|
|
structure Module where
|
|
name : Name
|
|
signature : Array StateComponent
|
|
deriving Inhabited
|
|
|
|
end Veil
|
|
|
|
section Util
|
|
set_option autoImplicit true
|
|
def getSimpleBinderType [Monad m] [MonadError m] (sig : TSyntax `Lean.Parser.Command.structSimpleBinder) : m (TSyntax `term) := do
|
|
match sig with
|
|
| `(Lean.Parser.Command.structSimpleBinder| $_:ident : $tp:term) => pure tp
|
|
| _ => throwError s!"getSimpleBinderType: don't know how to handle {sig}"
|
|
|
|
def mkArrowStx [Monad m] [MonadQuotation m] [MonadError m] (tps : List Term) (terminator : Option $ TSyntax `term := none) : m (TSyntax `term) := do
|
|
match tps with
|
|
| [] => if let some t := terminator then return t else throwError "empty list of types and no terminator"
|
|
| [a] => match terminator with
|
|
| none => `(term| $a)
|
|
| some t => `(term| $a -> $t)
|
|
| a :: as =>
|
|
let cont ← mkArrowStx as terminator
|
|
`(term| $a -> $cont)
|
|
|
|
def complexBinderToSimpleBinder [Monad m] [MonadQuotation m] [MonadError m] (nm : TSyntax `ident) (br : TSyntaxArray `Lean.Parser.Term.bracketedBinder) (domT : TSyntax `term) : m (TSyntax `Lean.Parser.Command.structSimpleBinder) := do
|
|
let types ← br.mapM fun m => match m with
|
|
| `(bracketedBinder| ($_arg:ident : $tp:term)) => return tp
|
|
| _ => throwError "Invalid binder syntax {br}"
|
|
let typeStx ← mkArrowStx types.toList domT
|
|
let simple ← `(Lean.Parser.Command.structSimpleBinder| $nm:ident : $typeStx)
|
|
return simple
|
|
end Util
|
|
|
|
open Lean Parser Elab Command Term
|
|
|
|
namespace Veil
|
|
|
|
instance : ToString Mutability where
|
|
toString
|
|
| Mutability.mutable => "mutable"
|
|
| Mutability.immutable => "immutable"
|
|
| Mutability.inherit => "inherit"
|
|
|
|
instance : ToString StateComponentType where
|
|
toString
|
|
| StateComponentType.simple t => toString t
|
|
| StateComponentType.complex b d => s!"{b} : {d}"
|
|
|
|
instance : ToString StateComponent where
|
|
toString sc := s!"{sc.mutability} {sc.name} {sc.type}"
|
|
|
|
def StateComponentType.stx [Monad m] [MonadQuotation m] [MonadError m] (sct : StateComponentType) : m (TSyntax `term) := do
|
|
match sct with
|
|
| .simple t => getSimpleBinderType t
|
|
| .complex b d => getSimpleBinderType $ ← complexBinderToSimpleBinder (mkIdent Name.anonymous) b d
|
|
|
|
/-- Returns, e.g., `initial_msg : address → address → round → value → Prop` -/
|
|
def StateComponent.getSimpleBinder [Monad m] [MonadQuotation m] [MonadError m] (sc : StateComponent) : m (TSyntax ``Command.structSimpleBinder) := do
|
|
match sc.type with
|
|
| .simple t => return t
|
|
| .complex b d => return ← complexBinderToSimpleBinder (mkIdent sc.name) b d
|
|
|
|
def StateComponent.stx {m} [Monad m] [MonadQuotation m] [MonadError m] (sc : StateComponent) : m Syntax := sc.getSimpleBinder
|
|
def StateComponent.typeStx [Monad m] [MonadQuotation m] [MonadError m] (sc : StateComponent) : m Term := sc.type.stx
|
|
|
|
def Module.getTheoryBinders [Monad m] [MonadQuotation m] [MonadError m] (mod : Module) : m (Array (TSyntax `Lean.Parser.Term.bracketedBinder)) := do
|
|
mod.signature.filterMapM fun sc => do
|
|
match sc.mutability with
|
|
| .immutable => return .some $ ← `(bracketedBinder| ($(mkIdent sc.name) : $(← sc.typeStx)))
|
|
| _ => pure .none
|
|
|
|
def Module.getStateBinders [Monad m] [MonadQuotation m] [MonadError m] (mod : Module) : m (Array (TSyntax `Lean.Parser.Term.bracketedBinder)) := do
|
|
let res ← mod.signature.filterMapM fun sc => do
|
|
match sc.mutability with
|
|
| .mutable =>
|
|
return .some $ ← `(bracketedBinder| ($(mkIdent sc.name) : $(← sc.typeStx)))
|
|
| _ =>
|
|
-- NOTE 1: uncommenting this line makes `trigger` work correctly
|
|
pure .none
|
|
return res
|
|
|
|
-- NOTE 2: commenting out this function (or the `trace` statement) makes `trigger` work correctly
|
|
def interference (mod : Module) : CommandElabM Unit := do
|
|
trace[veil.debug] "theory binders: {← mod.getTheoryBinders}"
|
|
return
|
|
|
|
end Veil
|
|
|
|
namespace Veil
|
|
|
|
open Lean Parser Elab Command
|
|
def trigger : CommandElabM Bool := do
|
|
let sig : Array StateComponent := #[
|
|
{ mutability := .mutable, name := `leader, «type» := .simple $ ← `(Command.structSimpleBinder| x : Nat → Bool)},
|
|
{ mutability := .mutable, name := `pending, «type» := .simple $ ← `(Command.structSimpleBinder| pending : Nat → Nat → Bool)}
|
|
]
|
|
let emptyMod : Module := default
|
|
let mod := { emptyMod with signature := sig}
|
|
let binders ← mod.getStateBinders
|
|
return binders.isEmpty
|
|
|
|
/-- info: false -/
|
|
#guard_msgs in
|
|
#eval trigger
|
|
|
|
end Veil
|
|
|