Skip to content

Commit 95dae6e

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 * Fix tests Resolves rdar://104987295, rdar://105187216
1 parent 302180f commit 95dae6e

File tree

10 files changed

+133
-72
lines changed

10 files changed

+133
-72
lines changed

include/swift/AST/AccessScope.h

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ class AccessScope {
6666
bool operator==(AccessScope RHS) const { return Value == RHS.Value; }
6767
bool operator!=(AccessScope RHS) const { return !(*this == RHS); }
6868
bool hasEqualDeclContextWith(AccessScope RHS) const {
69-
if (isPublic())
70-
return RHS.isPublic();
71-
if (isPackage())
72-
return RHS.isPackage();
7369
return getDeclContext() == RHS.getDeclContext();
7470
}
7571

@@ -81,26 +77,23 @@ class AccessScope {
8177
}
8278
bool isFileScope() const;
8379
bool isInternal() const;
84-
bool isPackage() const {
85-
return !Value.getPointer() && Value.getInt() == AccessLimitKind::Package;
86-
}
80+
bool isPackage() const;
8781

8882
/// Returns true if the context of this (use site) is more restrictive than
8983
/// the argument context (decl site). This function does _not_ check the
9084
/// restrictiveness of the access level between this and the argument. \see
9185
/// AccessScope::isInContext
9286
bool isChildOf(AccessScope AS) const {
93-
if (isInContext()) {
87+
if (isPackage())
88+
return AS.isPublic();
89+
else if (isInContext()) {
9490
if (AS.isInContext())
9591
return allowsPrivateAccess(getDeclContext(), AS.getDeclContext());
9692
else
97-
return AS.isPackage() || AS.isPublic();
98-
}
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;
93+
return AS.isPublic();
94+
} else // If this is public, it can't be less than access level of AS so
95+
// return false
96+
return false;
10497
}
10598

10699
/// Result depends on whether it's called at a use site or a decl site:

include/swift/AST/DeclContext.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,11 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
512512
return false;
513513
}
514514

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

519521
/// Returns the module context that contains this context.
520522
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: 45 additions & 25 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 reference 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;
207224

208225
/// Module name to use when referenced in clients module interfaces.
209226
mutable Identifier ExportAsName;
@@ -440,18 +457,21 @@ class ModuleDecl
440457
ModuleABIName = name;
441458
}
442459

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);
460+
/// Get the package name of this module
461+
/// FIXME: remove this and bump module version rdar://104723918
462+
Identifier getPackageName() const {
463+
if (auto pkg = getPackage())
464+
return pkg->getName();
465+
return Identifier();
453466
}
454467

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

457477
void setExportAsName(Identifier name) {

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";

lib/AST/Decl.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3773,6 +3773,10 @@ static AccessLevel getAdjustedFormalAccess(const ValueDecl *VD,
37733773
}
37743774

37753775
if (useDC) {
3776+
// If the use site decl context is PackageUnit, just return
3777+
// the access level that's passed in
3778+
if (auto usePkg = useDC->getPackageContext())
3779+
return access;
37763780
// Check whether we need to modify the access level based on
37773781
// @testable/@_private import attributes.
37783782
auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext());
@@ -3925,8 +3929,14 @@ getAccessScopeForFormalAccess(const ValueDecl *VD,
39253929
: AccessLimitKind::None);
39263930
case AccessLevel::Internal:
39273931
return AccessScope(resultDC->getParentModule());
3928-
case AccessLevel::Package:
3929-
return AccessScope::getPackage();
3932+
case AccessLevel::Package: {
3933+
auto pkg = resultDC->getPackageContext(true);
3934+
if (!pkg) {
3935+
auto &d = VD->getASTContext().Diags;
3936+
d.diagnose(VD->getLoc(), diag::access_control_requires_package_name);
3937+
}
3938+
return AccessScope(pkg);
3939+
}
39303940
case AccessLevel::Public:
39313941
case AccessLevel::Open:
39323942
return AccessScope::getPublic();
@@ -3965,10 +3975,6 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
39653975
// useDC is null only when caller wants to skip non-public type checks.
39663976
if (!useDC) return true;
39673977

3968-
// Check package access; accessing package decl should not be allowed if package names are different
3969-
if (accessScope.isPackage())
3970-
return VD->getDeclContext()->getParentModule()->getPackageName() == useDC->getParentModule()->getPackageName();
3971-
39723978
// Check SPI access
39733979
if (!VD->isSPI()) return true;
39743980
auto useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext());
@@ -4083,7 +4089,11 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
40834089
auto *useSF = dyn_cast<SourceFile>(useFile);
40844090
return useSF && useSF->hasTestableOrPrivateImport(access, sourceModule);
40854091
}
4086-
case AccessLevel::Package:
4092+
case AccessLevel::Package: {
4093+
auto srcPkg = sourceDC->getPackageContext(true);
4094+
auto usePkg = useDC->getPackageContext(true);
4095+
return usePkg->isSamePackageAs(srcPkg);
4096+
}
40874097
case AccessLevel::Public:
40884098
case AccessLevel::Open:
40894099
return true;

lib/AST/DeclContext.cpp

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,32 @@ DeclContext *DeclContext::getParentForLookup() const {
274274
return getParent();
275275
}
276276

277-
PackageUnit *DeclContext::getParentModulePackage() const {
278-
auto parentModule = getParentModule();
279-
auto pkg = parentModule->getParent();
280-
if (pkg)
281-
return const_cast<PackageUnit *>(cast<PackageUnit>(pkg));
277+
PackageUnit *DeclContext::getPackageContext(bool lookupIfNotCurrent) const {
278+
// Current decl context might be PackageUnit, which is not in the
279+
// DeclContext hierarchy, so check for it first
280+
if (ParentAndKind.getInt() == ASTHierarchy::Package) {
281+
return const_cast<PackageUnit *>(cast<PackageUnit>(this));
282+
}
283+
284+
// If the current context is not PackageUnit, look it up via
285+
// the parent module if needed
286+
if (lookupIfNotCurrent) {
287+
auto mdecl = getParentModule();
288+
return mdecl->getPackage();
289+
}
282290
return nullptr;
283291
}
284292

285293
ModuleDecl *DeclContext::getParentModule() const {
294+
// If the current context is PackageUnit, return the module
295+
// decl context pointing to the current context. This check
296+
// needs to be done first as PackageUnit is not in the DeclContext
297+
// hierarchy.
298+
if (auto pkg = getPackageContext()) {
299+
auto &mdecl = pkg->getSourceModule();
300+
return &mdecl;
301+
}
302+
286303
const DeclContext *DC = this;
287304
while (!DC->isModuleContext())
288305
DC = DC->getParent();
@@ -292,7 +309,7 @@ ModuleDecl *DeclContext::getParentModule() const {
292309
SourceFile *DeclContext::getParentSourceFile() const {
293310
const DeclContext *DC = this;
294311
SourceLoc loc;
295-
while (!DC->isModuleScopeContext()) {
312+
while (DC && !DC->isModuleScopeContext()) {
296313
// If we don't have a source location yet, try to grab one from this
297314
// context.
298315
if (loc.isInvalid()) {
@@ -323,6 +340,9 @@ SourceFile *DeclContext::getParentSourceFile() const {
323340
DC = DC->getParent();
324341
}
325342

343+
if (!DC)
344+
return nullptr;
345+
326346
auto fallbackSF = const_cast<SourceFile *>(dyn_cast<SourceFile>(DC));
327347
if (auto module = DC->getParentModule()) {
328348
if (auto sf = module->getSourceFileContainingLocation(loc))
@@ -345,15 +365,24 @@ SourceFile *DeclContext::getOutermostParentSourceFile() const {
345365
}
346366

347367
DeclContext *DeclContext::getModuleScopeContext() const {
348-
auto DC = const_cast<DeclContext*>(this);
368+
// If the current context is PackageUnit, return the module
369+
// decl context pointing to the current context. This check
370+
// needs to be done first as PackageUnit is not in the DeclContext
371+
// hierarchy.
372+
if (auto pkg = getPackageContext()) {
373+
auto &mdecl = pkg->getSourceModule();
374+
return &mdecl;
375+
}
349376

377+
auto DC = const_cast<DeclContext *>(this);
350378
while (true) {
351379
if (DC->ParentAndKind.getInt() == ASTHierarchy::FileUnit)
352380
return DC;
353381
if (auto NextDC = DC->getParent()) {
354382
DC = NextDC;
355383
} else {
356-
assert(isa<ModuleDecl>(DC->getAsDecl()));
384+
assert(DC && isa<ModuleDecl>(DC->getAsDecl()) &&
385+
"no module decl context found!");
357386
return DC;
358387
}
359388
}
@@ -1235,6 +1264,11 @@ bool AccessScope::isInternal() const {
12351264
return DC && isa<ModuleDecl>(DC);
12361265
}
12371266

1267+
bool AccessScope::isPackage() const {
1268+
auto DC = getDeclContext();
1269+
return DC && isa<PackageUnit>(DC);
1270+
}
1271+
12381272
AccessLevel AccessScope::accessLevelForDiagnostics() const {
12391273
if (isPublic())
12401274
return AccessLevel::Public;
@@ -1254,6 +1288,14 @@ bool AccessScope::allowsPrivateAccess(const DeclContext *useDC, const DeclContex
12541288
if (useDC->isChildContextOf(sourceDC))
12551289
return true;
12561290

1291+
// If the decl site has package acl, but the use site
1292+
// has internal or less acl, check if it belongs to
1293+
// the same package as the decl site's to allow access.
1294+
if (auto srcPkg = sourceDC->getPackageContext()) {
1295+
if (auto usePkg = sourceDC->getPackageContext(true)) {
1296+
return usePkg->isSamePackageAs(srcPkg);
1297+
}
1298+
}
12571299
// Do not allow access if the sourceDC is in a different file
12581300
auto useSF = useDC->getParentSourceFile();
12591301
if (useSF != sourceDC->getParentSourceFile())

lib/AST/Module.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -543,11 +543,6 @@ void SourceLookupCache::invalidate() {
543543
(void)SameSizeSmallVector{std::move(AllVisibleValues)};
544544
}
545545

546-
PackageUnit::PackageUnit(Identifier name)
547-
: DeclContext(DeclContextKind::Package, nullptr) {
548-
PackageName = name;
549-
}
550-
551546
//===----------------------------------------------------------------------===//
552547
// Module Implementation
553548
//===----------------------------------------------------------------------===//
@@ -3068,6 +3063,10 @@ SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
30683063
return restrictiveImport;
30693064
}
30703065

3066+
void ModuleDecl::setPackageName(Identifier name) {
3067+
Package = PackageUnit::create(name, *this, getASTContext());
3068+
}
3069+
30713070
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
30723071
if (module == this) return false;
30733072

lib/Sema/TypeCheckAccess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2286,7 +2286,7 @@ static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) {
22862286
desiredAccessScope = AccessScope(ED->getModuleContext());
22872287
break;
22882288
case AccessLevel::Package:
2289-
desiredAccessScope = AccessScope::getPackage();
2289+
desiredAccessScope = AccessScope(ED->getPackageContext(true));
22902290
break;
22912291
case AccessLevel::Public:
22922292
case AccessLevel::Open:

test/Sema/accessibility.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-objc-interop -disable-objc-attr-requires-foundation-module -swift-version 5
1+
// RUN: %target-typecheck-verify-swift -enable-objc-interop -disable-objc-attr-requires-foundation-module -swift-version 5 -package-name mylib
22

33
/// Test structs with protocols and extensions
44
public protocol PublicProto {
@@ -1021,8 +1021,8 @@ public struct PublicWithInternalSettersConformPublic : PublicMutationOperations
10211021
}
10221022

10231023
public struct PublicWithPackageSettersConformPublic : PublicMutationOperations {
1024-
public package(set) var size = 0 // FIXME: rdar://104987295 this should error
1025-
public package(set) subscript (_: Int) -> Int { // FIXME: rdar://104987295 this should error
1024+
public package(set) var size = 0 // expected-error {{setter for property 'size' must be declared public because it matches a requirement in public protocol 'PublicMutationOperations'}} {{none}} expected-note {{mark the property as 'public' to satisfy the requirement}} {{10-23=}}
1025+
public package(set) subscript (_: Int) -> Int { // expected-error {{subscript setter must be declared public because it matches a requirement in public protocol 'PublicMutationOperations'}} {{none}} expected-note {{mark the subscript as 'public' to satisfy the requirement}} {{10-23=}}
10261026
get { return 42 }
10271027
set {}
10281028
}

0 commit comments

Comments
 (0)