Skip to content

Commit e7f1e42

Browse files
committed
fix: make cdot anonymous function notation handle ambiguous notation
Fixes an issue where each alternative in choice nodes would get their own arguments. Now cdot function expansion is aware of choice nodes. Also modifies the variable naming so that multi-argument functions like `(· + ·)` expand as `fun x1 x2 => x1 + x2` rather than `fun x x_1 => x + x_1`. Closes #4832
1 parent 5938dbb commit e7f1e42

File tree

4 files changed

+65
-18
lines changed

4 files changed

+65
-18
lines changed

src/Lean/Elab/BuiltinNotation.lean

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -261,27 +261,62 @@ partial def hasCDot : Syntax → Bool
261261
- `f · · b` => `fun _a_1 _a_2 => f _a_1 _a_2 b` -/
262262
partial def expandCDot? (stx : Term) : MacroM (Option Term) := do
263263
if hasCDot stx then
264-
let (newStx, binders) ← (go stx).run #[]
264+
let numArgs ← countArgs stx
265+
let (newStx, binders) ← withFreshMacroScope <| (go numArgs stx).run #[]
265266
`(fun $binders* => $(⟨newStx⟩))
266267
else
267268
pure none
268269
where
269270
/--
270-
Auxiliary function for expanding the `·` notation.
271-
The extra state `Array Syntax` contains the new binder names.
272-
If `stx` is a `·`, we create a fresh identifier, store in the
273-
extra state, and return it. Otherwise, we just return `stx`. -/
274-
go : Syntax → StateT (Array Ident) MacroM Syntax
275-
| stx@`(($(_))) => pure stx
276-
| stx@`(·) => withFreshMacroScope do
277-
let id ← mkFreshIdent stx (canonical := true)
278-
modify (·.push id)
279-
pure id
280-
| stx => match stx with
281-
| .node _ k args => do
282-
let args ← args.mapM go
283-
return .node (.fromRef stx (canonical := true)) k args
284-
| _ => pure stx
271+
Count arguments, taking into consideration choice nodes
272+
and validating that each alternative has the same number of arguments.
273+
-/
274+
countArgs : Syntax → MacroM Nat
275+
| `(($_)) => return 0
276+
| `(·) => return 1
277+
| stx =>
278+
match stx with
279+
| .node _ k args => do
280+
if k == choiceKind then
281+
if args.isEmpty then
282+
Macro.throwUnsupported
283+
let counts ← args.mapM countArgs
284+
unless counts.all (fun c => counts[0]! == c) do
285+
Macro.throwErrorAt stx "Ambiguous notation in cdot function has different numbers of '·' arguments in each alternative."
286+
return counts[0]!
287+
else
288+
args.foldlM (init := 0) (fun acc arg => return acc + (← countArgs arg))
289+
| _ => return 0
290+
/--
291+
Auxiliary function for expanding the `·` notation.
292+
The extra state `Array Syntax` contains the new binder names.
293+
If `stx` is a `·`, we create a fresh identifier, store it in the
294+
extra state, and return it. Otherwise, we just return `stx`.
295+
-/
296+
go (numArgs : Nat) : Syntax → StateT (Array Ident) MacroM Syntax
297+
| stx@`(($(_))) => pure stx
298+
| stx@`(·) => do
299+
let i := (← get).size
300+
let name ← MonadQuotation.addMacroScope <|
301+
if numArgs == 1 then
302+
`x
303+
else
304+
Name.mkSimple s!"x{i + 1}"
305+
let id := mkIdentFrom stx name (canonical := true)
306+
modify (fun s => s.push id)
307+
pure id
308+
| stx => match stx with
309+
| .node _ k args => do
310+
let args ←
311+
if k == choiceKind then
312+
let s ← get
313+
let args' ← args.mapM (fun arg => go numArgs arg |>.run s)
314+
set args'[0]!.2
315+
pure <| args'.map Prod.fst
316+
else
317+
args.mapM (go numArgs)
318+
return .node (.fromRef stx (canonical := true)) k args
319+
| _ => pure stx
285320

286321
/--
287322
Helper method for elaborating terms such as `(.+.)` where a constant name is expected.

tests/lean/diamond1.lean.expected.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ toBar : Bar (α → α)
1111
c : Bool → α
1212
d : Nat
1313
def f : Nat → Foo Nat :=
14-
fun x => { a := fun y => x + y, b := fun x x_1 => x + x_1, c := fun x_1 => x, d := x }
14+
fun x => { a := fun y => x + y, b := fun x1 x2 => x1 + x2, c := fun x_1 => x, d := x }
1515
diamond1.lean:27:47-27:52: warning: field 'a' from 'Baz' has already been declared

tests/lean/interactive/hover.lean.expected.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@
407407
"end": {"line": 206, "character": 13}},
408408
"contents":
409409
{"value":
410-
"```lean\n?m x✝¹ x✝\n```\n***\n`a + b` computes the sum of `a` and `b`.\nThe meaning of this notation is type-dependent. ",
410+
"```lean\n?m x1✝ x2✝\n```\n***\n`a + b` computes the sum of `a` and `b`.\nThe meaning of this notation is type-dependent. ",
411411
"kind": "markdown"}}
412412
{"textDocument": {"uri": "file:///hover.lean"},
413413
"position": {"line": 215, "character": 28}}

tests/lean/run/cdotTests.lean

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,15 @@ rfl
3131

3232
theorem ex4 : sum [1, 2, 3, 4] = 10 :=
3333
rfl
34+
35+
/-
36+
Check that ambiguous notation inside cdot notation still has only a single argument.
37+
(Need to process choice nodes specially.)
38+
-/
39+
40+
def tag (_ : α) (y : α) := y
41+
notation "f" x => tag 1 x
42+
notation "f" x => tag "2" x
43+
/-- info: fun x => (f x).length : String → Nat -/
44+
#guard_msgs in
45+
#check (String.length <| f ·)

0 commit comments

Comments
 (0)