Zulip Chat Archive

Stream: std4

Topic: Problem with `repeat'`


Scott Morrison (Oct 19 2023 at 07:42):

I'm having a problem with repeat'. Here's the smallest minimisation I have so far:

import Std.Tactic.Basic

open Lean Elab Tactic Meta

def Lean.MVarId.assignFresh (mvarId : MVarId) : MetaM MVarId := do
  let mvarIdNew  mkFreshExprMVar ( mvarId.getType)
  mvarId.assign mvarIdNew
  return mvarIdNew.mvarId!

elab "foo" : tactic => liftMetaTactic fun g => do _  g.assignFresh; throwError ""

example : False := by -- (kernel) declaration has metavariables '_example'
  repeat' foo

Scott Morrison (Oct 19 2023 at 07:43):

This also happens with g.assignFresh replaced by g.exFalso.

Scott Morrison (Oct 19 2023 at 07:43):

This is minimised from the even shorter:

import Mathlib.Tactic.Linarith

example : False := by -- (kernel) declaration has metavariables '_example'
  repeat' linarith

Scott Morrison (Oct 19 2023 at 07:43):

@Mario Carneiro, do you see what is wrong here? I think it must be repeat's fault.

Mario Carneiro (Oct 19 2023 at 07:52):

Your foo is breaking the contract that repeat' expects. Specifically, if you throw an exception, then repeat' will put the original goal back on the goal list, so it should not be assigned. If it is assigned then it will be pruned from the list, so in this example that means that mvarIdNew is dropped on the floor and you have an empty goal list even though there are metavariables remaining

Scott Morrison (Oct 19 2023 at 07:53):

Would it be okay to change repeat' so it rewinds the MetavarContext on an exception?

Scott Morrison (Oct 19 2023 at 07:54):

This seems like a nasty footgun (as evidenced by the repeat' linarith example).

Mario Carneiro (Oct 19 2023 at 07:54):

is it something that can be fixed in linarith?

Scott Morrison (Oct 19 2023 at 07:55):

But... but... where is the contract that says that failing tactics are responsible for rewinding all of their state? That's not a universal expectation, is it?

Scott Morrison (Oct 19 2023 at 07:55):

I thought this was unreasonable expectations on the part of repeat', not bad behaviour on the part of linarith.

Mario Carneiro (Oct 19 2023 at 07:56):

repeat' essentially uses a try catch block to catch exceptions in the inner function. This does not rewind the state by default

Mario Carneiro (Oct 19 2023 at 07:56):

it is not clear to me if rewinding the state is desirable in general

Scott Morrison (Oct 19 2023 at 07:58):

I'm worried that we have lots of tactics out there that error without rewinding the state, and repeat' is not going to be usable with any othem.

Mario Carneiro (Oct 19 2023 at 07:59):

fixed (I had to change observing to observing?... it is not good that such similarly sounding functions have this different behavior)

Mario Carneiro (Oct 19 2023 at 08:08):

std#305

Scott Morrison (Oct 19 2023 at 08:14):

Okay, and #7769 is the matching mathlib PR that makes sure linarith rolls back the state as well. Defence in depth!


Last updated: Dec 20 2023 at 11:08 UTC