Skip to content

Commit 8f2f715

Browse files
nomeataclaude
andauthored
Add proj-of-prop soundness test (#36)
The exported theorem builds a proof of `False` via a projection from a `Prop`-typed structure whose constructor was applied to an ill-typed argument: structure Wrapper : Prop where mk :: p : False theorem badFalse : False := (Wrapper.mk True.intro).p A checker that types a projection by inferring (rather than checking) its structure — trusting the structure to be well-typed rather than verifying the constructor's arguments against its binders — will accept this and return `badFalse : False`. A sound checker must reject it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a0b4d90 commit 8f2f715

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

tests/proj-of-prop.lean

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Lean
2+
open Lean Elab Command
3+
4+
structure Wrapper : Prop where
5+
mk ::
6+
p : False
7+
8+
/-
9+
The value we want to add is
10+
11+
Expr.proj `Wrapper 0 (Wrapper.mk True.intro)
12+
13+
which has a `True.intro : True` sitting in the `Wrapper.mk` slot that
14+
expects a `False`. The elaborator never emits a raw `Expr.proj` from
15+
`(...).p` syntax (it desugars to the projection *function* `Wrapper.p`,
16+
which would get caught at `infer_app`), so we construct the declaration
17+
programmatically and hand it to `addDecl` under `debug.skipKernelTC`.
18+
-/
19+
20+
run_cmd liftTermElabM do
21+
let badValue : Expr :=
22+
.proj `Wrapper 0 (mkApp (mkConst ``Wrapper.mk) (mkConst ``True.intro))
23+
let decl : Declaration := .thmDecl {
24+
name := `badFalse
25+
levelParams := []
26+
type := mkConst ``False
27+
value := badValue
28+
}
29+
withOptions (debug.skipKernelTC.set · true) do
30+
addDecl decl

tests/proj-of-prop.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
description: |
2+
A proof of `False` via a projection from a `Prop`-typed structure whose
3+
constructor was applied to an ill-typed argument. The exported term is
4+
5+
badFalse : False := (Wrapper.mk True.intro).p
6+
7+
where `Wrapper : Prop` has a single field `p : False`, so `Wrapper.mk`
8+
expects a proof of `False` but is given `True.intro : True`.
9+
10+
A sound checker must reject this. A checker that types a projection by
11+
inferring (rather than checking) its structure argument — i.e. that
12+
trusts the structure to be well-typed instead of verifying the
13+
constructor's argument types against its binders — will accept it,
14+
because `Wrapper.mk True.intro` still formally inhabits `Wrapper` at
15+
the structural level, and the `p` projection is then read back out at
16+
the declared field type `False`.
17+
leanfile: tests/proj-of-prop.lean
18+
export-decls: ["badFalse"]
19+
outcome: reject

0 commit comments

Comments
 (0)