Skip to content

Commit c50a9e9

Browse files
Khaclaude
andcommitted
fix: exempt bootstrap-path extensions from the resolution access tripwire
Compiling `Init.Prelude` for stage 2 panicked on the extension access tripwire: `Environment.find?` consults the reserved-name predicates on lookup misses, also inside a resolution search, and the predicates (and their realization-action preludes) read their registering module's extensions. These paths are only exercised during bootstrapping, so the stage 1 test suite never reached them; CI builds stage 2 for this branch because `stage0/src/stdlib_flags.h` differs. Register the involved extensions as `.exempt`, each of which is monotone and keyed by declarations that must exist before any query can observe them: `reservedNamePredicatesExt` (fixed at initialization), `defHeightOverrideExt`, `methodSpecsAttr` (threading `tcResolutionAccess` through `ParametricAttributeImpl`), `eqnsExt`/`eqnOptionsExt`, `matchEqnsExt`, `sparseCasesOnInfoExt`/`sparseCasesOnCacheExt`, and the partial-fixpoint `eqnInfoExt`. Realization bodies themselves run on the realization context's environment, which is never armed, so no further classification is needed there. Verified by a full local stage 2 build (previously failing at `Init.Prelude`, now clean). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0a8335f commit c50a9e9

7 files changed

Lines changed: 55 additions & 10 deletions

File tree

src/Lean/Elab/PreDefinition/PartialFixpoint/Eqns.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ public structure EqnInfo where
2626
deriving Inhabited
2727

2828
public builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ←
29-
mkMapDeclarationExtension (exportEntriesFn := fun env s =>
29+
-- consulted by the reserved-name predicates for fixpoint induction names, so also on
30+
-- resolution search paths; populated only when the declaration is created (monotone)
31+
mkMapDeclarationExtension (tcResolutionAccess := .exempt) (exportEntriesFn := fun env s =>
3032
let all := s.toArray
3133
-- Do not export for non-exposed defs at exported/server levels
3234
let exported := s.filter (fun n _ => env.hasExposedBody n) |>.toArray

src/Lean/Environment.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3055,7 +3055,9 @@ This is consulted for all definitions regardless of their reducibility hints. Cu
30553055
structural recursion to ensure that parent definitions get the correct height even though the
30563056
`_f` helper definitions are marked as `.abbrev` (which `getMaxHeight` would otherwise ignore). -/
30573057
builtin_initialize defHeightOverrideExt : EnvExtension (NameMap UInt32) ←
3058-
registerEnvExtension (pure {}) (asyncMode := .local)
3058+
-- read when auxiliary declarations are created during realization, which can happen inside a
3059+
-- resolution search; overrides are set only for declarations being created (monotone)
3060+
registerEnvExtension (pure {}) (asyncMode := .local) (tcResolutionAccess := .exempt)
30593061

30603062
/-- Register a height override for a definition so that `getMaxHeight` uses it. -/
30613063
def setDefHeightOverride (env : Environment) (declName : Name) (height : UInt32) : Environment :=

src/Lean/Meta/Constructions/SparseCasesOn.lean

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ structure SparseCasesOnKey where
2424
deriving BEq, Hashable
2525

2626
builtin_initialize sparseCasesOnCacheExt : EnvExtension (PHashMap SparseCasesOnKey Name) ←
27-
registerEnvExtension (pure {}) (asyncMode := .local) -- mere cache, keep it local
27+
-- mere cache, keep it local; may be consulted on resolution search paths via the reserved-name
28+
-- machinery (monotone realization cache)
29+
registerEnvExtension (pure {}) (asyncMode := .local) (tcResolutionAccess := .exempt)
2830

2931
/-- Information necessary to recognize and split on sparse casesOn (in particular in MatchEqs) -/
3032
public structure SparseCasesOnInfo where
@@ -35,7 +37,9 @@ public structure SparseCasesOnInfo where
3537
deriving Inhabited
3638

3739
builtin_initialize sparseCasesOnInfoExt : MapDeclarationExtension SparseCasesOnInfo ←
38-
mkMapDeclarationExtension (exportEntriesFn := fun env s =>
40+
-- consulted by the reserved-name predicate for `else_eq` names, so also on resolution search
41+
-- paths; populated only when the declaration is created (monotone)
42+
mkMapDeclarationExtension (tcResolutionAccess := .exempt) (exportEntriesFn := fun env s =>
3943
let all := s.toArray
4044
-- Do not export for non-exposed defs at exported/server levels
4145
let exported := s.filter (fun n _ => env.hasExposedBody n) |>.toArray

src/Lean/Meta/Eqns.lean

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ def eqnAffectingOptions : Array (Lean.Option Bool) :=
4747
keyed by declaration name. Only populated when at least one option has a non-default value.
4848
Stores an association list of (option name, value) pairs for options that differ from defaults. -/
4949
builtin_initialize eqnOptionsExt : MapDeclarationExtension (Array (Name × DataValue)) ←
50-
mkMapDeclarationExtension (asyncMode := .local)
50+
-- consulted during equation realization, which can happen inside a resolution search;
51+
-- populated only when the declaration is created (monotone)
52+
mkMapDeclarationExtension (asyncMode := .local) (tcResolutionAccess := .exempt)
5153

5254
def eqnThmSuffixBase := "eq"
5355
def eqnThmSuffixBasePrefix := eqnThmSuffixBase ++ "_"
@@ -157,7 +159,9 @@ structure EqnsExtState where
157159

158160
/-- A mapping from equational theorem to the declaration it was derived from. -/
159161
builtin_initialize eqnsExt : EnvExtension EqnsExtState ←
160-
registerEnvExtension (pure {}) (asyncMode := .local)
162+
-- consulted during equation realization, which can happen inside a resolution search;
163+
-- a mere name mapping for realized equation theorems (monotone)
164+
registerEnvExtension (pure {}) (asyncMode := .local) (tcResolutionAccess := .exempt)
161165

162166
/--
163167
Runs `act` with the equation-affecting options restored to the values stored for `declName`

src/Lean/Meta/Match/MatchEqsExt.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ structure MatchEqnsExtState where
3030

3131
/- We generate the equations and splitter on demand, and do not save them on .olean files. -/
3232
builtin_initialize matchEqnsExt : EnvExtension MatchEqnsExtState ←
33+
-- consulted during match-equation realization, which can happen inside a resolution search;
34+
-- a mere name mapping for realized equation theorems (monotone)
3335
-- Using `local` allows us to use the extension in `realizeConst` without specifying `replay?`.
3436
-- The resulting state can still be accessed on the generated declarations using `.asyncEnv`;
3537
-- see below
36-
registerEnvExtension (pure {}) (asyncMode := .local)
38+
registerEnvExtension (pure {}) (asyncMode := .local) (tcResolutionAccess := .exempt)
3739

3840
def registerMatchEqns (matchDeclName : Name) (matchEqns : MatchEqns) : CoreM Unit := do
3941
modifyEnv fun env => matchEqnsExt.modifyState env fun { map, eqns } => {

src/Lean/Meta/MethodSpecs.lean

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,41 @@ overloaded `Cls.op` operation, and similarly `instClsT.op_spec_<n>` based on the
112112
`opImpl.eq_<n>`.
113113
-/
114114
@[builtin_doc]
115-
builtin_initialize methodSpecsAttr : ParametricAttribute MethodSpecsAttrData ←
116-
registerParametricAttribute {
115+
builtin_initialize methodSpecsAttr : ParametricAttribute MethodSpecsAttrData ← do
116+
-- `registerParametricAttribute` inlined so that the extension can be registered with
117+
-- `tcResolutionAccess := .exempt`: it is consulted (via the reserved-name machinery) when
118+
-- specification theorems are realized, which can happen inside a resolution search, and the
119+
-- attribute is applied when the method implementation is declared (monotone).
120+
let impl : ParametricAttributeImpl MethodSpecsAttrData := {
117121
name := `method_specs
118122
descr := "generate method specification theorems"
119123
getParam
120124
}
125+
let ext : PersistentEnvExtension (Name × MethodSpecsAttrData) (Name × MethodSpecsAttrData) (List Name × NameMap MethodSpecsAttrData) ← registerPersistentEnvExtension {
126+
name := impl.ref
127+
tcResolutionAccess := .exempt
128+
mkInitial := pure ([], {})
129+
addImportedFn := fun _ => pure ([], {})
130+
addEntryFn := fun (decls, m) p => (p.1 :: decls, m.insert p.1 p.2)
131+
exportEntriesFnEx := fun env (decls, m) => Id.run do
132+
let all := (m.foldl (fun a n p => a.push (n, p)) #[]).qsort (fun a b => Name.quickLt a.1 b.1)
133+
let exported := all.filter fun (n, a) => impl.filterExport env n a
134+
{ exported, server := exported, «private» := all }
135+
statsFn := fun (_, m) => "parametric attribute" ++ Format.line ++ "number of local entries: " ++ format m.size
136+
}
137+
let attrImpl : AttributeImpl := {
138+
impl.toAttributeImplCore with
139+
add := fun decl stx kind => do
140+
unless kind == AttributeKind.global do throwAttrMustBeGlobal impl.name kind
141+
let env ← getEnv
142+
unless (env.getModuleIdxFor? decl).isNone do
143+
throwAttrDeclInImportedModule impl.name decl
144+
let val ← impl.getParam decl stx
145+
modifyEnv fun env => ext.addEntry (asyncDecl := decl) env (decl, val)
146+
try impl.afterSet decl val catch _ => setEnv env
147+
}
148+
registerBuiltinAttribute attrImpl
149+
pure { attr := attrImpl, ext, preserveOrder := impl.preserveOrder }
121150

122151
builtin_initialize methodSpecsSimpExtension : SimpExtension ←
123152
registerSimpAttr `method_specs_simp

src/Lean/ResolveName.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ def registerReservedNamePredicate (p : Environment → Name → Bool) : IO Unit
4343
reservedNamePredicatesRef.modify fun ps => ps.push p
4444

4545
builtin_initialize reservedNamePredicatesExt : EnvExtension (Array (Environment → Name → Bool)) ←
46-
registerEnvExtension reservedNamePredicatesRef.get
46+
-- consulted by `Environment.find?` (via `isReservedName`), so also on resolution search paths;
47+
-- the predicate set is fixed at initialization
48+
registerEnvExtension reservedNamePredicatesRef.get (tcResolutionAccess := .exempt)
4749

4850
/--
4951
Returns `true` if `name` is a reserved name.

0 commit comments

Comments
 (0)