Skip to content

Commit 2e73cb4

Browse files
committed
[Sema] Type-check the use and exposability of SPI decls
1 parent 56880d7 commit 2e73cb4

File tree

6 files changed

+168
-43
lines changed

6 files changed

+168
-43
lines changed

Diff for: include/swift/AST/DiagnosticsSema.def

+23-9
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ FIXIT(insert_type_qualification,"%0.",(Type))
148148

149149
ERROR(candidate_inaccessible,none,
150150
"%0 is inaccessible due to "
151-
"'%select{private|fileprivate|internal|%error|%error}1' protection level",
151+
"'%select{private|fileprivate|internal|SPI|SPI}1' protection level",
152152
(DeclBaseName, AccessLevel))
153153

154154
NOTE(note_candidate_inaccessible,none,
@@ -158,7 +158,7 @@ NOTE(note_candidate_inaccessible,none,
158158

159159
ERROR(init_candidate_inaccessible,none,
160160
"%0 initializer is inaccessible due to "
161-
"'%select{private|fileprivate|internal|%error|%error}1' protection level",
161+
"'%select{private|fileprivate|internal|SPI|SPI}1' protection level",
162162
(Type, AccessLevel))
163163

164164
ERROR(cannot_pass_rvalue_mutating_subelement,none,
@@ -1681,6 +1681,13 @@ ERROR(function_type_access,none,
16811681
"because its %select{parameter|result}5 uses "
16821682
"%select{a private|a fileprivate|an internal|%error|%error}3 type",
16831683
(bool, AccessLevel, bool, AccessLevel, unsigned, bool))
1684+
ERROR(function_type_spi,none,
1685+
"%select{function|method|initializer}0 "
1686+
"cannot be declared '@_spi' "
1687+
"because its %select{parameter|result}1 uses "
1688+
"%select{a private|a fileprivate|an internal|%error|%error}2 type "
1689+
"without a compatible '@_spi'",
1690+
(unsigned, bool, AccessLevel))
16841691
WARNING(function_type_access_warn,none,
16851692
"%select{function|method|initializer}4 "
16861693
"%select{should be declared %select{private|fileprivate|internal|%error|%error}1"
@@ -2637,12 +2644,13 @@ NOTE(enum_raw_value_incrementing_from_zero,none,
26372644
NOTE(construct_raw_representable_from_unwrapped_value,none,
26382645
"construct %0 from unwrapped %1 value", (Type, Type))
26392646

2640-
ERROR(decl_from_implementation_only_module,none,
2647+
ERROR(decl_from_hidden_module,none,
26412648
"cannot use %0 %1 %select{here|"
26422649
"in an extension with public or '@usableFromInline' members|"
2643-
"in an extension with conditional conformances}2; %3 has been imported "
2644-
"as implementation-only",
2645-
(DescriptiveDeclKind, DeclName, unsigned, Identifier))
2650+
"in an extension with conditional conformances}2; "
2651+
"%select{%3 has been imported as implementation-only|"
2652+
"it is an SPI imported from %3}4",
2653+
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
26462654
ERROR(conformance_from_implementation_only_module,none,
26472655
"cannot use conformance of %0 to %1 %select{here|"
26482656
"in an extension with public or '@usableFromInline' members|"
@@ -4593,10 +4601,16 @@ WARNING(resilience_decl_unavailable_warn,
45934601
"should not be referenced from " FRAGILE_FUNC_KIND "3",
45944602
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))
45954603

4596-
ERROR(inlinable_decl_ref_implementation_only,
4604+
ERROR(resilience_decl_unavailable_spi,
4605+
none, DECL_OR_ACCESSOR "4 %1 is imported as SPI; "
4606+
"it cannot be referenced from " FRAGILE_FUNC_KIND "3",
4607+
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))
4608+
4609+
ERROR(inlinable_decl_ref_from_hidden_module,
45974610
none, "%0 %1 cannot be used in " FRAGILE_FUNC_KIND "2 "
4598-
"because %3 was imported implementation-only",
4599-
(DescriptiveDeclKind, DeclName, unsigned, Identifier))
4611+
"because %select{%3 was imported implementation-only|"
4612+
"it is an SPI imported from %3}4",
4613+
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
46004614

46014615
#undef FRAGILE_FUNC_KIND
46024616

Diff for: include/swift/AST/SourceFile.h

+3
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ class SourceFile final : public FileUnit {
287287
lookupImportedSPIs(const ModuleDecl *importedModule,
288288
SmallVectorImpl<Identifier> &spis) const override;
289289

290+
// Is \p targetDecl accessible as an explictly imported SPI from this file?
291+
bool isImportedAsSPI(const ValueDecl *targetDecl) const;
292+
290293
bool shouldCrossImport() const;
291294

292295
/// Register a separately-imported overlay as shadowing the module that

Diff for: lib/AST/Decl.cpp

+26-7
Original file line numberDiff line numberDiff line change
@@ -3150,8 +3150,8 @@ static AccessLevel getMaximallyOpenAccessFor(const ValueDecl *decl) {
31503150
return AccessLevel::Public;
31513151
}
31523152

3153-
/// Adjust \p access based on whether \p VD is \@usableFromInline or has been
3154-
/// testably imported from \p useDC.
3153+
/// Adjust \p access based on whether \p VD is \@usableFromInline, has been
3154+
/// testably imported from \p useDC or \p VD is an imported SPI.
31553155
///
31563156
/// \p access isn't always just `VD->getFormalAccess()` because this adjustment
31573157
/// may be for a write, in which case the setter's access might be used instead.
@@ -3177,6 +3177,9 @@ static AccessLevel getAdjustedFormalAccess(const ValueDecl *VD,
31773177
if (!useSF) return access;
31783178
if (useSF->hasTestableOrPrivateImport(access, VD))
31793179
return getMaximallyOpenAccessFor(VD);
3180+
} else if (VD->getAttrs().hasAttribute<SPIAccessControlAttr>()) {
3181+
// Restrict access to SPI decls.
3182+
return AccessLevel::Internal;
31803183
}
31813184

31823185
return access;
@@ -3267,7 +3270,7 @@ bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
32673270

32683271
/// Given the formal access level for using \p VD, compute the scope where
32693272
/// \p VD may be accessed, taking \@usableFromInline, \@testable imports,
3270-
/// and enclosing access levels into account.
3273+
/// \@_spi imports, and enclosing access levels into account.
32713274
///
32723275
/// \p access isn't always just `VD->getFormalAccess()` because this adjustment
32733276
/// may be for a write, in which case the setter's access might be used instead.
@@ -3322,9 +3325,17 @@ getAccessScopeForFormalAccess(const ValueDecl *VD,
33223325
case AccessLevel::Internal:
33233326
return AccessScope(resultDC->getParentModule());
33243327
case AccessLevel::Public:
3325-
case AccessLevel::Open:
3328+
case AccessLevel::Open: {
3329+
if (useDC) {
3330+
auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext());
3331+
if (useSF &&
3332+
VD->getAttrs().hasAttribute<SPIAccessControlAttr>() &&
3333+
!useSF->isImportedAsSPI(VD))
3334+
return AccessScope(VD->getModuleContext(), /*private*/false);
3335+
}
33263336
return AccessScope::getPublic();
33273337
}
3338+
}
33283339

33293340
llvm_unreachable("unknown access level");
33303341
}
@@ -3356,8 +3367,8 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
33563367
AccessScope(useDC).isChildOf(accessScope);
33573368
}
33583369

3359-
/// Checks if \p VD may be used from \p useDC, taking \@testable imports into
3360-
/// account.
3370+
/// Checks if \p VD may be used from \p useDC, taking \@testable and \@_spi
3371+
/// imports into account.
33613372
///
33623373
/// When \p access is the same as `VD->getFormalAccess()` and the enclosing
33633374
/// context of \p VD is usable from \p useDC, this ought to be the same as
@@ -3428,9 +3439,17 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
34283439
return useSF && useSF->hasTestableOrPrivateImport(access, sourceModule);
34293440
}
34303441
case AccessLevel::Public:
3431-
case AccessLevel::Open:
3442+
case AccessLevel::Open: {
3443+
if (useDC) {
3444+
auto *useSF = dyn_cast<SourceFile>(useDC->getModuleScopeContext());
3445+
if (useSF &&
3446+
VD->getAttrs().hasAttribute<SPIAccessControlAttr>() &&
3447+
!useSF->isImportedAsSPI(VD))
3448+
return false;
3449+
}
34323450
return true;
34333451
}
3452+
}
34343453
llvm_unreachable("bad access level");
34353454
}
34363455

Diff for: lib/AST/Module.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,23 @@ void SourceFile::lookupImportedSPIs(const ModuleDecl *importedModule,
17931793
}
17941794
}
17951795

1796+
bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const {
1797+
if (!targetDecl->getAttrs().hasAttribute<SPIAccessControlAttr>())
1798+
return false;
1799+
1800+
auto targetModule = targetDecl->getModuleContext();
1801+
SmallVector<Identifier, 4> importedSpis;
1802+
lookupImportedSPIs(targetModule, importedSpis);
1803+
1804+
for (auto attr : targetDecl->getAttrs().getAttributes<SPIAccessControlAttr>())
1805+
for (auto declSPI : attr->getSPINames())
1806+
for (auto importedSPI : importedSpis)
1807+
if (importedSPI == declSPI)
1808+
return true;
1809+
1810+
return false;
1811+
}
1812+
17961813
bool SourceFile::shouldCrossImport() const {
17971814
return Kind != SourceFileKind::SIL && Kind != SourceFileKind::Interface &&
17981815
getASTContext().LangOpts.EnableCrossImportOverlays;

Diff for: lib/Sema/ResilienceDiagnostics.cpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
184184
if (downgradeToWarning == DowngradeToWarning::Yes)
185185
diagID = diag::resilience_decl_unavailable_warn;
186186

187+
// If SPI, don't mention the access level.
188+
const SourceFile *SF = DC->getParentSourceFile();
189+
if (SF && SF->isImportedAsSPI(D))
190+
diagID = diag::resilience_decl_unavailable_spi;
191+
187192
Context.Diags.diagnose(
188193
loc, diagID,
189194
D->getDescriptiveKind(), diagName,
@@ -206,15 +211,19 @@ static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D,
206211
const SourceFile &userSF,
207212
FragileFunctionKind fragileKind) {
208213
auto definingModule = D->getModuleContext();
209-
if (!userSF.isImportedImplementationOnly(definingModule))
214+
215+
bool isImplementationOnly =
216+
userSF.isImportedImplementationOnly(definingModule);
217+
if (!isImplementationOnly && !userSF.isImportedAsSPI(D))
210218
return false;
211219

212220
// TODO: different diagnostics
213221
ASTContext &ctx = definingModule->getASTContext();
214-
ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_implementation_only,
222+
ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module,
215223
D->getDescriptiveKind(), D->getFullName(),
216224
static_cast<unsigned>(fragileKind),
217-
definingModule->getName());
225+
definingModule->getName(),
226+
static_cast<unsigned>(!isImplementationOnly));
218227
return true;
219228
}
220229

0 commit comments

Comments
 (0)