Skip to content

Commit f484b56

Browse files
authored
fix: inline/specialize may only refer to publicly imported decls for now (#10494)
This PR resolves a potential bad interaction between the compiler and the module system where references to declarations not imported are brought into scope by inlining or specializing. We now proactively check that declarations to be inlined/specialized only reference public imports. The intention is to later resolve this limitation by moving out compilation into a separate build step with its own import/incremental system.
1 parent 02f4821 commit f484b56

File tree

20 files changed

+63
-25
lines changed

20 files changed

+63
-25
lines changed

src/Init/Data/Array/Lex/Basic.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ prelude
99
public import Init.Core
1010
import Init.Data.Array.Basic
1111
import Init.Data.Nat.Lemmas
12-
import Init.Data.Range.Polymorphic.Iterators
13-
import Init.Data.Range.Polymorphic.Nat
12+
public import Init.Data.Range.Polymorphic.Iterators
13+
public import Init.Data.Range.Polymorphic.Nat
1414
import Init.Data.Iterators.Consumers
1515

1616
public section

src/Init/Data/Array/Subarray.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module
77

88
prelude
99
public import Init.GetElem
10+
public import Init.Data.Array.Basic
1011
import Init.Data.Array.GetLit
1112
public import Init.Data.Slice.Basic
1213

src/Init/Data/ToString/Name.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module
77

88
prelude
99
public import Init.Meta
10-
import Init.Data.String.Extra
10+
public import Init.Data.String.Extra
1111

1212
/-!
1313
Here we give the. implementation of `Name.toString`. There is also a private implementation in

src/Lean/Compiler/LCNF/Main.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def run (declNames : Array Name) : CompilerM (Array IR.Decl) := withAtLeastMaxRe
112112
-- Check meta accesses now before optimizations may obscure references. This check should stay in
113113
-- `lean` if some compilation is moved out.
114114
for decl in decls do
115-
checkMeta (isMeta (← getEnv) decl.name) decl
115+
checkMeta decl
116116
let decls := markRecDecls decls
117117
let manager ← getPassManager
118118
let isCheckEnabled := compiler.check.get (← getOptions)

src/Lean/Compiler/LCNF/Passes.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def builtinPassManager : PassManager := {
8585
-/
8686
simp { etaPoly := true, inlinePartial := true, implementedBy := true } (occurrence := 1),
8787
eagerLambdaLifting,
88+
checkTemplateVisibility,
8889
specialize,
8990
simp (occurrence := 2),
9091
cse (shouldElimFunDecls := false) (occurrence := 1),

src/Lean/Compiler/LCNF/Visibility.lean

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,21 @@ partial def markDeclPublicRec (phase : Phase) (decl : Decl) : CompilerM Unit :=
5757
markDeclPublicRec phase refDecl
5858

5959
/-- Checks whether references in the given declaration adhere to phase distinction. -/
60-
partial def checkMeta (isMeta : Bool) (origDecl : Decl) : CompilerM Unit := do
60+
partial def checkMeta (origDecl : Decl) : CompilerM Unit := do
6161
if !(← getEnv).header.isModule then
6262
return
63+
let isMeta := isMeta (← getEnv) origDecl.name
6364
-- If the meta decl is public, we want to ensure it can only refer to public meta imports so that
6465
-- references to private imports cannot escape the current module. In particular, we check that
6566
-- decls with relevant global attrs are public (`Lean.ensureAttrDeclIsMeta`).
6667
let isPublic := !isPrivateName origDecl.name
67-
go isPublic origDecl |>.run' {}
68-
where go (isPublic : Bool) (decl : Decl) : StateT NameSet CompilerM Unit := do
68+
go isMeta isPublic origDecl |>.run' {}
69+
where go (isMeta isPublic : Bool) (decl : Decl) : StateT NameSet CompilerM Unit := do
6970
decl.value.forCodeM fun code =>
7071
for ref in collectUsedDecls code do
7172
if (← get).contains ref then
7273
continue
73-
modify (·.insert decl.name)
74+
modify (·.insert ref)
7475
if isMeta && isPublic then
7576
if let some modIdx := (← getEnv).getModuleIdxFor? ref then
7677
if (← getEnv).header.modules[modIdx]?.any (!·.isExported) then
@@ -85,7 +86,7 @@ where go (isPublic : Bool) (decl : Decl) : StateT NameSet CompilerM Unit := do
8586
-- *their* references in this case. We also need to do this for non-auxiliary defs in case a
8687
-- public meta def tries to use a private meta import via a local private meta def :/ .
8788
if let some refDecl ← getLocalDecl? ref then
88-
go isPublic refDecl
89+
go isMeta isPublic refDecl
8990

9091
/--
9192
Checks meta availability just before `evalConst`. This is a "last line of defense" as accesses
@@ -104,13 +105,48 @@ where go (decl : Decl) : StateT NameSet (Except String) Unit :=
104105
for ref in collectUsedDecls code do
105106
if (← get).contains ref then
106107
continue
107-
modify (·.insert decl.name)
108+
modify (·.insert ref)
108109
if let some localDecl := baseExt.getState env |>.find? ref then
109110
go localDecl
110111
else
111112
if getIRPhases env ref == .runtime then
112113
throw s!"Cannot evaluate constant `{declName}` as it uses `{ref}` which is neither marked nor imported as `meta`"
113114

115+
/--
116+
Checks that imports necessary for inlining/specialization are public as otherwise we may run into
117+
unknown declarations at the point of inlining/specializing. This is a limitation that we want to
118+
lift in the future by moving main compilation into a different process that can use a different
119+
import/incremental system.
120+
-/
121+
partial def checkTemplateVisibility : Pass where
122+
occurrence := 0
123+
phase := .base
124+
name := `checkTemplateVisibility
125+
run decls := do
126+
if (← getEnv).header.isModule then
127+
for decl in decls do
128+
-- A private template-like decl cannot directly be used by a different module. If it could be used
129+
-- indirectly via a public template-like, we do a recursive check when checking the latter.
130+
if !isPrivateName decl.name && (← decl.isTemplateLike) then
131+
let isMeta := isMeta (← getEnv) decl.name
132+
go isMeta decl decl |>.run' {}
133+
return decls
134+
where go (isMeta : Bool) (origDecl decl : Decl) : StateT NameSet CompilerM Unit := do
135+
decl.value.forCodeM fun code =>
136+
for ref in collectUsedDecls code do
137+
if (← get).contains ref then
138+
continue
139+
modify (·.insert decl.name)
140+
if let some localDecl := baseExt.getState (← getEnv) |>.find? ref then
141+
-- check transitively through local decls
142+
if isPrivateName localDecl.name && (← localDecl.isTemplateLike) then
143+
go isMeta origDecl localDecl
144+
else if let some modIdx := (← getEnv).getModuleIdxFor? ref then
145+
if (← getEnv).header.modules[modIdx]?.any (!·.isExported) then
146+
throwError "Cannot compile inline/specializing declaration `{.ofConstName origDecl.name}` as \
147+
it uses `{.ofConstName ref}` of module `{(← getEnv).header.moduleNames[modIdx]!}` \
148+
which must be imported publicly. This limitation may be lifted in the future."
149+
114150
def inferVisibility (phase : Phase) : Pass where
115151
occurrence := 0
116152
phase

src/Lean/Data/Array.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ module
88
prelude
99
public import Init.Prelude
1010
import Init.Data.Stream
11-
import Init.Data.Range.Polymorphic.Nat
12-
import Init.Data.Range.Polymorphic.Iterators
11+
public import Init.Data.Range.Polymorphic.Nat
12+
public import Init.Data.Range.Polymorphic.Iterators
1313

1414
namespace Array
1515

src/Lean/DocString/Add.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Lean.Elab.DocString
1414
import Lean.DocString.Extension
1515
import Lean.DocString.Links
1616
import Lean.Parser.Types
17-
import Lean.DocString.Parser
17+
public import Lean.DocString.Parser
1818
import Lean.ResolveName
1919
public import Lean.Elab.Term.TermElabM
2020
import Std.Data.HashMap

src/Lean/Elab/DocString/Builtin/Parsing.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Author: David Thrane Christiansen
66
module
77
prelude
88
import Lean.Elab.DocString
9-
import Lean.Parser.Extension
9+
public import Lean.Parser.Extension
1010
public import Lean.Parser.Types
1111

1212
namespace Lean.Doc

src/Lean/Elab/Util.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
66
module
77

88
prelude
9-
import Lean.Parser.Extension
9+
public import Lean.Parser.Extension
1010
meta import Lean.Parser.Command
1111
public import Lean.KeyedDeclsAttribute
1212
public import Lean.Elab.Exception

0 commit comments

Comments
 (0)