agda icon indicating copy to clipboard operation
agda copied to clipboard

Internal error with generalized variable in definition inside record

Open szumixie opened this issue 2 months ago • 1 comments

I get an internal error with the following code:

postulate
  P : (A : Set) → A → Set
  F : Set → Set
  g : (A : Set) → F A

variable A : Set

record R : Set₁ where
  h : A → P ? (g ?)
  h = ?

  field B : Set
conApp: constructor Temp.R..mkGeneralizeTel with fields
  Temp.R..generalizedField-A
 and args
  {@0}
 projected by Temp..generalizedField-A
An internal error has occurred. Please report this as a bug.
Location of the error: __IMPOSSIBLE__, called at src/full/Agda/TypeChecking/Substitute.hs:145:33 in Agda-2.9.0-9us0L8SqiHiLjIfhQSrY07:Agda.TypeChecking.Substitute

If I fill in the first hole, then I get a nonsensical error:

  h : A → P (F A) (g ?)
error: [UnequalTerms]
A != A
(although these terms are looking the same, they contain different
 but identically rendered identifiers somewhere)
when checking that the expression g ? has type F A

If I replace the holes with underscores, then the errors disappear.

szumixie avatar Nov 01 '25 21:11 szumixie

The root of the problem is how we type-check records (#434), and a similar issue was #5478:

  • #434
  • #5478

Let's follow along with this slightly modified version of the OP, where I renamed the generalized variable to G:

{-# OPTIONS --show-implicit #-}
{-# OPTIONS -v tc.generalize:10 #-}
{-# OPTIONS -v tc.rec:40 #-}
{-# OPTIONS -v tc.interaction:30 #-}

postulate
  P : (A : Set) → A → Set
  F : Set → Set
  g : (A : Set) → F A

variable G : Set

record R : Set₁ where
  h : G → P ? (g ?)
  h = {!!}

  field B : Set

The problem is that Agda has already allocated the metas for the ?s during construction of the record constructor. This triggered already a generalization even though the final constructor type (Set -> R) does not mention any generalized variables.

checking record def R
  ps = []
  contel = (let h : G → P ? (g ?)
                h = ?)
           (B : Set) →
           Set
  fields = mutual
             syntax h ...
             postulate h : G → P ? (g ?)
             h = ?
           syntax B ...
checking fields
Found interaction point ConcreteDef ?0 : Set
Found interaction point ConcreteDef ?1 : Set
computing generalization for type _3
we're generalizing over _G_4

We then insert the record "self" variable r to check the declarations in the record (a second time, first time was during construction of the constructor):

record section: R (r : R) [], [Issue8182.R.B]
  field tel = (B : Set)

We encounter the interaction points a second time, triggering a heuristics that tries to reuse the meta we created for them the first time:

Found interaction point ConcreteDef ?0 : Set
reusing meta
  creation context: [(genTel : Issue8182..GeneralizeTel)]
  reusage  context: [(genTel : _11 (r = genTel)), (r : R)]

This goes well for ?0 because they share the context [genTel].

meta reuse arguments: [genTel]

It does not go well for ?1 though:

Found interaction point ConcreteDef ?1 : Set
reusing meta
  creation context: [{G : Set}]
  reusage  context: [(genTel : _11 (r = genTel)), (r : R)]

Here we strangely have [G] as original context and not [genTel]. I don't know why this is so, looks like a problem with generalization. Things go awry. Agda does not recognize G and thinks it must be a record field. Subsequently it produces garbage meta reuse arguments, leading to an internal error.

What I did for now (#8185) is another sanity check that checks the validity of supposed fields by their concrete name. This mismatch will now trigger an internal error in a more suitable place.

  fs    = [(Issue8182.R.B)]
  gfs   = [G]

However, to fix the issue one would have to understand why we see G instead of genTel for ?1. Also, the "meta reuse heuristics" is a terrible hack I wish we did not need in the first place, so the proper fix would be to rewrite the scope and type checker for record declarations (as outlined in #434).

andreasabel avatar Nov 03 '25 10:11 andreasabel