Skip to content

Commit 3b4bb19

Browse files
authored
@_implementationOnly: fix over-eager checking for vars in extensions (#24629)
The logic I had here checked whether an extension used an implementation-only type whenever there was a declaration within that extension that needed checking...but unfortunately that included not just PatternBindingDecls (whose access is filtered at a later step) but things like IfConfigDecls (#if). Change this to only check signatures of extensions with ABI-public members, something that is tested once when visiting an ExtensionDecl. Additionally, skip AccessorDecls entirely, since they'll be tested as part of the corresponding subscript or var. This saves a duplicate diagnostic. rdar://problem/50541589
1 parent 868156c commit 3b4bb19

File tree

5 files changed

+77
-32
lines changed

5 files changed

+77
-32
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,12 +2436,17 @@ NOTE(construct_raw_representable_from_unwrapped_value,none,
24362436
"construct %0 from unwrapped %1 value", (Type, Type))
24372437

24382438
ERROR(decl_from_implementation_only_module,none,
2439-
"cannot use %0 %1 here; %2 has been imported as implementation-only",
2440-
(DescriptiveDeclKind, DeclName, Identifier))
2439+
"cannot use %0 %1 %select{here|"
2440+
"in an extension with public or '@usableFromInline' members|"
2441+
"in an extension with conditional conformances}2; %3 has been imported "
2442+
"as implementation-only",
2443+
(DescriptiveDeclKind, DeclName, unsigned, Identifier))
24412444
ERROR(conformance_from_implementation_only_module,none,
2442-
"cannot use conformance of %0 to %1 here; %2 has been imported as "
2443-
"implementation-only",
2444-
(Type, DeclName, Identifier))
2445+
"cannot use conformance of %0 to %1 %select{here|"
2446+
"in an extension with public or '@usableFromInline' members|"
2447+
"in an extension with conditional conformances}2; %3 has been imported "
2448+
"as implementation-only",
2449+
(Type, DeclName, unsigned, Identifier))
24452450
ERROR(assoc_conformance_from_implementation_only_module,none,
24462451
"cannot use conformance of %0 to %1 in associated type %3 (inferred as "
24472452
"%4); %2 has been imported as implementation-only",

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ diagnoseGenericArgumentsExportability(SourceLoc loc,
249249
ASTContext &ctx = M->getASTContext();
250250
ctx.Diags.diagnose(loc, diag::conformance_from_implementation_only_module,
251251
rootConf->getType(),
252-
rootConf->getProtocol()->getFullName(), M->getName());
252+
rootConf->getProtocol()->getFullName(), 0, M->getName());
253253
hadAnyIssues = true;
254254
}
255255
return hadAnyIssues;

lib/Sema/TypeCheckAccess.cpp

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
593593
highlightOffendingType(TC, diag, complainRepr);
594594
});
595595
}
596-
596+
597597
void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) {
598598
// TODO(opaque): The constraint class/protocols on the opaque interface, as
599599
// well as the naming decl for the opaque type, need to be accessible.
@@ -1597,18 +1597,30 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
15971597
});
15981598
}
15991599

1600+
// This enum must be kept in sync with
1601+
// diag::decl_from_implementation_only_module and
1602+
// diag::conformance_from_implementation_only_module.
1603+
enum class Reason : unsigned {
1604+
General,
1605+
ExtensionWithPublicMembers,
1606+
ExtensionWithConditionalConformances
1607+
};
1608+
16001609
class DiagnoseGenerically {
16011610
TypeChecker &TC;
16021611
const Decl *D;
1612+
Reason reason;
16031613
public:
1604-
DiagnoseGenerically(TypeChecker &TC, const Decl *D) : TC(TC), D(D) {}
1614+
DiagnoseGenerically(TypeChecker &TC, const Decl *D, Reason reason)
1615+
: TC(TC), D(D), reason(reason) {}
16051616

16061617
void operator()(const TypeDecl *offendingType,
16071618
const TypeRepr *complainRepr) {
16081619
ModuleDecl *M = offendingType->getModuleContext();
16091620
auto diag = TC.diagnose(D, diag::decl_from_implementation_only_module,
16101621
offendingType->getDescriptiveKind(),
1611-
offendingType->getFullName(), M->getName());
1622+
offendingType->getFullName(),
1623+
static_cast<unsigned>(reason), M->getName());
16121624
highlightOffendingType(TC, diag, complainRepr);
16131625
}
16141626

@@ -1617,7 +1629,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16171629
TC.diagnose(D, diag::conformance_from_implementation_only_module,
16181630
offendingConformance->getType(),
16191631
offendingConformance->getProtocol()->getFullName(),
1620-
M->getName());
1632+
static_cast<unsigned>(reason), M->getName());
16211633
}
16221634
};
16231635

@@ -1630,14 +1642,20 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16301642
CheckExportabilityConformanceCallback>::value,
16311643
"DiagnoseGenerically has wrong call signature for conformance diags");
16321644

1633-
DiagnoseGenerically getDiagnoseCallback(const Decl *D) {
1634-
return DiagnoseGenerically(TC, D);
1645+
DiagnoseGenerically getDiagnoseCallback(const Decl *D,
1646+
Reason reason = Reason::General) {
1647+
return DiagnoseGenerically(TC, D, reason);
16351648
}
16361649

16371650
public:
16381651
explicit ExportabilityChecker(TypeChecker &TC) : TC(TC) {}
16391652

16401653
static bool shouldSkipChecking(const ValueDecl *VD) {
1654+
// Accessors are handled as part of their Var or Subscript, and we don't
1655+
// want to redo extension signature checking for them.
1656+
if (isa<AccessorDecl>(VD))
1657+
return true;
1658+
16411659
// Is this part of the module's API or ABI?
16421660
AccessScope accessScope =
16431661
VD->getFormalAccessScope(nullptr,
@@ -1671,12 +1689,6 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16711689
return;
16721690

16731691
DeclVisitor<ExportabilityChecker>::visit(D);
1674-
1675-
if (auto *extension = dyn_cast<ExtensionDecl>(D->getDeclContext())) {
1676-
checkType(extension->getExtendedTypeLoc(), extension,
1677-
getDiagnoseCallback(extension), getDiagnoseCallback(extension));
1678-
checkConstrainedExtensionRequirements(extension);
1679-
}
16801692
}
16811693

16821694
// Force all kinds to be handled at a lower level.
@@ -1716,12 +1728,12 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
17161728
void checkNamedPattern(const NamedPattern *NP,
17171729
const llvm::DenseSet<const VarDecl *> &seenVars) {
17181730
const VarDecl *theVar = NP->getDecl();
1719-
if (shouldSkipChecking(theVar))
1720-
return;
17211731
// Only check individual variables if we didn't check an enclosing
17221732
// TypedPattern.
17231733
if (seenVars.count(theVar) || theVar->isInvalid())
17241734
return;
1735+
if (shouldSkipChecking(theVar))
1736+
return;
17251737

17261738
checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar,
17271739
getDiagnoseCallback(theVar), getDiagnoseCallback(theVar));
@@ -1848,12 +1860,13 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18481860
getDiagnoseCallback(EED));
18491861
}
18501862

1851-
void checkConstrainedExtensionRequirements(ExtensionDecl *ED) {
1863+
void checkConstrainedExtensionRequirements(ExtensionDecl *ED,
1864+
Reason reason) {
18521865
if (!ED->getTrailingWhereClause())
18531866
return;
18541867
forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) {
1855-
checkType(type, typeRepr, ED, getDiagnoseCallback(ED),
1856-
getDiagnoseCallback(ED));
1868+
checkType(type, typeRepr, ED, getDiagnoseCallback(ED, reason),
1869+
getDiagnoseCallback(ED, reason));
18571870
});
18581871
}
18591872

@@ -1869,8 +1882,26 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18691882
getDiagnoseCallback(ED));
18701883
});
18711884

1872-
if (!ED->getInherited().empty())
1873-
checkConstrainedExtensionRequirements(ED);
1885+
bool hasPublicMembers = llvm::any_of(ED->getMembers(),
1886+
[](const Decl *member) -> bool {
1887+
auto *valueMember = dyn_cast<ValueDecl>(member);
1888+
if (!valueMember)
1889+
return false;
1890+
return !shouldSkipChecking(valueMember);
1891+
});
1892+
1893+
if (hasPublicMembers) {
1894+
checkType(ED->getExtendedTypeLoc(), ED,
1895+
getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers),
1896+
getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers));
1897+
}
1898+
1899+
if (hasPublicMembers || !ED->getInherited().empty()) {
1900+
Reason reason =
1901+
hasPublicMembers ? Reason::ExtensionWithPublicMembers
1902+
: Reason::ExtensionWithConditionalConformances;
1903+
checkConstrainedExtensionRequirements(ED, reason);
1904+
}
18741905
}
18751906

18761907
void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD,
@@ -1883,6 +1914,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18831914

18841915
auto diag = TC.diagnose(diagLoc, diag::decl_from_implementation_only_module,
18851916
PGD->getDescriptiveKind(), PGD->getName(),
1917+
static_cast<unsigned>(Reason::General),
18861918
M->getName());
18871919
if (refRange.isValid())
18881920
diag.highlight(refRange);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3678,7 +3678,7 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36783678
conformanceBeingChecked->getLoc(),
36793679
diag::conformance_from_implementation_only_module,
36803680
rootConformance->getType(),
3681-
rootConformance->getProtocol()->getName(), M->getName());
3681+
rootConformance->getProtocol()->getName(), 0, M->getName());
36823682
} else {
36833683
ctx.Diags.diagnose(
36843684
conformanceBeingChecked->getLoc(),

test/Sema/implementation-only-import-in-decls.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,35 @@ public var (testBadTypeTuplePartlyInferred3, testBadTypeTuplePartlyInferred4): (
8686
public var testMultipleBindings1: Int? = nil, testMultipleBindings2: BadStruct? = nil // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
8787
public var testMultipleBindings3: BadStruct? = nil, testMultipleBindings4: Int? = nil // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
8888

89-
extension BadStruct { // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
90-
public func testExtensionOfBadType() {} // FIXME: Should complain here instead of at the extension decl.
89+
extension BadStruct { // expected-error {{cannot use struct 'BadStruct' in an extension with public or '@usableFromInline' members; 'BADLibrary' has been imported as implementation-only}}
90+
public func testExtensionOfBadType() {}
91+
public var testExtensionVarBad: Int { 0 }
92+
public subscript(bad _: Int) -> Int { 0 }
9193
}
9294
extension BadStruct {
9395
func testExtensionOfOkayType() {}
96+
var testExtensionVarOkay: Int { 0 }
97+
subscript(okay _: Int) -> Int { 0 }
9498
}
9599

96-
extension Array where Element == BadStruct { // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
97-
public func testExtensionWithBadRequirement() {} // FIXME: Should complain here instead of at the extension decl.
100+
extension Array where Element == BadStruct { // expected-error {{cannot use struct 'BadStruct' in an extension with public or '@usableFromInline' members; 'BADLibrary' has been imported as implementation-only}}
101+
public func testExtensionWithBadRequirement() {}
102+
public var testExtensionVarBad: Int { 0 }
103+
public subscript(bad _: Int) -> Int { 0 }
98104
}
99105

100106
extension Array where Element == BadStruct {
101107
func testExtensionWithOkayRequirement() {} // okay
108+
var testExtensionVarOkay: Int { 0 } // okay
109+
subscript(okay _: Int) -> Int { 0 } // okay
102110
}
103111

104112
extension Int: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}}
105113
struct TestExtensionConformanceOkay {}
106114
extension TestExtensionConformanceOkay: BadProto {} // okay
107115

108116
public protocol TestConstrainedExtensionProto {}
109-
extension Array: TestConstrainedExtensionProto where Element == BadStruct { // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}}
117+
extension Array: TestConstrainedExtensionProto where Element == BadStruct { // expected-error {{cannot use struct 'BadStruct' in an extension with conditional conformances; 'BADLibrary' has been imported as implementation-only}}
110118
}
111119

112120

@@ -165,7 +173,7 @@ public class SubclassOfNormalClass: NormalClass {}
165173
public func testInheritedConformance(_: NormalProtoAssocHolder<SubclassOfNormalClass>) {} // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
166174
public func testSpecializedConformance(_: NormalProtoAssocHolder<GenericStruct<Int>>) {} // expected-error {{cannot use conformance of 'GenericStruct<T>' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
167175

168-
extension Array where Element == NormalProtoAssocHolder<NormalStruct> { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}}
176+
extension Array where Element == NormalProtoAssocHolder<NormalStruct> { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in an extension with public or '@usableFromInline' members; 'BADLibrary' has been imported as implementation-only}}
169177
public func testConstrainedExtensionUsingBadConformance() {}
170178
}
171179

0 commit comments

Comments
 (0)