Skip to content

Commit 49cff79

Browse files
authored
fix: privacy checks and import all (#10550)
This PR ensures private declarations are accessible from the private scope iff they are local or imported through an `import all` chain, including for anonymous notation and structure instance notation.
1 parent 2677ca8 commit 49cff79

File tree

9 files changed

+60
-22
lines changed

9 files changed

+60
-22
lines changed

src/Lean/Environment.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ structure EnvironmentHeader where
182182
`ModuleIdx` for the same module.
183183
-/
184184
modules : Array EffectiveImport := #[]
185+
/--
186+
Subset of `modules` for which `importAll` is `true`. This is assumed to be a much smaller set so
187+
we precompute it instead of iterating over all of `modules` multiple times. However, note that
188+
in a non-`module` file, this is identical to `modules`.
189+
-/
190+
importAllModules : Array EffectiveImport := modules.filter (·.importAll)
185191
/-- Module data for all imported modules. -/
186192
moduleData : Array ModuleData := #[]
187193
deriving Nonempty

src/Lean/Modifiers.lean

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,19 @@ def mkPrivateName (env : Environment) (n : Name) : Name :=
2626
-- is private to *this* module.
2727
mkPrivateNameCore env.mainModule <| privateToUserName n
2828

29-
def isInaccessiblePrivateName (env : Environment) (n : Name) : Bool :=
30-
if env.header.isModule then
31-
-- Allow access through `import all`.
32-
env.isExporting && isPrivateName n
33-
else match privateToUserName? n with
34-
| some userName => mkPrivateName env userName != n
35-
| _ => false
29+
def isInaccessiblePrivateName (env : Environment) (n : Name) : Bool := Id.run do
30+
if !isPrivateName n then
31+
return false
32+
-- All private names are inaccessible from the public scope
33+
if env.isExporting then
34+
return true
35+
-- In the private scope, ...
36+
match env.getModuleIdxFor? n with
37+
| some modIdx =>
38+
-- ... allow access through `import all`
39+
!env.header.isModule || !env.header.modules[modIdx]?.any (·.importAll)
40+
| none =>
41+
-- ... allow all accesses in the current module
42+
false
3643

3744
end Lean

src/Lean/ResolveName.lean

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,9 @@ private partial def resolvePrivateName (env : Environment) (declName : Name) : O
111111
if containsDeclOrReserved env (mkPrivateName env declName) then
112112
return mkPrivateName env declName
113113
-- Under the module system, we assume there are at most a few `import all`s and we can just test
114-
-- them on by one.
114+
-- them one by one.
115115
guard <| env.header.isModule
116-
-- As `all` is not transitive, we only have to check the direct imports.
117-
env.header.imports.findSome? fun i => do
118-
guard i.importAll
116+
env.header.importAllModules.findSome? fun i => do
119117
let n := mkPrivateNameCore i.module declName
120118
guard <| containsDeclOrReserved env n
121119
return n

tests/lean/ctor_layout.lean

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ def test : CoreM Unit := do
77
let ctorLayout ← getCtorLayout ``Lean.IR.Expr.reuse;
88
ctorLayout.fieldInfo.forM $ fun finfo => IO.println (format finfo);
99
IO.println "---";
10-
let ctorLayout ← getCtorLayout ``Lean.EnvironmentHeader.mk;
11-
ctorLayout.fieldInfo.forM $ fun finfo => IO.println (format finfo);
12-
IO.println "---";
1310
let ctorLayout ← getCtorLayout ``Subtype.mk;
1411
ctorLayout.fieldInfo.forM $ fun finfo => IO.println (format finfo);
1512
pure ()

tests/lean/ctor_layout.lean.expected.out

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,5 @@ obj@1:obj
33
scalar#1@0:u8
44
obj@2:obj
55
---
6-
scalar#4@0:u32
7-
obj@0:tobj
8-
scalar#1@4:u8
9-
obj@1:obj
10-
obj@2:obj
11-
obj@3:obj
12-
obj@4:obj
13-
---
146
obj@0:tobj
157

tests/pkg/module/Module.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Module.ImportedAll
55
import Module.ImportedPrivateImported
66
import Module.PrivateImported
77
import Module.ImportedAllPrivateImported
8+
import Module.ImportedAllImportedAll
89
import Module.NonModule
910
import Module.MetaImported
1011

tests/pkg/module/Module/Basic.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,15 @@ constructor:
312312
#with_exporting
313313
#check { x := 1 : StructWithPrivateField }
314314

315+
#check (⟨1⟩ : StructWithPrivateField)
316+
317+
/--
318+
error: Invalid `⟨...⟩` notation: Constructor for `StructWithPrivateField` is marked as private
319+
-/
320+
#guard_msgs in
321+
#with_exporting
322+
#check (⟨1⟩ : StructWithPrivateField)
323+
315324
#check StructWithPrivateField.x
316325

317326
/-- error: Unknown constant `StructWithPrivateField.x` -/

tests/pkg/module/Module/ImportedAll.lean

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,19 @@ Note: A private declaration `priv✝` (from `Module.Basic`) exists but would nee
144144
-/
145145
#guard_msgs in
146146
@[expose] public def pub' := priv
147+
148+
#check { x := 1 : StructWithPrivateField }
149+
150+
/-- error: invalid {...} notation, constructor for `StructWithPrivateField` is marked as private -/
151+
#guard_msgs in
152+
#with_exporting
153+
#check { x := 1 : StructWithPrivateField }
154+
155+
#check (⟨1⟩ : StructWithPrivateField)
156+
157+
/--
158+
error: Invalid `⟨...⟩` notation: Constructor for `StructWithPrivateField` is marked as private
159+
-/
160+
#guard_msgs in
161+
#with_exporting
162+
#check (⟨1⟩ : StructWithPrivateField)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module
2+
3+
prelude
4+
import all Module.ImportedAll
5+
6+
/-! `import all` should chain with nested `import all`s. -/
7+
8+
#check priv
9+
10+
#check { x := 1 : StructWithPrivateField }
11+
12+
#check (⟨1⟩ : StructWithPrivateField)

0 commit comments

Comments
 (0)