Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primitives are incorrectly considered convertible to anything by module subtyping #18503

Closed
SkySkimmer opened this issue Jan 16, 2024 · 7 comments · Fixed by #18507
Closed

Primitives are incorrectly considered convertible to anything by module subtyping #18503

SkySkimmer opened this issue Jan 16, 2024 · 7 comments · Fixed by #18507
Labels
kind: inconsistency Proof of False accepted by the kernel and/or checker. part: modules The module system of Coq. part: primitive types Primitive ints, floats, arrays, etc.
Milestone

Comments

@SkySkimmer
Copy link
Contributor

SkySkimmer commented Jan 16, 2024

Require Import PrimInt63.
Open Scope int63_scope.

Module Type T.
  Primitive bar := #int63_sub.
End T.

Module F(X:T).
  Definition foo : X.bar 1 1 = 0 := eq_refl.
End F.

Module M.
  Definition bar := land.
End M.

Module N := F M. (* "bar := land" is subtype of "bar := #int63_sub"? *)

Lemma bad : False.
Proof.
  pose (f := fun x => eqb x 1).
  assert (H:f 1 = f 0) by (f_equal;exact N.foo).
  change (true = false) in H.
  inversion H.
Qed.

Print Assumptions bad.
(* Axioms:
land : int -> int -> int
int : Set
eqb : int -> int -> bool
*)
@SkySkimmer SkySkimmer added part: modules The module system of Coq. kind: inconsistency Proof of False accepted by the kernel and/or checker. part: primitive types Primitive ints, floats, arrays, etc. labels Jan 16, 2024
@mattam82
Copy link
Member

Great find, just in time :)

@CohenCyril
Copy link
Contributor

At least Print Assumptions prints something... 😆

@SkySkimmer
Copy link
Contributor Author

The original example is rejected by coqchk but this slightly modified example is accepted:

Require Import PrimInt63.
Open Scope int63_scope.

Module Type T.
  Primitive bar := #int63_sub.

  Axiom bar_land : bar = land.
End T.

Module F(X:T).
  Definition foo : X.bar 1 1 = 0 := eq_refl.
End F.

Module M.
  Definition bar := land.
  Definition bar_land : bar = land := eq_refl.
End M.

Module N : T := M.

Module A := F N.

Lemma bad : False.
Proof.
  pose (f := fun x => eqb x 1).
  assert (H:f 1 = f 0).
  { f_equal. change 1 with (land 1 1).
    rewrite <-N.bar_land.
    exact A.foo. }
  change (true = false) in H.
  inversion H.
Qed.

@SkySkimmer
Copy link
Contributor Author

I tried to get the inconsistency with empty Print Assumptions but it seems impossible:

  • we can't use literals in the module type because they're only
    convertible to literals so we would need to use them in the
    implementation

  • we can't use literals in the functor because "int" isn't registered
    after the module type we could register a separate Primitive int
    but then literals are of that type not X.int

  • all primitive operations take some primitive value as argument except array max length
    array max length is expanded to a literal at declaration time

  • we need to call a primitive with a literal to get it to evaluate in the functor otherwise we have nothing of interest

@coqbot-app coqbot-app bot added this to the 8.19.0 milestone Jan 22, 2024
SkySkimmer added a commit that referenced this issue Jan 22, 2024
louiseddp pushed a commit to louiseddp/coq that referenced this issue Feb 27, 2024
@JasonGross
Copy link
Member

I tried to get the inconsistency with empty Print Assumptions but it seems impossible:

Belatedly, this one is closed under the global context (in 8.18.0), but does not pass coqchk.

Require Import BinInt PrimInt63.
Require Uint63.
Open Scope int63_scope.

Module Type T.
  Primitive bar := #int63_sub.

  Axiom bar_land : Uint63.to_Z (bar 1 1) = Uint63.to_Z (land 1 1).
End T.

Module F(X:T).
  Definition foo : Uint63.to_Z (X.bar 1 1) = Uint63.to_Z 0 := eq_refl.
  Lemma bad : False.
  Proof.
    pose (f := fun x => Z.eqb x 1).
    assert (H:f 1%Z = f 0%Z).
    { f_equal. change 1%Z with (Uint63.to_Z (land 1 1)).
      rewrite <-X.bar_land.
      exact foo. }
    change (true = false) in H.
    inversion H.
  Defined.
  Definition rbad := Eval cbv in bad.
End F.

Module M.
  Definition bar := land.
  Definition bar_land : Uint63.to_Z (bar 1 1) = Uint63.to_Z (land 1 1) := eq_refl 1%Z.
End M.

Module N <: T := M.

Module A := F N.

Print A.rbad.
Print Assumptions A.rbad.
(* Closed under the global context *)

@SkySkimmer
Copy link
Contributor Author

It's possible to make coqchk accept it, just do an opaque Module N : T := M.. coqchk defunctorializes so the bad subtyping isn't trusted when applying a functor, but it relies on correct subtyping when opacifying a module.

PS: You can define bad directly without needing to use cbv:

  Definition bad :=
    match
      match
        match
          match
            (* cast so that the parameters cached in the match node don't refer to primitives *)
            match X.bar_land :> (0 = 1)%Z in (_ = a) return (a = 0%Z) with
            | eq_refl => eq_refl
            end in (_ = a) return (a = 1%Z)
          with
          | eq_refl => eq_refl
          end in (_ = a) return match a with
                                | 0%Z => True
                                | _ => False
                                end
        with
        | eq_refl => I
        end return (true = false)
      with
      end in (_ = b) return (b = false -> False)
    with
    | eq_refl =>
        fun H0 : true = false =>
          match
            match H0 in (_ = a) return (if a then True else False) with
            | eq_refl => I
            end return False
          with
          end
    end eq_refl.

(this is just what Print bad on your proof says + the cast as commented, turning it back into a nice-ish tactic sequence or term is left to the reader)

@SkySkimmer
Copy link
Contributor Author

SkySkimmer commented Mar 23, 2024

Thus simplified and coqchk accepted:

Require Import PrimInt63.
Open Scope int63_scope.

Module Type T.
  Primitive bar := #int63_sub.

  Axiom bar_land : eqb (bar 1 1) 1 = true.
End T.

Module F(X:T).
  Lemma bad : False.
  Proof.
    assert (H : false = true) by exact X.bar_land.
    inversion H.
  Defined.
End F.

Module M.
  Definition bar := land.
  Definition bar_land : eqb (bar 1 1) 1 = true := eq_refl true.
End M.

(* coqchk defunctorializes so the bad subtyping doesn't matter when applying a functor,
   but it relies on correct subtyping when opacifying a module *)
Module N : T := M.

Module A := F N.

Check A.bad : False.

Print Assumptions A.bad.
(* Closed under the global context *)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: inconsistency Proof of False accepted by the kernel and/or checker. part: modules The module system of Coq. part: primitive types Primitive ints, floats, arrays, etc.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants