Skip to content

Commit 437a084

Browse files
authored
@_implementationOnly: fix over-eager checking for vars in extensions (#24629) (#24843)
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 (cherry picked from commit 3b4bb19)
1 parent 8dcc3e0 commit 437a084

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
@@ -2427,12 +2427,17 @@ NOTE(construct_raw_representable_from_unwrapped_value,none,
24272427
"construct %0 from unwrapped %1 value", (Type, Type))
24282428

24292429
ERROR(decl_from_implementation_only_module,none,
2430-
"cannot use %0 %1 here; %2 has been imported as implementation-only",
2431-
(DescriptiveDeclKind, DeclName, Identifier))
2430+
"cannot use %0 %1 %select{here|"
2431+
"in an extension with public or '@usableFromInline' members|"
2432+
"in an extension with conditional conformances}2; %3 has been imported "
2433+
"as implementation-only",
2434+
(DescriptiveDeclKind, DeclName, unsigned, Identifier))
24322435
ERROR(conformance_from_implementation_only_module,none,
2433-
"cannot use conformance of %0 to %1 here; %2 has been imported as "
2434-
"implementation-only",
2435-
(Type, DeclName, Identifier))
2436+
"cannot use conformance of %0 to %1 %select{here|"
2437+
"in an extension with public or '@usableFromInline' members|"
2438+
"in an extension with conditional conformances}2; %3 has been imported "
2439+
"as implementation-only",
2440+
(Type, DeclName, unsigned, Identifier))
24362441
ERROR(assoc_conformance_from_implementation_only_module,none,
24372442
"cannot use conformance of %0 to %1 in associated type %3 (inferred as "
24382443
"%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
@@ -568,7 +568,7 @@ class AccessControlChecker : public AccessControlCheckerBase,
568568
highlightOffendingType(TC, diag, complainRepr);
569569
});
570570
}
571-
571+
572572
void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) {
573573
// TODO(opaque): The constraint class/protocols on the opaque interface, as
574574
// well as the naming decl for the opaque type, need to be accessible.
@@ -1559,18 +1559,30 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
15591559
});
15601560
}
15611561

1562+
// This enum must be kept in sync with
1563+
// diag::decl_from_implementation_only_module and
1564+
// diag::conformance_from_implementation_only_module.
1565+
enum class Reason : unsigned {
1566+
General,
1567+
ExtensionWithPublicMembers,
1568+
ExtensionWithConditionalConformances
1569+
};
1570+
15621571
class DiagnoseGenerically {
15631572
TypeChecker &TC;
15641573
const Decl *D;
1574+
Reason reason;
15651575
public:
1566-
DiagnoseGenerically(TypeChecker &TC, const Decl *D) : TC(TC), D(D) {}
1576+
DiagnoseGenerically(TypeChecker &TC, const Decl *D, Reason reason)
1577+
: TC(TC), D(D), reason(reason) {}
15671578

15681579
void operator()(const TypeDecl *offendingType,
15691580
const TypeRepr *complainRepr) {
15701581
ModuleDecl *M = offendingType->getModuleContext();
15711582
auto diag = TC.diagnose(D, diag::decl_from_implementation_only_module,
15721583
offendingType->getDescriptiveKind(),
1573-
offendingType->getFullName(), M->getName());
1584+
offendingType->getFullName(),
1585+
static_cast<unsigned>(reason), M->getName());
15741586
highlightOffendingType(TC, diag, complainRepr);
15751587
}
15761588

@@ -1579,7 +1591,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
15791591
TC.diagnose(D, diag::conformance_from_implementation_only_module,
15801592
offendingConformance->getType(),
15811593
offendingConformance->getProtocol()->getFullName(),
1582-
M->getName());
1594+
static_cast<unsigned>(reason), M->getName());
15831595
}
15841596
};
15851597

@@ -1592,14 +1604,20 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
15921604
CheckExportabilityConformanceCallback>::value,
15931605
"DiagnoseGenerically has wrong call signature for conformance diags");
15941606

1595-
DiagnoseGenerically getDiagnoseCallback(const Decl *D) {
1596-
return DiagnoseGenerically(TC, D);
1607+
DiagnoseGenerically getDiagnoseCallback(const Decl *D,
1608+
Reason reason = Reason::General) {
1609+
return DiagnoseGenerically(TC, D, reason);
15971610
}
15981611

15991612
public:
16001613
explicit ExportabilityChecker(TypeChecker &TC) : TC(TC) {}
16011614

16021615
static bool shouldSkipChecking(const ValueDecl *VD) {
1616+
// Accessors are handled as part of their Var or Subscript, and we don't
1617+
// want to redo extension signature checking for them.
1618+
if (isa<AccessorDecl>(VD))
1619+
return true;
1620+
16031621
// Is this part of the module's API or ABI?
16041622
AccessScope accessScope =
16051623
VD->getFormalAccessScope(nullptr,
@@ -1633,12 +1651,6 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16331651
return;
16341652

16351653
DeclVisitor<ExportabilityChecker>::visit(D);
1636-
1637-
if (auto *extension = dyn_cast<ExtensionDecl>(D->getDeclContext())) {
1638-
checkType(extension->getExtendedTypeLoc(), extension,
1639-
getDiagnoseCallback(extension), getDiagnoseCallback(extension));
1640-
checkConstrainedExtensionRequirements(extension);
1641-
}
16421654
}
16431655

16441656
// Force all kinds to be handled at a lower level.
@@ -1678,12 +1690,12 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16781690
void checkNamedPattern(const NamedPattern *NP,
16791691
const llvm::DenseSet<const VarDecl *> &seenVars) {
16801692
const VarDecl *theVar = NP->getDecl();
1681-
if (shouldSkipChecking(theVar))
1682-
return;
16831693
// Only check individual variables if we didn't check an enclosing
16841694
// TypedPattern.
16851695
if (seenVars.count(theVar) || theVar->isInvalid())
16861696
return;
1697+
if (shouldSkipChecking(theVar))
1698+
return;
16871699

16881700
checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar,
16891701
getDiagnoseCallback(theVar), getDiagnoseCallback(theVar));
@@ -1809,12 +1821,13 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18091821
getDiagnoseCallback(EED));
18101822
}
18111823

1812-
void checkConstrainedExtensionRequirements(ExtensionDecl *ED) {
1824+
void checkConstrainedExtensionRequirements(ExtensionDecl *ED,
1825+
Reason reason) {
18131826
if (!ED->getTrailingWhereClause())
18141827
return;
18151828
forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) {
1816-
checkType(type, typeRepr, ED, getDiagnoseCallback(ED),
1817-
getDiagnoseCallback(ED));
1829+
checkType(type, typeRepr, ED, getDiagnoseCallback(ED, reason),
1830+
getDiagnoseCallback(ED, reason));
18181831
});
18191832
}
18201833

@@ -1830,8 +1843,26 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18301843
getDiagnoseCallback(ED));
18311844
});
18321845

1833-
if (!ED->getInherited().empty())
1834-
checkConstrainedExtensionRequirements(ED);
1846+
bool hasPublicMembers = llvm::any_of(ED->getMembers(),
1847+
[](const Decl *member) -> bool {
1848+
auto *valueMember = dyn_cast<ValueDecl>(member);
1849+
if (!valueMember)
1850+
return false;
1851+
return !shouldSkipChecking(valueMember);
1852+
});
1853+
1854+
if (hasPublicMembers) {
1855+
checkType(ED->getExtendedTypeLoc(), ED,
1856+
getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers),
1857+
getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers));
1858+
}
1859+
1860+
if (hasPublicMembers || !ED->getInherited().empty()) {
1861+
Reason reason =
1862+
hasPublicMembers ? Reason::ExtensionWithPublicMembers
1863+
: Reason::ExtensionWithConditionalConformances;
1864+
checkConstrainedExtensionRequirements(ED, reason);
1865+
}
18351866
}
18361867

18371868
void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD,
@@ -1844,6 +1875,7 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
18441875

18451876
auto diag = TC.diagnose(diagLoc, diag::decl_from_implementation_only_module,
18461877
PGD->getDescriptiveKind(), PGD->getName(),
1878+
static_cast<unsigned>(Reason::General),
18471879
M->getName());
18481880
if (refRange.isValid())
18491881
diag.highlight(refRange);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3684,7 +3684,7 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36843684
conformanceBeingChecked->getLoc(),
36853685
diag::conformance_from_implementation_only_module,
36863686
rootConformance->getType(),
3687-
rootConformance->getProtocol()->getName(), M->getName());
3687+
rootConformance->getProtocol()->getName(), 0, M->getName());
36883688
} else {
36893689
ctx.Diags.diagnose(
36903690
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)