@@ -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/--
9192Checks 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+
114150def inferVisibility (phase : Phase) : Pass where
115151 occurrence := 0
116152 phase
0 commit comments