agda / agda

Agda is a dependently typed programming language / interactive theorem prover.

Home Page:https://wiki.portal.chalmers.se/agda/pmwiki.php

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Why do we `reduce` and `instantiateFull` constraints?

jespercockx opened this issue · comments

When playing around with instances, I noticed that Agda was reducing them even when doing so is not needed. Here is an example (with a looping function to clearly demonstrate the unwanted behavior):

open import Agda.Builtin.Nat

instance
  {-# TERMINATING #-}
  loop : Nat
  loop = loop

get : {{Nat}}  Nat
get {{x}} = x

test : Nat
test = get

I would expect this to type-check, but instead Agda goes into a loop when type-checking the definition of test. Looking at the debug output, the last it says is the following:

adding constraint Resolve instance argument _4 : Nat
                  Candidates loop : Nat
                    (stuck) unblocker:  any()

This strongly suggests that the following call to reduce is responsible:

-- Need to reduce to reveal possibly blocking metas
c <- reduce =<< instantiateFull c

The comment mentions that reduce and instantiateFull are necessary to reveal blocking metas, but the only blocker that is used is the one that is passed into the addConstraintTCM, not any metas from the term (reduced or not).

The call seems to have been introduced 13 years ago by @UlfNorell for the new constraint machinery at that time:

c172e4e

This call will reduce and fully instantiate every constraint that we postpone, so I think it's worth investigating whether this call is actually still required.

Of course, changing such a long-standing call to instantiateFull reveals warts where we rely implicitly on terms being fully instantiated. There are two in the Succeed test suite:

  • The test case for #4687 fails because the dropSameCandidates breaks. The problem here is that the non-reduced candidates have "fresh" metavariables in them that block the equality check from happening. This could presumably be fixed by calling reduce in this function right before the fresh variable check.
  • The test case LaterPrims also breaks with a bunch of unsolved constraints, I'm not sure why but it might have something to do with the CheckLockedVars constraint.

It also causes problems in the standard library:

Failed to solve the following constraints:
  _f_185 y v = _f_185 y v : A (blocked on _f_185)
  _f_185 (x • y) (x • z) = (x • y) ◦ (x • z) : A (blocked on _f_185)
  (y • x) ◦ (z • x) = _f_185 (y • x) (z • x) : A (blocked on _f_185)
  y ◦ z = _f_185 y z : A (blocked on _f_185)
  _f_185 y z = y ◦ z : A (blocked on _f_185)
  _f_164 y v = _f_164 y v : A (blocked on _f_164)
  _f_164 (y • x) (z • x) = (y • x) ◦ (z • x) : A (blocked on _f_164)
  (x • y) ◦ (x • z) = _f_164 (x • y) (x • z) : A (blocked on _f_164)
  y ◦ z = _f_164 y z : A (blocked on _f_164)
  _f_164 y z = y ◦ z : A (blocked on _f_164)
when scope checking the declaration
  import Algebra.Consequences.Propositional
/home/jesper/agda/std-lib/Everything.agda:42,1-42

This requires some further investigation.

Related: #3094 (except here we are not only calling instantiateFull but also reduce)