Skip to content

Commit 7c87e1a

Browse files
authored
Merge pull request swiftlang#32085 from xymus/spi-isnt-internal-for-real
[Sema] Report public use of local SPIs by the exportability checker
2 parents 440cd0b + 63d9acd commit 7c87e1a

File tree

5 files changed

+62
-70
lines changed

5 files changed

+62
-70
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2692,7 +2692,8 @@ ERROR(decl_from_hidden_module,none,
26922692
"in an extension with public or '@usableFromInline' members|"
26932693
"in an extension with conditional conformances}2; "
26942694
"%select{%3 has been imported as implementation-only|"
2695-
"it is an SPI imported from %3}4",
2695+
"it is an SPI imported from %3|"
2696+
"it is SPI}4",
26962697
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
26972698
ERROR(conformance_from_implementation_only_module,none,
26982699
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -4766,7 +4767,8 @@ WARNING(resilience_decl_unavailable_warn,
47664767
ERROR(inlinable_decl_ref_from_hidden_module,
47674768
none, "%0 %1 cannot be used in " FRAGILE_FUNC_KIND "2 "
47684769
"because %select{%3 was imported implementation-only|"
4769-
"it is an SPI imported from %3}4",
4770+
"it is an SPI imported from %3|"
4771+
"it is SPI}4",
47704772
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
47714773

47724774
#undef FRAGILE_FUNC_KIND

lib/AST/Decl.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3550,17 +3550,9 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
35503550
return useSF && useSF->hasTestableOrPrivateImport(access, sourceModule);
35513551
}
35523552
case AccessLevel::Public:
3553-
case AccessLevel::Open: {
3554-
if (useDC && VD->isSPI()) {
3555-
auto useModuleScopeContext = useDC->getModuleScopeContext();
3556-
if (useModuleScopeContext == sourceDC->getModuleScopeContext()) return true;
3557-
3558-
auto *useSF = dyn_cast<SourceFile>(useModuleScopeContext);
3559-
return !useSF || useSF->isImportedAsSPI(VD);
3560-
}
3553+
case AccessLevel::Open:
35613554
return true;
35623555
}
3563-
}
35643556
llvm_unreachable("bad access level");
35653557
}
35663558

lib/Sema/TypeCheckAccess.cpp

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,7 @@ void AccessControlCheckerBase::checkTypeAccessImpl(
235235
contextAccessScope.isChildOf(*typeReprAccessScope)) {
236236
// Only if both the Type and the TypeRepr follow the access rules can
237237
// we exit; otherwise we have to emit a diagnostic.
238-
239-
if (typeReprAccessScope->isPublic() != contextAccessScope.isPublic() ||
240-
!typeReprAccessScope->isSPI() ||
241-
contextAccessScope.isSPI()) {
242-
// And we exit only if there is no SPI violation.
243-
return;
244-
}
238+
return;
245239
}
246240
problematicAccessScope = *typeReprAccessScope;
247241

@@ -349,8 +343,7 @@ void AccessControlCheckerBase::checkGenericParamAccess(
349343
(thisDowngrade == DowngradeToWarning::No &&
350344
downgradeToWarning == DowngradeToWarning::Yes) ||
351345
(!complainRepr &&
352-
typeAccessScope.hasEqualDeclContextWith(minAccessScope)) ||
353-
typeAccessScope.isSPI()) {
346+
typeAccessScope.hasEqualDeclContextWith(minAccessScope))) {
354347
minAccessScope = typeAccessScope;
355348
complainRepr = thisComplainRepr;
356349
accessControlErrorKind = callbackACEK;
@@ -375,7 +368,7 @@ void AccessControlCheckerBase::checkGenericParamAccess(
375368
const_cast<GenericContext *>(ownerCtx)),
376369
accessScope, DC, callback);
377370

378-
if (minAccessScope.isPublic() && !minAccessScope.isSPI())
371+
if (minAccessScope.isPublic())
379372
return;
380373

381374
// FIXME: Promote these to an error in the next -swift-version break.
@@ -958,7 +951,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
958951
});
959952
}
960953

961-
if (!minAccessScope.isPublic() || minAccessScope.isSPI()) {
954+
if (!minAccessScope.isPublic()) {
962955
auto minAccess = minAccessScope.accessLevelForDiagnostics();
963956
auto functionKind = isa<ConstructorDecl>(fn)
964957
? FK_Initializer
@@ -970,24 +963,13 @@ class AccessControlChecker : public AccessControlCheckerBase,
970963
? fn->getFormalAccess()
971964
: minAccessScope.requiredAccessForDiagnostics();
972965

973-
// Report as an SPI problem if the type is at least as public as the decl.
974-
AccessScope contextAccessScope =
975-
fn->getFormalAccessScope(fn->getDeclContext(), checkUsableFromInline);
976-
977-
if (contextAccessScope.isSPI()) {
978-
auto diag = fn->diagnose(diag::function_type_spi, functionKind,
979-
problemIsResult, minAccess,
980-
minAccess >= AccessLevel::Public);
981-
highlightOffendingType(diag, complainRepr);
982-
} else {
983-
auto diagID = diag::function_type_access;
984-
if (downgradeToWarning == DowngradeToWarning::Yes)
985-
diagID = diag::function_type_access_warn;
986-
auto diag = fn->diagnose(diagID, isExplicit, fnAccess,
987-
isa<FileUnit>(fn->getDeclContext()), minAccess,
988-
functionKind, problemIsResult);
989-
highlightOffendingType(diag, complainRepr);
990-
}
966+
auto diagID = diag::function_type_access;
967+
if (downgradeToWarning == DowngradeToWarning::Yes)
968+
diagID = diag::function_type_access_warn;
969+
auto diag = fn->diagnose(diagID, isExplicit, fnAccess,
970+
isa<FileUnit>(fn->getDeclContext()), minAccess,
971+
functionKind, problemIsResult);
972+
highlightOffendingType(diag, complainRepr);
991973
}
992974
}
993975

@@ -1471,6 +1453,31 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
14711453
class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
14721454
class Diagnoser;
14731455

1456+
// Problematic origin of an exported type.
1457+
//
1458+
// This enum must be kept in sync with
1459+
// diag::decl_from_hidden_module and
1460+
// diag::conformance_from_implementation_only_module.
1461+
enum class DisallowedOriginKind : uint8_t {
1462+
ImplementationOnly,
1463+
SPIImported,
1464+
SPILocal,
1465+
None
1466+
};
1467+
1468+
// If there's an exportability problem with \p typeDecl, get its origin kind.
1469+
static DisallowedOriginKind getDisallowedOriginKind(
1470+
const TypeDecl *typeDecl, const SourceFile &SF, const Decl *context) {
1471+
ModuleDecl *M = typeDecl->getModuleContext();
1472+
if (SF.isImportedImplementationOnly(M))
1473+
return DisallowedOriginKind::ImplementationOnly;
1474+
else if (typeDecl->isSPI() && !context->isSPI())
1475+
return context->getModuleContext() == M ?
1476+
DisallowedOriginKind::SPILocal :
1477+
DisallowedOriginKind::SPIImported;
1478+
return DisallowedOriginKind::None;
1479+
};
1480+
14741481
void checkTypeImpl(
14751482
Type type, const TypeRepr *typeRepr, const SourceFile &SF,
14761483
const Decl *context,
@@ -1487,11 +1494,9 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
14871494
const_cast<TypeRepr *>(typeRepr)->walk(TypeReprIdentFinder(
14881495
[&](const ComponentIdentTypeRepr *component) {
14891496
TypeDecl *typeDecl = component->getBoundDecl();
1490-
ModuleDecl *M = typeDecl->getModuleContext();
1491-
bool isImplementationOnly = SF.isImportedImplementationOnly(M);
1492-
if (isImplementationOnly ||
1493-
(SF.isImportedAsSPI(typeDecl) && !context->isSPI())) {
1494-
diagnoser.diagnoseType(typeDecl, component, isImplementationOnly);
1497+
auto originKind = getDisallowedOriginKind(typeDecl, SF, context);
1498+
if (originKind != DisallowedOriginKind::None) {
1499+
diagnoser.diagnoseType(typeDecl, component, originKind);
14951500
foundAnyIssues = true;
14961501
}
14971502

@@ -1519,12 +1524,9 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
15191524
: SF(SF), context(context), diagnoser(diagnoser) {}
15201525

15211526
void visitTypeDecl(const TypeDecl *typeDecl) {
1522-
ModuleDecl *M = typeDecl->getModuleContext();
1523-
bool isImplementationOnly = SF.isImportedImplementationOnly(M);
1524-
if (isImplementationOnly ||
1525-
(SF.isImportedAsSPI(typeDecl) && !context->isSPI()))
1526-
diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr,
1527-
isImplementationOnly);
1527+
auto originKind = getDisallowedOriginKind(typeDecl, SF, context);
1528+
if (originKind != DisallowedOriginKind::None)
1529+
diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr, originKind);
15281530
}
15291531

15301532
void visitSubstitutionMap(SubstitutionMap subs) {
@@ -1621,7 +1623,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16211623
});
16221624
}
16231625

1624-
// These enums must be kept in sync with
1626+
// This enum must be kept in sync with
16251627
// diag::decl_from_hidden_module and
16261628
// diag::conformance_from_implementation_only_module.
16271629
enum class Reason : unsigned {
@@ -1630,10 +1632,6 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16301632
ExtensionWithPublicMembers,
16311633
ExtensionWithConditionalConformances
16321634
};
1633-
enum class HiddenImportKind : uint8_t {
1634-
ImplementationOnly,
1635-
SPI
1636-
};
16371635

16381636
class Diagnoser {
16391637
const Decl *D;
@@ -1643,16 +1641,13 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16431641

16441642
void diagnoseType(const TypeDecl *offendingType,
16451643
const TypeRepr *complainRepr,
1646-
bool isImplementationOnly) const {
1644+
DisallowedOriginKind originKind) const {
16471645
ModuleDecl *M = offendingType->getModuleContext();
1648-
HiddenImportKind importKind = isImplementationOnly?
1649-
HiddenImportKind::ImplementationOnly:
1650-
HiddenImportKind::SPI;
16511646
auto diag = D->diagnose(diag::decl_from_hidden_module,
16521647
offendingType->getDescriptiveKind(),
16531648
offendingType->getName(),
16541649
static_cast<unsigned>(reason), M->getName(),
1655-
static_cast<unsigned>(importKind));
1650+
static_cast<unsigned>(originKind));
16561651
highlightOffendingType(diag, complainRepr);
16571652
}
16581653

@@ -1981,7 +1976,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
19811976
DE.diagnose(diagLoc, diag::decl_from_hidden_module,
19821977
PGD->getDescriptiveKind(), PGD->getName(),
19831978
static_cast<unsigned>(Reason::General), M->getName(),
1984-
static_cast<unsigned>(HiddenImportKind::ImplementationOnly)
1979+
static_cast<unsigned>(DisallowedOriginKind::ImplementationOnly)
19851980
);
19861981
if (refRange.isValid())
19871982
diag.highlight(refRange);

test/SPI/local_spi_decls.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
@_spi() public func emptyParensSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
1111
@_spi(set) public func keywordSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
1212

13-
@_spi(S) public class SPIClass {} // expected-note 2 {{type declared here}}
13+
@_spi(S) public class SPIClass {} // expected-note 3 {{type declared here}}
1414
// expected-note @-1 2 {{class 'SPIClass' is not '@usableFromInline' or public}}
1515
class InternalClass {} // expected-note 2 {{type declared here}}
1616
private class PrivateClass {} // expected-note 2 {{type declared here}}
1717

1818
@_spi(S) public protocol SPIProtocol {} // expected-note {{type declared here}}
1919

2020
@_spi(S) public func useOfSPITypeOk(_ p0: SPIProtocol, p1: SPIClass) -> SPIClass { fatalError() } // OK
21-
public func useOfSPITypeInvalid() -> SPIClass { fatalError() } // expected-error {{function cannot be declared public because its result uses an '@_spi' type}}
22-
@_spi(S) public func spiUseOfInternalType() -> InternalClass { fatalError() } // expected-error{{function cannot be declared '@_spi' because its result uses an internal type}}
23-
@_spi(S) public func spiUseOfPrivateType(_ a: PrivateClass) { fatalError() } // expected-error{{function cannot be declared '@_spi' because its parameter uses a private type}}
21+
public func useOfSPITypeInvalid() -> SPIClass { fatalError() } // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
22+
@_spi(S) public func spiUseOfInternalType() -> InternalClass { fatalError() } // expected-error{{function cannot be declared public because its result uses an internal type}}
23+
@_spi(S) public func spiUseOfPrivateType(_ a: PrivateClass) { fatalError() } // expected-error{{function cannot be declared public because its parameter uses a private type}}
2424

2525
@inlinable
2626
func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
@@ -41,12 +41,17 @@ private protocol PrivateProtocol {} // expected-note {{type declared here}}
4141

4242
@_spi(S) public class BadSubclass : InternalClass {} // expected-error{{class cannot be declared public because its superclass is internal}}
4343
@_spi(S) public class OkSPISubclass : SPIClass {} // OK
44-
public class BadPublicClass : SPIClass {} // expected-error {{class cannot be declared public because its superclass is '@_spi'}}
44+
public class BadPublicClass : SPIClass {} // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
4545
@_spi(S) public class BadSPIClass : PrivateClass {} // expected-error {{class cannot be declared public because its superclass is private}}
4646

4747
@_spi(s) public func genFunc<T: PrivateProtocol>(_ t: T) {} // expected-error {{global function cannot be declared public because its generic parameter uses a private type}}
48-
public func genFuncBad<T: SPIProtocol>(_ t: T) {} // expected-error {{global function cannot be declared public because its generic parameter uses an '@_spi' type}}
48+
public func genFuncBad<T: SPIProtocol>(_ t: T) {} // expected-error {{cannot use protocol 'SPIProtocol' here; it is SPI}}
4949
@_spi(S) public func genFuncSPI<T: SPIProtocol>(_ t: T) {} // OK
5050

5151
@_spi(S) private func privateCantBeSPI() {} // expected-error{{private global function cannot be declared '@_spi' because only public and open declarations can be '@_spi'}} {{1-10=}}
5252
@_spi(S) func internalCantBeSPI() {} // expected-error{{internal global function cannot be declared '@_spi' because only public and open declarations can be '@_spi'}} {{1-10=}}
53+
54+
public struct PublicStructWithProperties {
55+
public var a: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
56+
public var b = SPIClass() // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
57+
}

test/SPI/spi_client.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ print(ps.spiVar)
5050
otherApiFunc() // expected-error {{cannot find 'otherApiFunc' in scope}}
5151

5252
public func publicUseOfSPI(param: SPIClass) -> SPIClass {} // expected-error 2{{cannot use class 'SPIClass' here; it is an SPI imported from 'SPIHelper'}}
53-
// expected-error@-1 {{function cannot be declared public because its parameter uses an '@_spi' type}}
5453
public func publicUseOfSPI2() -> [SPIClass] {} // expected-error {{cannot use class 'SPIClass' here; it is an SPI imported from 'SPIHelper'}}
55-
// expected-error@-1 {{function cannot be declared public because its result uses an '@_spi' type}}
5654

5755
@inlinable
5856
func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}

0 commit comments

Comments
 (0)