Conversation
Resolve conflicts from c0409bb 'Add tactic for simplifying termination proofs': - Use term_by_mem where applicable in Laurel termination proofs - Keep manual proofs where term_by_mem can't handle the goal (HighType recursion, attach patterns)
| inputs : List Parameter | ||
| outputs : List Parameter | ||
| precondition : WithMetadata StmtExpr | ||
| determinism : Determinism |
There was a problem hiding this comment.
What happened to determinism?
There was a problem hiding this comment.
Intentionally removed in 67931730 — it was only ever set to .nondeterministic and never pattern-matched on.
There was a problem hiding this comment.
I know it wasn't used but that doesn't mean it shouldn't be there. The whole AST wasn't used when it was first merged. Can you bring it back?
|
|
||
| theorem WithMetadata.sizeOf_val_lt {t : Type} [SizeOf t] (e : WithMetadata t) : sizeOf e.val < sizeOf e := by | ||
| cases e; grind | ||
| cases e <;> simp_wf <;> omega |
There was a problem hiding this comment.
Intentional. omega is simpler and more predictable than grind here, and it proves the goal.
| | _ => none | ||
|
|
||
| /-- Collect Array.Get accesses with constrained element types -/ | ||
| def collectConstrainedArrayAccesses (env : TypeEnv) (tcMap : TranslatedConstraintMap) (e : StmtExprMd) : List (StmtExprMd × StmtExprMd × TranslatedConstraint) := |
There was a problem hiding this comment.
Shouldn't the implementation of constraints be independent of the implementation of Arrays? If this is temporary code, can we mark it as such?
There was a problem hiding this comment.
Added a TODO. Array.Get is special because it's a built-in (SMT select), not a procedure call, so there's no contract to assume from. Other constrained type sources (procedure returns, field reads) get their assumes via contracts. If more built-ins return constrained types in the future, this should be generalized.
| let belse ← match elseBranch with | ||
| | some e => do let (_, s) ← translateStmt ctMap tcMap env outputParams postconds e; pure s | ||
| | none => pure [] | ||
| pure (env, arrayElemAssumes ++ [Imperative.Stmt.ite bcond bthen belse stmt.md]) |
There was a problem hiding this comment.
Why does the translation of IfThenElse refer to arrayElemAssumes? That can't be right
There was a problem hiding this comment.
Good catch — fixed in fb9c7f60. Compound statements (Block, IfThenElse, While) no longer generate arrayElemAssumes at the top level. Their sub-statements handle their own assumes via recursive translateStmt calls.
| (e', acc ++ ss)) (env, []) | ||
| (env', stmtsList) | ||
| | .LocalVariable name ty initializer => | ||
| def translateStmt (ctMap : ConstrainedTypeMap) (tcMap : TranslatedConstraintMap) (env : TypeEnv) (outputParams : List Parameter) (postconds : List (String × Core.Expression.Expr)) (stmt : StmtExprMd) : Except String (TypeEnv × List Core.Statement) := do |
There was a problem hiding this comment.
Why would you introduce assert statements related to postconditions? Core will already check that procedure bodies adhere to the postconditions of that procedure.
There was a problem hiding this comment.
These asserts are needed for early returns. Laurel's return is translated to result := value; assert postconditions; assume false. The assume false prevents fall-through but also makes the path vacuously true, so Core's VCG won't check postconditions on it.
Without the asserts, a postcondition-violating early return passes verification silently. I verified this by writing a Core program where the early return path sets result := -x (violating ensures result >= 0):
- Without the assert before
assume false: ✅ pass (unsound) - With the assert before
assume false: ❌ fail (correct)
There was a problem hiding this comment.
Laurel's return is translated to
result := value; assert postconditions; assume false
Is that new? It's not mentioned in the PR. I think it would be better to encode a return using a jump than an assume false. With the assume false it would be incorrect when executing the code, since then the assume is ignored, and having to assert the postconditions introduces a lot of duplication.
There was a problem hiding this comment.
You're right that goto would be better. I tried implementing it (return value → result := value; goto "\$return" with \$return: {} at the end of the body), and it works correctly for individual procedures.
However, I found a Core VCG issue: when a procedure has a goto inside an if branch, processIteBranches produces multiple EnvWithNext results, and these multiply across procedures via ProgramEval.flatMap. A second procedure's assertions get checked N times instead of once.
Filed as #419 with a proposed fix (merge paths by nextLabel in processIteBranches). Keeping assume false for now until that's resolved.
There was a problem hiding this comment.
However, I found a Core VCG issue
Nice find! How did you find that?
I'm hesitant to merge the assert+assume solution for reasons that I think you identified as well:
- It works with an analysis that respects
assume falsebut not one that ignores it, like concrete execution. - It duplicates the postconditions for each return so it breaks our goal of having transformations that are linear in size.
The current PythonToCore translation uses the goto approach, so to support moving that over to Laurel, we could use the goto approach as well.
I'm also ok with using the assert+assume approach if we sufficiently document that it is a temporary solution.
…aints - Don't generate arrayElemAssumes for compound statements (Block, IfThenElse, While). Their sub-statements handle their own assumes via recursive translateStmt calls. Previously, assumes for array accesses inside branches were incorrectly hoisted before the if/while statement. - Add TODO comment explaining why constraint injection is array-specific: Array.Get is a built-in (SMT select), not a procedure call, so there's no contract to assume from.
joehendrix
left a comment
There was a problem hiding this comment.
I added a few suggestions, but this looks reasonable to me. I'll approve this if you don't like the suggestions.
- Parser.lean: use Id.run do + early return for comment parsing, replace startsWith with utf8ByteSize check (comment 6) - Java/Gen.lean: match on DDM category name (Init.Num/Init.Decimal) instead of Java type string for builder parameter generation (comment 7) - Format.lean: collapse comma/space/newline seq formatting into single sepBy arm, eliminating duplication (comment 8) - Java/Gen.lean: consolidate getSeparator with SepFormat.toIonName by adding fromCategoryName? and moving Ion-specific functions to Strata.DDM.Ion (comment 9)
80b2cc2 to
88d1a43
Compare
| @@ -1 +1 @@ | |||
| /- | |||
There was a problem hiding this comment.
Why the rename of this test?
| body := body | ||
| }) .empty | ||
|
|
||
| def arrayTypeSynonym : Core.Decl := .type (.syn { name := "Array", typeArgs := ["T"], type := .tcons "Map" [.int, .ftvar "T"] }) |
There was a problem hiding this comment.
Can you move these static Core definition to CorePrelude.lean ?
| explicitPreconditions := explicitPreconditions ++ [(s!"{proc.name}_pre_{i}", check)] | ||
| let preconditions := inputConstraints ++ arrayLenConstraints ++ explicitPreconditions | ||
| -- Generate constraint checks for output parameters with constrained types | ||
| let outputConstraints : List (Core.CoreLabel × Core.Procedure.Check) ← |
There was a problem hiding this comment.
Can you generate the implicit pre and postconditions in a separate pass in a separate file?
| | some expr => pure (some (s!"{proc.name}_input_{p.name}_constraint", { expr, md := p.type.md })) | ||
| | none => pure none) | ||
| -- Array lengths are implicitly >= 0 | ||
| let arrayLenConstraints : List (Core.CoreLabel × Core.Procedure.Check) := |
There was a problem hiding this comment.
Can you move everything related to arrays to a separate file? Can you elaborate on the design of Array support? How come it's not a Laurel library? The length constraint seems like a type invariant or constrained type.
|
|
||
| /-- Extract sequence bounds from Seq.From/Take/Drop chain. | ||
| Note: Seq.From only works with simple Identifier arguments; complex expressions are not supported. -/ | ||
| def translateSeqBounds (env : TypeEnv) (expr : StmtExprMd) : Except String SeqBounds := |
There was a problem hiding this comment.
Can everything related to sequences be in a separate file? Can it be a separate pass?
| @@ -0,0 +1,42 @@ | |||
| /- | |||
There was a problem hiding this comment.
Can you move and Sequence support tests into a separate folder?
|
Can you add Laurel test |
| match outputParams.head? with | ||
| | some _ => do | ||
| let coreExpr ← translateExpr ctMap tcMap env stmt | ||
| mkReturnStmts (some coreExpr) |
There was a problem hiding this comment.
Is this the scenario where we return "naturally" because we reach the end of a block? Since this is not a jump, should we not call something different than mkReturnStmt which does do a jump?
| op assume (cond : StmtExpr) : StmtExpr => "assume " cond ";"; | ||
| op return (value : StmtExpr) : StmtExpr => "return " value ";"; | ||
| op block (stmts : Seq StmtExpr) : StmtExpr => @[prec(1000)] "{" stmts "}"; | ||
| op block (stmts : NewlineSepBy StmtExpr) : StmtExpr => @[prec(1000)] "{" indent(2, "\n" stmts) "\n}"; |
There was a problem hiding this comment.
The indent(2, is never used anywhere, right? I don't think we're ever printing Laurel through the DDM. Why did you add this?
| return commands | ||
| let (some tree, true) ← runChecked <| elabCommand leanEnv | ||
| | return commands | ||
| -- Prevent infinite loop if parser makes no progress |
| | q`Init.SpacePrefixSepBy, 1 => | ||
| let inner := mkCApp ``Array #[args[0]] | ||
| return if addAnn then mkCApp ``Ann #[inner, annType] else inner | ||
| | q`Init.NewlineSepBy, 1 => |
There was a problem hiding this comment.
Can you say more about this NewlineSepBy operator? What is it for?
Contains a subset of the changes from #385 ### Changes - Fix to Strata/DDM/Parser.lean so it can handle parsing comments `//` in languages that have a division operator - New operators: `-`, `*`, `/`, `%`, `/t`, `%t` (truncating), `==>`, `!`, unary `-` - Quantifiers: `forall`, `exists` - While loops with multiple invariants ### Testing - Added tests for the new features --------- Co-authored-by: Andrew Wells <130512013+andrewmwells-amazon@users.noreply.github.com>
Summary
Expands the Laurel language with additional operators, data types, and verification constructs. Improves formatting across multiple dialects.
Key Changes
Laurel Grammar & Translator
-,*,/,%,/t,%t(truncating),==>,!, unary-Array<T>type, indexing, lengthSeq.From,Seq.Take,Seq.Drop,Seq.Containsforall,existsrequiresclauses per procedureInfrastructure
NewlineSepByseparator type for formatting preservationSourceRangeoverloadsCLI Commands
laurelParse,laurelAnalyze,laurelToCore,laurelPrintBug Fixes
MaptypesubstFvarLiftingfor proper de Bruijn index handlingTests