Skip to content

Commit 5c27568

Browse files
committed
Check assoc types for conformances from implementation-only imports
Okay, strictly we're checking the "signature conformances" of a newly- declared conformance, but that wouldn't have fit on one line. This is making sure that we don't have a requirement on an associated type (or on the conforming type) that can only be satisfied using a conformance in an implementation-only import.
1 parent 519fa00 commit 5c27568

File tree

3 files changed

+137
-9
lines changed

3 files changed

+137
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2395,6 +2395,10 @@ ERROR(conformance_from_implementation_only_module,none,
23952395
"cannot use conformance of %0 to %1 here; %2 has been imported as "
23962396
"'@_implementationOnly'",
23972397
(Type, DeclName, Identifier))
2398+
ERROR(assoc_conformance_from_implementation_only_module,none,
2399+
"cannot use conformance of %0 to %1 in associated type %3 (inferred as "
2400+
"%4); %2 has been imported as '@_implementationOnly'",
2401+
(Type, DeclName, Identifier, Type, Type))
23982402

23992403
// Derived conformances
24002404
ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3643,23 +3643,70 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36433643
std::function<void(ProtocolConformanceRef)> writer
36443644
= Conformance->populateSignatureConformances();
36453645

3646+
SourceFile *fileForCheckingImplementationOnlyUse = nullptr;
3647+
if (getRequiredAccessScope().isPublic() || isUsableFromInlineRequired())
3648+
fileForCheckingImplementationOnlyUse = DC->getParentSourceFile();
3649+
36463650
class GatherConformancesListener : public GenericRequirementsCheckListener {
3647-
NormalProtocolConformance *conformance;
3651+
NormalProtocolConformance *conformanceBeingChecked;
3652+
SourceFile *SF;
36483653
std::function<void(ProtocolConformanceRef)> &writer;
3654+
3655+
void checkForImplementationOnlyUse(Type depTy, Type replacementTy,
3656+
const ProtocolConformance *conformance) {
3657+
if (!SF)
3658+
return;
3659+
3660+
SubstitutionMap subs =
3661+
conformance->getSubstitutions(SF->getParentModule());
3662+
for (auto &subConformance : subs.getConformances()) {
3663+
if (!subConformance.isConcrete())
3664+
continue;
3665+
checkForImplementationOnlyUse(depTy, replacementTy,
3666+
subConformance.getConcrete());
3667+
}
3668+
3669+
const RootProtocolConformance *rootConformance =
3670+
conformance->getRootConformance();
3671+
ModuleDecl *M = rootConformance->getDeclContext()->getParentModule();
3672+
if (!SF->isImportedImplementationOnly(M))
3673+
return;
3674+
3675+
ASTContext &ctx = M->getASTContext();
3676+
3677+
Type selfTy = rootConformance->getProtocol()->getProtocolSelfType();
3678+
if (depTy->isEqual(selfTy)) {
3679+
ctx.Diags.diagnose(
3680+
conformanceBeingChecked->getLoc(),
3681+
diag::conformance_from_implementation_only_module,
3682+
rootConformance->getType(),
3683+
rootConformance->getProtocol()->getName(), M->getName());
3684+
} else {
3685+
ctx.Diags.diagnose(
3686+
conformanceBeingChecked->getLoc(),
3687+
diag::assoc_conformance_from_implementation_only_module,
3688+
rootConformance->getType(),
3689+
rootConformance->getProtocol()->getName(), M->getName(),
3690+
depTy, replacementTy->getCanonicalType());
3691+
}
3692+
}
3693+
36493694
public:
36503695
GatherConformancesListener(
36513696
NormalProtocolConformance *conformance,
3652-
std::function<void(ProtocolConformanceRef)> &writer)
3653-
: conformance(conformance), writer(writer) { }
3697+
std::function<void(ProtocolConformanceRef)> &writer,
3698+
SourceFile *fileForCheckingImplementationOnlyUse)
3699+
: conformanceBeingChecked(conformance),
3700+
SF(fileForCheckingImplementationOnlyUse), writer(writer) { }
36543701

36553702
void satisfiedConformance(Type depTy, Type replacementTy,
36563703
ProtocolConformanceRef conformance) override {
36573704
// The conformance will use contextual types, but we want the
36583705
// interface type equivalent.
3659-
if (conformance.isConcrete() &&
3660-
conformance.getConcrete()->getType()->hasArchetype()) {
3706+
if (conformance.isConcrete()) {
36613707
auto concreteConformance = conformance.getConcrete();
36623708

3709+
if (conformance.getConcrete()->getType()->hasArchetype()) {
36633710
// Map the conformance.
36643711
concreteConformance = concreteConformance->subst(
36653712
[](SubstitutableType *type) -> Type {
@@ -3669,7 +3716,12 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36693716
},
36703717
MakeAbstractConformanceForGenericType());
36713718

3672-
conformance = ProtocolConformanceRef(concreteConformance);
3719+
3720+
conformance = ProtocolConformanceRef(concreteConformance);
3721+
}
3722+
3723+
checkForImplementationOnlyUse(depTy, replacementTy,
3724+
concreteConformance);
36733725
}
36743726

36753727
writer(conformance);
@@ -3679,13 +3731,13 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36793731
const Requirement &req, Type first, Type second,
36803732
ArrayRef<ParentConditionalConformance> parents) override {
36813733
// Invalidate the conformance to suppress further diagnostics.
3682-
if (conformance->getLoc().isValid()) {
3683-
conformance->setInvalid();
3734+
if (conformanceBeingChecked->getLoc().isValid()) {
3735+
conformanceBeingChecked->setInvalid();
36843736
}
36853737

36863738
return false;
36873739
}
3688-
} listener(Conformance, writer);
3740+
} listener(Conformance, writer, fileForCheckingImplementationOnlyUse);
36893741

36903742
auto result = TC.checkGenericArguments(
36913743
DC, Loc, Loc,

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,75 @@ extension ConditionalGenericStruct: NormalProto where T: NormalProto {
173173
public typealias Assoc = Int
174174
}
175175
public func testConditionalGeneric(_: NormalProtoAssocHolder<ConditionalGenericStruct<NormalStruct>>) {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
176+
177+
public protocol PublicInferredAssociatedType {
178+
associatedtype Assoc: NormalProto
179+
func takesAssoc(_: Assoc)
180+
}
181+
@usableFromInline protocol UFIInferredAssociatedType {
182+
associatedtype Assoc: NormalProto
183+
func takesAssoc(_: Assoc)
184+
}
185+
protocol InternalInferredAssociatedType {
186+
associatedtype Assoc: NormalProto
187+
func takesAssoc(_: Assoc)
188+
}
189+
190+
public struct PublicInferredAssociatedTypeImpl {
191+
public func takesAssoc(_: NormalStruct) {}
192+
}
193+
extension PublicInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
194+
extension PublicInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
195+
extension PublicInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay
196+
197+
@usableFromInline struct UFIInferredAssociatedTypeImpl {
198+
public func takesAssoc(_: NormalStruct) {}
199+
}
200+
extension UFIInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
201+
extension UFIInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
202+
extension UFIInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay
203+
204+
struct InternalInferredAssociatedTypeImpl {
205+
public func takesAssoc(_: NormalStruct) {}
206+
}
207+
extension InternalInferredAssociatedTypeImpl: PublicInferredAssociatedType {} // okay
208+
extension InternalInferredAssociatedTypeImpl: UFIInferredAssociatedType {} // okay
209+
extension InternalInferredAssociatedTypeImpl: InternalInferredAssociatedType {} // okay
210+
211+
212+
public protocol BaseProtoWithNoRequirement {
213+
associatedtype Assoc
214+
func takesAssoc(_: Assoc)
215+
}
216+
public protocol RefinedProto: BaseProtoWithNoRequirement where Assoc: NormalProto {
217+
}
218+
219+
public struct RefinedProtoImpl: RefinedProto { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
220+
public func takesAssoc(_: NormalStruct) {}
221+
}
222+
223+
public protocol RefinedSelfProto where Self: NormalProto {}
224+
extension NormalStruct: RefinedSelfProto {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
225+
226+
public protocol RefinedSelfProtoInheritance: NormalProto {}
227+
extension NormalStruct: RefinedSelfProtoInheritance {} // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as '@_implementationOnly'}}
228+
229+
230+
public protocol SlightlyMoreComplicatedRequirement {
231+
associatedtype Assoc: Collection where Assoc.Element: NormalProto
232+
func takesAssoc(_: Assoc)
233+
}
234+
public struct SlightlyMoreComplicatedRequirementImpl: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'NormalStruct'); 'BADLibrary' has been imported as '@_implementationOnly'}}
235+
public func takesAssoc(_: [NormalStruct]) {}
236+
}
237+
public struct RequirementsHandleSubclassesToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'SubclassOfNormalClass'); 'BADLibrary' has been imported as '@_implementationOnly'}}
238+
public func takesAssoc(_: [SubclassOfNormalClass]) {}
239+
}
240+
241+
public struct RequirementsHandleSpecializationsToo: SlightlyMoreComplicatedRequirement { // expected-error {{cannot use conformance of 'NormalStruct' to 'NormalProto' in associated type 'Self.Assoc.Element' (inferred as 'ConditionalGenericStruct<NormalStruct>'); 'BADLibrary' has been imported as '@_implementationOnly'}}
242+
public func takesAssoc(_: [ConditionalGenericStruct<NormalStruct>]) {}
243+
}
244+
245+
public struct ClassConstrainedGenericArg<T: NormalClass>: PublicInferredAssociatedType { // expected-error {{cannot use conformance of 'NormalClass' to 'NormalProto' in associated type 'Self.Assoc' (inferred as 'T'); 'BADLibrary' has been imported as '@_implementationOnly'}}
246+
public func takesAssoc(_: T) {}
247+
}

0 commit comments

Comments
 (0)