Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Strata/Languages/Laurel/Resolution.lean
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ structure ResolveState where
/-- When resolving inside an instance procedure, the owning composite type name.
Used by `resolveFieldRef` to resolve `self.field` when `self` has type `Any`. -/
instanceTypeName : Option String := none
/-- True when resolving inside an expression where the value is used (e.g., as an
argument to another call or operator). Multi-output calls are only diagnosed
in value context, not in statement position or direct assignment RHS. -/
inValueContext : Bool := false

@[expose] abbrev ResolveM := StateM ResolveState

Expand Down Expand Up @@ -437,10 +441,30 @@ def resolveStmtExpr (exprMd : StmtExprMd) : ResolveM StmtExprMd := do
| .StaticCall callee args =>
let callee' ← resolveRef callee source
(expected := #[.parameter, .staticProcedure, .datatypeConstructor, .constant])
-- Resolve arguments in value context (their results are used as values)
let saved := (← get).inValueContext
modify fun s => { s with inValueContext := true }
let args' ← args.mapM resolveStmtExpr
modify fun s => { s with inValueContext := saved }
-- Multi-output procedures must not appear in value context: the extra
-- outputs (e.g. error channels) would be silently discarded.
let s ← get
if s.inValueContext then
let outputCount := match s.scope.get? callee'.text with
| some (_, .staticProcedure proc) => proc.outputs.length
| some (_, .instanceProcedure _ proc) => proc.outputs.length
| _ => 0
if outputCount > 1 then
let diag := diagnosticFromSource source
s!"Multi-output procedure '{callee'.text}' used in expression position; it returns {outputCount} values but only one can be used here. Use a multi-target assignment instead."
modify fun s => { s with errors := s.errors.push diag }
pure (.StaticCall callee' args')
| .PrimitiveOp op args =>
Comment thread
tautschnig marked this conversation as resolved.
-- Resolve arguments in value context
let saved := (← get).inValueContext
modify fun s => { s with inValueContext := true }
let args' ← args.mapM resolveStmtExpr
modify fun s => { s with inValueContext := saved }
pure (.PrimitiveOp op args')
| .New ref =>
let ref' ← resolveRef ref source
Expand Down
13 changes: 13 additions & 0 deletions StrataTest/Languages/Laurel/ResolutionKindTests.lean
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,17 @@ composite Foo extends nat { }
#guard_msgs (error, drop all) in
#eval testInputWithOffset "ExtendConstrained" extendConstrained 90 processResolution

/-! ## Multi-output procedure used in expression position -/

def multiOutputInExpr := r"
procedure multi(x: int) returns (a: int, b: int) opaque;
procedure test() opaque {
assert multi(1) == 1
// ^^^^^^^^ error: Multi-output procedure 'multi' used in expression position
};
"

#guard_msgs (error, drop all) in
#eval testInputWithOffset "MultiOutputInExpr" multiOutputInExpr 100 processResolution

end Laurel
Loading