Skip to content

Commit 671533f

Browse files
committed
Associate PackageUnit with ModuleDecl
* Weakly reference ModuleDecl from PackageUnit * Add PackageUnit decl context getter and use it for a package AccessScope * Return module decl referenced by PackageUnit in getModuleScopeContext and getParentModule * Handle package acl in access scope checkers * Remove AccessLimitKind * Fix tests Resolves rdar://104987295, rdar://105187216, rdar://104723918
1 parent 302180f commit 671533f

File tree

10 files changed

+244
-158
lines changed

10 files changed

+244
-158
lines changed

include/swift/AST/AccessScope.h

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,40 +20,39 @@
2020

2121
namespace swift {
2222

23-
/// Used to provide the kind of scope limitation in AccessScope::Value
24-
enum class AccessLimitKind : uint8_t { None = 0, Private, Package };
25-
2623
/// The wrapper around the outermost DeclContext from which
2724
/// a particular declaration can be accessed.
2825
class AccessScope {
29-
/// The declaration context along with an enum indicating the level of
30-
/// scope limitation.
31-
/// If the declaration context is set, and the limit kind is Private, the
32-
/// access level is considered 'private'. Whether it's 'internal' or
33-
/// 'fileprivate' is determined by what the declaration context casts to. If
34-
/// the declaration context is null, and the limit kind is None, the access
35-
/// level is considered 'public'. If the limit kind is Private, the access
36-
/// level is considered SPI. If it's Package, the access level is considered
37-
/// 'package'. Below is a table showing the combinations.
26+
/// To validate access of a decl, access scope check for both decl site
27+
/// and use site needs to be done. The underlying mechanism uses a
28+
/// <DeclContext*, bool> pair to determine the scope, as shown
29+
/// in the table below.
3830
///
39-
/// AccessLimitKind DC == nullptr DC != nullptr
40-
/// ---------------------------------------------------------------------------
41-
/// None public fileprivate or internal (check DC to tell which)
42-
/// Private `@_spi` public private
43-
/// Package package (unused)
44-
45-
llvm::PointerIntPair<const DeclContext *, 2, AccessLimitKind> Value;
31+
/// <DeclContext*, bool> AccessScope AccessLevel
32+
/// --------------------------------------------
33+
/// <nullptr, false> Public public or open
34+
/// <nullptr, true> Public public `@_spi`
35+
/// <PackageUnit*, _> Package package
36+
/// <ModuleDecl*, _> Module internal
37+
/// <FileUnit*, false> FileScope fileprivate
38+
/// <FileUnit*, true> Private private
39+
///
40+
/// For example, if a decl with `public` access level is referenced outside of
41+
/// its defining module, it will be maped to the <nullptr, false> pair during
42+
/// the access scope check. This pair is determined based on the decl's access
43+
/// level in \c getAccessScopeForFormalAccess and passed to
44+
/// \c checkAccessUsingAccessScope which compares access scope of the
45+
/// use site and decl site.
46+
///
47+
/// \see AccessScope::getAccessScopeForFormalAccess
48+
/// \see AccessScope::checkAccessUsingAccessScope
49+
/// \see DeclContext::ASTHierarchy
50+
llvm::PointerIntPair<const DeclContext *, 1, bool> Value;
4651

4752
public:
48-
AccessScope(const DeclContext *DC,
49-
AccessLimitKind limitKind = AccessLimitKind::None);
53+
AccessScope(const DeclContext *DC, bool isPrivate = false);
5054

51-
static AccessScope getPublic() {
52-
return AccessScope(nullptr, AccessLimitKind::None);
53-
}
54-
static AccessScope getPackage() {
55-
return AccessScope(nullptr, AccessLimitKind::Package);
56-
}
55+
static AccessScope getPublic() { return AccessScope(nullptr, false); }
5756

5857
/// Check if private access is allowed. This is a lexical scope check in Swift
5958
/// 3 mode. In Swift 4 mode, declarations and extensions of the same type will
@@ -66,63 +65,60 @@ class AccessScope {
6665
bool operator==(AccessScope RHS) const { return Value == RHS.Value; }
6766
bool operator!=(AccessScope RHS) const { return !(*this == RHS); }
6867
bool hasEqualDeclContextWith(AccessScope RHS) const {
69-
if (isPublic())
70-
return RHS.isPublic();
71-
if (isPackage())
72-
return RHS.isPackage();
7368
return getDeclContext() == RHS.getDeclContext();
7469
}
7570

76-
bool isPublic() const {
77-
return !Value.getPointer() && Value.getInt() == AccessLimitKind::None;
78-
}
79-
bool isPrivate() const {
80-
return Value.getPointer() && Value.getInt() == AccessLimitKind::Private;
81-
}
71+
bool isPublic() const { return !Value.getPointer(); }
72+
bool isPrivate() const { return Value.getPointer() && Value.getInt(); }
8273
bool isFileScope() const;
8374
bool isInternal() const;
84-
bool isPackage() const {
85-
return !Value.getPointer() && Value.getInt() == AccessLimitKind::Package;
86-
}
75+
bool isPackage() const;
8776

88-
/// Returns true if the context of this (use site) is more restrictive than
89-
/// the argument context (decl site). This function does _not_ check the
90-
/// restrictiveness of the access level between this and the argument. \see
91-
/// AccessScope::isInContext
77+
/// Checks if the DeclContext of this (use site) access scope is more
78+
/// restrictive than that of the argument (decl site) based on the DeclContext
79+
/// hierarchy: (most to least restrictive)
80+
/// decl/expr (e.g. ClassDecl) -> FileUnit -> ModuleDecl -> PackageUnit -> nullptr
81+
///
82+
/// A few things to note:
83+
/// 1. If both have the same DeclContext, returns false as one is _not_ a
84+
/// child of the other.
85+
/// 2. This function does _not_ check the restrictiveness of the _access
86+
/// level_ between two decls.
87+
/// 3. The DeclContext of this (use site) may not be null even if the use site has
88+
/// a `public` access level.
89+
///
90+
/// Here's an example while typechecking a file with the following code.
91+
///
92+
/// ```
93+
/// import OtherModule
94+
///
95+
/// // `Foo` is a `public` struct defined in `OtherModule`
96+
/// public func myFunc(_ arg: OtherModule.Foo) {}
97+
/// ```
98+
///
99+
/// The use site of `Foo`is a function `myFunc`, and its DeclContext is non-null
100+
/// (FileUnit) even though the function decl itself has a `public` access level.
101+
/// When `isChildOf` is called, the argument passed in is a pair <nullptr,
102+
/// false> created in \c getAccessScopeForFormalAccess based on the access
103+
/// level of the decl `Foo`. Since FileUnit is a child of nullptr in the DeclContext
104+
/// hierarchy (described above), it returns true.
105+
///
106+
/// \see AccessScope::getAccessScopeForFormalAccess
107+
/// \see AccessScope::checkAccessUsingAccessScope
108+
/// \see DeclContext::ASTHierarchy
92109
bool isChildOf(AccessScope AS) const {
93-
if (isInContext()) {
110+
if (isPackage()) { // This needs to be checked first before isInContext
111+
return AS.isPublic();
112+
} else if (isInContext()) {
94113
if (AS.isInContext())
95114
return allowsPrivateAccess(getDeclContext(), AS.getDeclContext());
96115
else
97-
return AS.isPackage() || AS.isPublic();
116+
return AS.isPublic();
117+
} else {// It's public, so can't be a child of the argument scope
118+
return false;
98119
}
99-
if (isPackage())
100-
return AS.isPublic();
101-
// If this is public, it can't be less than access level of AS
102-
// so return false
103-
return false;
104120
}
105121

106-
/// Result depends on whether it's called at a use site or a decl site:
107-
///
108-
/// For example,
109-
///
110-
/// ```
111-
/// public func foo(_ arg: bar) {} // `bar` is a `package` decl in another
112-
/// module
113-
/// ```
114-
///
115-
/// The meaning of \c isInContext changes whether it's at the use site or the
116-
/// decl site.
117-
///
118-
/// The use site of \c bar, i.e. \c foo, is "in context" (decl context is
119-
/// non-null), regardless of the access level of \c foo (\c public in this
120-
/// case).
121-
///
122-
/// The decl site of \c bar is only "in context" if the access level of the
123-
/// decl is \c internal or more restrictive. The context at the decl site is\c
124-
/// FileUnit if the decl is \c fileprivate or \c private; \c ModuleDecl if \c
125-
/// internal, and null if \c package or \c public.
126122
bool isInContext() const { return getDeclContext() != nullptr; }
127123

128124
/// Returns the associated access level for diagnostic purposes.

include/swift/AST/DeclContext.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,15 @@ struct FragileFunctionKind {
229229
/// and therefore can safely access trailing memory. If you need to create a
230230
/// macro context, please see GenericContext for how to minimize new entries in
231231
/// the ASTHierarchy enum below.
232+
///
233+
/// The hierarchy between DeclContext subclasses is set in their ctors. For example,
234+
/// FileUnit ctor takes ModuleDecl as its parent DeclContext. The hierarchy from the most
235+
/// to least restrictive order is:
236+
/// decl/expr (e.g. ClassDecl) -> FileUnit -> ModuleDecl -> PackageUnit -> nullptr
237+
///
238+
/// There's an exception, however; the parent of ModuleDecl is set nullptr, not set to
239+
/// PackageUnit; ModuleDecl has a pointer to PackageUnit as its field, and it is treated as
240+
/// the enclosing scope of ModuleDecl.
232241
class alignas(1 << DeclContextAlignInBits) DeclContext
233242
: public ASTAllocated<DeclContext> {
234243
enum class ASTHierarchy : unsigned {
@@ -324,11 +333,7 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
324333
return getContextKind() <= DeclContextKind::Last_LocalDeclContextKind;
325334
}
326335

327-
/// \returns true if this is a context with package-wide scope, e.g. a package,
328-
/// a module, or a source file.
329-
LLVM_READONLY
330-
bool isPackageScopeContext() const; // see swift/AST/Module.h
331-
336+
/// \returns true if this is a package context
332337
LLVM_READONLY
333338
bool isPackageContext() const; // see swift/AST/Module.h
334339

@@ -512,9 +517,11 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
512517
return false;
513518
}
514519

515-
/// Returns the package context of the parent module.
520+
/// Returns the package unit of this context.
521+
/// \p lookupIfNotCurrent If the current decl context is not PackageUnit, look
522+
/// it up via parent module
516523
LLVM_READONLY
517-
PackageUnit *getParentModulePackage() const;
524+
PackageUnit *getPackageContext(bool lookupIfNotCurrent = false) const;
518525

519526
/// Returns the module context that contains this context.
520527
LLVM_READONLY

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,7 @@ ERROR(access_control_open_bad_decl,none,
16791679
WARNING(access_control_non_objc_open_member,none,
16801680
"non-'@objc' %0 in extensions cannot be overridden; use 'public' instead",
16811681
(DescriptiveDeclKind))
1682+
ERROR(access_control_requires_package_name, none, "decl has a package access level but no -package-name was passed", ())
16821683

16831684
ERROR(invalid_decl_attribute,none,
16841685
"'%0' attribute cannot be applied to this declaration", (DeclAttribute))

include/swift/AST/Module.h

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -159,33 +159,48 @@ class OverlayFile;
159159
/// location.
160160
class ModuleSourceFileLocationMap;
161161

162-
/// Package unit used to allow grouping of modules by a package name.
163-
/// ModuleDecl can set PackageUnit as a parent in its DeclContext
164-
/// See \c ModuleDecl
162+
/// A unit that allows grouping of modules by a package name.
163+
/// It's set as a property in ModuleDecl, instead of as its parent decl context,
164+
/// since otherwise it will break the existing decl context lookups that assume
165+
/// ModuleDecl as the top level context. See \c ModuleDecl
165166
class PackageUnit: public DeclContext {
166-
167+
/// Identifies this package and used for the equality check
167168
Identifier PackageName;
168-
169-
PackageUnit(Identifier name);
169+
/// Weakly references ModuleDecl that points to this package.
170+
/// Instead of having multiple ModuleDecls pointing to one PackageUnit, we
171+
/// create and set one PackageUnit per ModuleDecl, to make it easier to look
172+
/// up the module pointing to this package; this look up is needed in existing
173+
/// DeclContext functions, e.g. \c DeclContext::getModuleScopeContext,
174+
/// and \c DeclContext::getParentModule
175+
ModuleDecl &SourceModule;
176+
177+
PackageUnit(Identifier name, ModuleDecl &src)
178+
: DeclContext(DeclContextKind::Package, nullptr), PackageName(name),
179+
SourceModule(src) {}
170180

171181
public:
172-
static PackageUnit *
173-
create(Identifier name, ASTContext &ctx) {
174-
return new (ctx) PackageUnit(name);
182+
static PackageUnit *create(Identifier name, ModuleDecl &src,
183+
ASTContext &ctx) {
184+
return new (ctx) PackageUnit(name, src);
175185
}
176186

177187
static bool classof(const DeclContext *DC) {
178188
return DC->getContextKind() == DeclContextKind::Package;
179189
}
180190

181-
static bool classof(const PackageUnit *PU) {
182-
// FIXME: add a correct check
183-
return true;
184-
}
191+
static bool classof(const PackageUnit *PU) { return true; }
185192

186193
Identifier getName() const {
187194
return PackageName;
188195
}
196+
197+
ModuleDecl &getSourceModule() { return SourceModule; }
198+
199+
/// Equality check via package name instead of pointer comparison.
200+
/// Returns false if the name is empty.
201+
bool isSamePackageAs(PackageUnit *other) {
202+
return !(getName().empty()) && getName() == other->getName();
203+
}
189204
};
190205

191206
/// The minimum unit of compilation.
@@ -202,8 +217,10 @@ class ModuleDecl
202217
/// The ABI name of the module, if it differs from the module name.
203218
mutable Identifier ModuleABIName;
204219

205-
/// The name of the package this module belongs to
206-
mutable Identifier PackageName;
220+
/// A package this module belongs to. It's set as a property instead of a
221+
/// parent decl context; otherwise it will break the existing decl context
222+
/// lookups that assume ModuleDecl as the top level context.
223+
PackageUnit *Package = nullptr;
207224

208225
/// Module name to use when referenced in clients module interfaces.
209226
mutable Identifier ExportAsName;
@@ -313,7 +330,7 @@ class ModuleDecl
313330
/// Used by the debugger to bypass resilient access to fields.
314331
bool BypassResilience = false;
315332

316-
ModuleDecl(Identifier name, ASTContext &ctx, ImplicitImportInfo importInfo, PackageUnit *pkg);
333+
ModuleDecl(Identifier name, ASTContext &ctx, ImplicitImportInfo importInfo);
317334

318335
public:
319336
/// Creates a new module with a given \p name.
@@ -322,14 +339,13 @@ class ModuleDecl
322339
/// imported by each file of this module.
323340
static ModuleDecl *
324341
create(Identifier name, ASTContext &ctx,
325-
ImplicitImportInfo importInfo = ImplicitImportInfo(),
326-
PackageUnit *pkg = nullptr) {
327-
return new (ctx) ModuleDecl(name, ctx, importInfo, pkg);
342+
ImplicitImportInfo importInfo = ImplicitImportInfo()) {
343+
return new (ctx) ModuleDecl(name, ctx, importInfo);
328344
}
329345

330346
static ModuleDecl *
331-
createMainModule(ASTContext &ctx, Identifier name, ImplicitImportInfo iinfo, PackageUnit *pkg = nullptr) {
332-
auto *Mod = ModuleDecl::create(name, ctx, iinfo, pkg);
347+
createMainModule(ASTContext &ctx, Identifier name, ImplicitImportInfo iinfo) {
348+
auto *Mod = ModuleDecl::create(name, ctx, iinfo);
333349
Mod->Bits.ModuleDecl.IsMainModule = true;
334350
return Mod;
335351
}
@@ -440,18 +456,21 @@ class ModuleDecl
440456
ModuleABIName = name;
441457
}
442458

443-
/// Get the package name of the module
444-
Identifier getPackageName() const { return PackageName; }
445-
446-
/// Set the name of the package this module belongs to
447-
void setPackageName(Identifier name) {
448-
PackageName = name;
449-
// TODO: uncomment when PackageUnit gets passed to constructor
450-
// PackageUnit *pkgUnit = PackageUnit::create(name, getASTContext());
451-
// DeclContext newContext = DeclContext(DeclContextKind::Module, pkgUnit);
452-
// setDeclContext(&newContext);
459+
/// Get the package name of this module
460+
/// FIXME: remove this and bump module version rdar://104723918
461+
Identifier getPackageName() const {
462+
if (auto pkg = getPackage())
463+
return pkg->getName();
464+
return Identifier();
453465
}
454466

467+
/// Get the package associated with this module
468+
PackageUnit *getPackage() const { return Package; }
469+
470+
/// Set the package this module is associated with
471+
/// FIXME: rename this with setPackage(name) rdar://104723918
472+
void setPackageName(Identifier name);
473+
455474
Identifier getExportAsName() const { return ExportAsName; }
456475

457476
void setExportAsName(Identifier name) {
@@ -1070,7 +1089,7 @@ inline bool DeclContext::isModuleScopeContext() const {
10701089
return isModuleContext();
10711090
}
10721091

1073-
inline bool DeclContext::isPackageScopeContext() const {
1092+
inline bool DeclContext::isPackageContext() const {
10741093
return ParentAndKind.getInt() == ASTHierarchy::Package;
10751094
}
10761095

lib/AST/ASTVerifier.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -963,12 +963,6 @@ class Verifier : public ASTWalker {
963963
if (D->hasAccess()) {
964964
PrettyStackTraceDecl debugStack("verifying access", D);
965965
if (!D->getASTContext().isAccessControlDisabled()) {
966-
if (D->getFormalAccessScope().isPackage() &&
967-
D->getFormalAccess() < AccessLevel::Package) {
968-
Out << "non-package decl has no formal access scope\n";
969-
D->dump(Out);
970-
abort();
971-
}
972966
if (D->getFormalAccessScope().isPublic() &&
973967
D->getFormalAccess() < AccessLevel::Public) {
974968
Out << "non-public decl has no formal access scope\n";

0 commit comments

Comments
 (0)