Zulip Chat Archive

Stream: lean4

Topic: Interesting `withAssignableSyntheticOpaque` `isDefEq` bug


Thomas Murrills (Nov 06 2023 at 06:48):

I figure this has to do with delayed-assigned metavariables. Consider the following apparent refine bug:

example : (fun _ : Nat => 2) = (fun _ => 2) := by
  refine (?_ : (fun _ : Nat => ?e) = _) -- unknown free variable '_uniq.39413'

This seems to actually be due to elabTermEnsuringType:

import Lean

open Lean Meta Elab Tactic in
elab "test1 " stx:term : tactic => withMainContext do
  discard <| elabTermEnsuringType (←`(term|(?_ : $stx))) ( getMainTarget)

example : (fun _ : Nat => 2) = (fun _ => 2) := by
  test1 (fun _ : Nat => ?e) = _ -- unknown free variable '_uniq.46964'

which in turn seems to be due to an issue with isDefEq under withAssignableSyntheticOpaque:

import Lean

open Lean Meta Elab Tactic in
elab "test2 " stx:term : tactic => withMainContext do
  let e  elabTerm stx none
  discard <| withAssignableSyntheticOpaque <| isDefEq e ( getMainTarget)

example : (fun _ : Nat => 2) = (fun _ => 2) := by
  test2 (fun _ : Nat => ?e) = _ -- unknown free variable '_uniq.48582'

Removing withAssignableSyntheticOpaque fixes the issue.

Note: replacing the underscore on the rhs of the = with the explicit term fixes the issue as well.

Kyle Miller (Nov 06 2023 at 19:32):

It seems to be that there's a bug in how ?... holes are abstracted? Just elaborating the term fun _ : Nat => ?_ leads to an expression with a metavariable that (through delayed assignment) is a metavariable with a type with the offending unbound fvar.

import Lean

open Lean Meta Elab Tactic in
elab "test" stx:term : tactic => withMainContext do
  let e  Term.elabTerm stx none
  logInfo m!"e = {e}"
  let mvs  getMVars e
  logInfo m!"mvars = {mvs.map Expr.mvar}\n{← mvs.mapM MVarId.isDelayedAssigned}"

example : False := by
  test fun _ : Nat => ?_
  /-
  e = fun x ↦ ?m.4341 x
  mvars = [?m.4341, ?m.4339]
  [true, false]
  -- ?m.4341 : (x : Nat) → ?m.4340 x
  -- ?m.4339 : ?m.4340 _uniq.4336
  -/
  test fun _ : Nat => _
  /-
  e = fun x ↦ ?m.4348 x
  mvars = [?m.4348]
  [false]
  -- ?m.4348 : (x : Nat) → ?m.4347 x
  -/

Thomas Murrills (Nov 06 2023 at 19:52):

Oh interesting! Hmmm...the offending fvar is present in local context of the mvar that has that fvar in its type, at least, though.

import Lean

open Lean Meta Elab Tactic

def showLCtxFVars (m : MVarId) : MetaM (List Name) := m.withContext do
  return ( getLCtx).fvarIdToDecl.toList.map (·.1.name)

elab "test" stx:term : tactic => withMainContext do
  let e  Term.elabTerm stx none
  logInfo m!"e = {e}"
  let mvs  getMVars e
  let fvars  liftM <| mvs.mapM showLCtxFVars
  let types  liftM <| mvs.mapM MVarId.getType
  logInfo m!"mvars = {mvs.map Expr.mvar}\n{fvars}\n{types}\n{← mvs.mapM MVarId.isDelayedAssigned}"

example : False := by
  test fun _ : Nat => ?_
  /-
  mvars = [?m.7027, ?m.7025]
  [[_uniq.7019], [_uniq.7019, _uniq.7022]]
  [(x : Nat) → ?m.7026 x, ?m.7026 _uniq.7022]
  [true, false]
  -/

If all the eventual delayed-assignment-related substitutions worked as intended, this would be ok, right?


Last updated: Dec 20 2023 at 11:08 UTC