agda / agda

Agda is a dependently typed programming language / interactive theorem prover.
https://wiki.portal.chalmers.se/agda/pmwiki.php
Other
2.41k stars 339 forks source link

with-abstraction regression #7295

Closed UlfNorell closed 3 weeks ago

UlfNorell commented 4 weeks ago

The following works in agda-2.6.4.3 but fails on master.

open import Agda.Builtin.Nat
open import Agda.Builtin.Bool
open import Agda.Builtin.Equality

postulate
  IsPreorder : {A : Set} → (A → A → Set) → (A → A → Set) → Set₁

module NatOrder where
  postulate
    _≤_ : Nat → Nat → Set
    ≤-isPreorder : IsPreorder _≡_ _≤_

module BoolOrder where
  postulate
    _≤_ : Bool → Bool → Set
    ≤-isPreorder : IsPreorder _≡_ _≤_

record HasPreorder (A : Set) (_≈_ : A → A → Set) : Set₁ where
  infix 4 _≤_
  field
    _≤_          : A → A → Set
    ≤-isPreorder : IsPreorder _≈_ _≤_

open HasPreorder ⦃...⦄ public

_∋_ : ∀ {a} (A : Set a) → A → A
A ∋ x = x

instance
  Nat-hasPreorder  = HasPreorder _ _ ∋ record {NatOrder}
  Bool-hasPreorder = HasPreorder _ _ ∋ record {BoolOrder}

data Dec (P : Set) : Set where
  yes : P → Dec P
  no : Dec P

case_of_ : ∀ {A B : Set} → A → (A → B) → B
case x of f = f x

record _⁇ (P : Set) : Set where
  constructor ⁇_
  field dec : Dec P

open _⁇ ⦃...⦄ public

¿_¿ : ∀ (X : Set) → ⦃ X ⁇ ⦄ → Dec X
¿ _ ¿ = dec

instance
  postulate Dec≤ : ∀ {n m : Nat} → (n ≤ m) ⁇

top : Set
top = Nat
  where  -- only fails in a `where` block
    IsGood : (new : Nat) → Set
    IsGood new = 1 ≤ new

    -- case/with over this works:
    -- checkGood : ∀ new → Dec (IsGood new)
    -- checkGood new = ¿ IsGood new ¿

    foo : ∀ new → Bool
    foo new = case ¿ IsGood new ¿ of λ where
      (yes _) → true
      no → true

    bar : ∀ new → foo new ≡ true
    bar new with ¿ IsGood new ¿   -- with-abstraction fails
    ... | yes _ = refl
    ... | no    = refl
UlfNorell commented 4 weeks ago

Bisection blames 403ee426.

andreasabel commented 4 weeks ago

This is PR (CC @plt-amy)

UlfNorell commented 4 weeks ago

@plt-amy do you have some time to take a look at this?

What's interesting is that it works if you give the type argument to the (unused) Bool instance:

 Bool-hasPreorder = HasPreorder Bool _ ∋ record {BoolOrder}

The only difference in this case is that we add the instance to the discrimination tree with the Bool directly:

added value Bool-hasPreorder to discrimination tree with case
  case 0 of
    HasPreorder² → case 0 of
      Bool⁰ → done {Bool-hasPreorder}

instead of first adding it as

added value Bool-hasPreorder to discrimination tree with case
  case 0 of
    HasPreorder² → done {Bool-hasPreorder}

and only adding the refined version once the meta is solved. I haven't been able to figure out yet why this makes a difference.

plt-amy commented 4 weeks ago

Sure, I'll take a look.

plt-amy commented 4 weeks ago

@UlfNorell My immediate guess was right: this is a result of my rather silly attempts to avoid repeatedly waking up instances. The intent was to block on the metas that caused the discrimination tree to go off and find multiple instances:

instance candidates from signature for goal:
  HasPreorder Nat __≈__52
  {Nat-hasPreorder, Bool-hasPreorder} length: 2
blocker:
  __≈__52
mutual block:
  {}

As you can tell from the amount of Bools, this isn't exactly stellar code; turns out many things are sensitive to the order that instance constraints are solved in (like termination checking). Computing the type of a with isn't one of the situations I accounted for. Fixes I can see:

UlfNorell commented 4 weeks ago

Thanks @plt-amy! I'm not sure if it's relevant, but I did some waking of instance constraints for with-abstraction in #6868.