Skip to content

Commit 1a72ca1

Browse files
committed
Sema: Enforce coherence condition on type witnesses of tuple conformance
We want that (repeat each Element).[P]A == (repeat (each Element).[P]A), where on the left is type witness projection from the tuple conformance, and on the right is a tuple with a pack expansion.
1 parent 1ec6c35 commit 1a72ca1

File tree

6 files changed

+216
-36
lines changed

6 files changed

+216
-36
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,10 +2733,23 @@ NOTE(ambiguous_witnesses_wrong_name,none,
27332733
NOTE(no_witnesses_type,none,
27342734
"protocol requires nested type %0; add nested type %0 for conformance",
27352735
(const AssociatedTypeDecl *))
2736-
NOTE(default_associated_type_req_fail,none,
2736+
NOTE(no_witnesses_type_tuple,none,
2737+
"protocol requires nested type %0; add type alias %0 with underlying type %1 "
2738+
"for conformance",
2739+
(const AssociatedTypeDecl *, Type))
2740+
NOTE(default_associated_type_unsatisfied_conformance,none,
2741+
"default type %0 for associated type %1 (from protocol %2) "
2742+
"does not conform to %3",
2743+
(Type, const AssociatedTypeDecl *, Type, Type))
2744+
NOTE(default_associated_type_unsatisfied_superclass,none,
2745+
"default type %0 for associated type %1 (from protocol %2) "
2746+
"does not inherit from %3",
2747+
(Type, const AssociatedTypeDecl *, Type, Type))
2748+
NOTE(default_associated_type_tuple,none,
27372749
"default type %0 for associated type %1 (from protocol %2) "
2738-
"does not %select{inherit from|conform to}4 %3",
2739-
(Type, const AssociatedTypeDecl *, Type, Type, bool))
2750+
"is unsuitable for tuple conformance; the associated type requirement "
2751+
"must be fulfilled by a type alias with underlying type %3",
2752+
(Type, const AssociatedTypeDecl *, Type, Type))
27402753
ERROR(associated_type_access,none,
27412754
"associated type in "
27422755
"%select{a private|a fileprivate|an internal|a package|a public|%error}0 protocol "
@@ -2764,10 +2777,19 @@ WARNING(associated_type_not_usable_from_inline_warn,none,
27642777
NOTE(bad_associated_type_deduction,none,
27652778
"unable to infer associated type %0 for protocol %1",
27662779
(const AssociatedTypeDecl *, const ProtocolDecl *))
2767-
NOTE(associated_type_deduction_witness_failed,none,
2780+
NOTE(associated_type_deduction_unsatisfied_conformance,none,
2781+
"candidate would match and infer %0 = %1 if %1 "
2782+
"conformed to %2",
2783+
(const AssociatedTypeDecl *, Type, Type))
2784+
NOTE(associated_type_deduction_unsatisfied_superclass,none,
27682785
"candidate would match and infer %0 = %1 if %1 "
2769-
"%select{inherited from|conformed to}3 %2",
2770-
(const AssociatedTypeDecl *, Type, Type, bool))
2786+
"inherited from %2",
2787+
(const AssociatedTypeDecl *, Type, Type))
2788+
NOTE(associated_type_deduction_tuple,none,
2789+
"cannot infer %0 = %1 in tuple conformance because "
2790+
"the associated type requirement must be fulfilled by a type alias with "
2791+
"underlying type %2",
2792+
(const AssociatedTypeDecl *, Type, Type))
27712793
NOTE(associated_type_witness_conform_impossible,none,
27722794
"candidate can not infer %0 = %1 because %1 "
27732795
"is not a nominal type and so can't conform to %2",
@@ -2858,9 +2880,17 @@ NOTE(protocol_witness_enum_case_payload, none,
28582880

28592881
NOTE(protocol_witness_type,none,
28602882
"possibly intended match", ())
2861-
NOTE(protocol_witness_nonconform_type,none,
2883+
NOTE(protocol_type_witness_unsatisfied_conformance,none,
28622884
"possibly intended match %0 does not "
2863-
"%select{inherit from|conform to}2 %1", (Type, Type, bool))
2885+
"conform to %1", (Type, Type))
2886+
NOTE(protocol_type_witness_unsatisfied_superclass,none,
2887+
"possibly intended match %0 does not "
2888+
"inherit from %1", (Type, Type))
2889+
NOTE(protocol_type_witness_tuple,none,
2890+
"possibly intended match %0 is unsuitable for tuple conformance; "
2891+
"the associated type requirement must be fulfilled by a type alias "
2892+
"with underlying type %1",
2893+
(Type, Type))
28642894

28652895
NOTE(protocol_witness_circularity,none,
28662896
"candidate references itself", ())

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,6 +3543,18 @@ static bool isNSObjectProtocol(ProtocolDecl *proto) {
35433543
return proto->hasClangNode();
35443544
}
35453545

3546+
static Type getTupleConformanceTypeWitness(DeclContext *dc,
3547+
AssociatedTypeDecl *assocType) {
3548+
auto genericSig = dc->getGenericSignatureOfContext();
3549+
assert(genericSig.getGenericParams().size() == 1);
3550+
3551+
auto paramTy = genericSig.getGenericParams()[0];
3552+
auto elementTy = DependentMemberType::get(paramTy, assocType);
3553+
auto expansionTy = PackExpansionType::get(elementTy, paramTy);
3554+
3555+
return TupleType::get(TupleTypeElt(expansionTy), dc->getASTContext());
3556+
}
3557+
35463558
bool swift::
35473559
printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
35483560
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) {
@@ -3583,7 +3595,15 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
35833595
Printer << "public ";
35843596

35853597
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(Requirement)) {
3586-
Printer << "typealias " << MissingTypeWitness->getName() << " = <#type#>";
3598+
Printer << "typealias " << MissingTypeWitness->getName() << " = ";
3599+
3600+
if (isa<BuiltinTupleDecl>(Adopter->getSelfNominalTypeDecl())) {
3601+
auto expectedTy = getTupleConformanceTypeWitness(Adopter, MissingTypeWitness);
3602+
Printer << expectedTy.getString();
3603+
} else {
3604+
Printer << "<#type#>";
3605+
}
3606+
35873607
Printer << "\n";
35883608
} else {
35893609
if (isa<ConstructorDecl>(Requirement)) {
@@ -3894,17 +3914,25 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
38943914

38953915
// Issue diagnostics for witness types.
38963916
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(VD)) {
3917+
llvm::Optional<InFlightDiagnostic> diag;
3918+
if (isa<BuiltinTupleDecl>(DC->getSelfNominalTypeDecl())) {
3919+
auto expectedTy = getTupleConformanceTypeWitness(DC, MissingTypeWitness);
3920+
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type_tuple,
3921+
MissingTypeWitness, expectedTy));
3922+
} else {
3923+
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
3924+
MissingTypeWitness));
3925+
}
38973926
if (SameFile) {
38983927
// If the protocol member decl is in the same file of the stub,
38993928
// we can directly associate the fixit with the note issued to the
39003929
// requirement.
3901-
Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
3902-
MissingTypeWitness).fixItInsertAfter(FixitLocation, FixIt);
3930+
diag->fixItInsertAfter(FixitLocation, FixIt);
39033931
} else {
3932+
diag.value().flush();
3933+
39043934
// Otherwise, we have to issue another note to carry the fixit,
39053935
// because editor may assume the fixit is in the same file with the note.
3906-
Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
3907-
MissingTypeWitness);
39083936
if (EditorMode) {
39093937
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general)
39103938
.fixItInsertAfter(FixitLocation, FixIt);
@@ -4687,8 +4715,19 @@ swift::checkTypeWitness(Type type, AssociatedTypeDecl *assocType,
46874715
}
46884716

46894717
if (sig->requiresClass(depTy) &&
4690-
!contextType->satisfiesClassConstraint())
4691-
return CheckTypeWitnessResult::forLayout(module->getASTContext().getAnyObjectType());
4718+
!contextType->satisfiesClassConstraint()) {
4719+
return CheckTypeWitnessResult::forLayout(
4720+
module->getASTContext().getAnyObjectType());
4721+
}
4722+
4723+
// Tuple conformances can only witness associated types by projecting them
4724+
// element-wise.
4725+
if (isa<BuiltinTupleDecl>(dc->getSelfNominalTypeDecl())) {
4726+
auto expectedTy = getTupleConformanceTypeWitness(dc, assocType);
4727+
if (!expectedTy->isEqual(type)) {
4728+
return CheckTypeWitnessResult::forTuple(expectedTy);
4729+
}
4730+
}
46924731

46934732
// Success!
46944733
return CheckTypeWitnessResult::forSuccess();
@@ -4881,12 +4920,36 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
48814920
candidate.second.getKind() == CheckTypeWitnessResult::Error)
48824921
continue;
48834922

4884-
diags.diagnose(
4885-
candidate.first,
4886-
diag::protocol_witness_nonconform_type,
4887-
candidate.first->getDeclaredInterfaceType(),
4888-
candidate.second.getRequirement(),
4889-
candidate.second.getKind() != CheckTypeWitnessResult::Superclass);
4923+
switch (candidate.second.getKind()) {
4924+
case CheckTypeWitnessResult::Success:
4925+
case CheckTypeWitnessResult::Error:
4926+
llvm_unreachable("Should not end up here");
4927+
4928+
case CheckTypeWitnessResult::Conformance:
4929+
case CheckTypeWitnessResult::Layout:
4930+
diags.diagnose(
4931+
candidate.first,
4932+
diag::protocol_type_witness_unsatisfied_conformance,
4933+
candidate.first->getDeclaredInterfaceType(),
4934+
candidate.second.getRequirement());
4935+
break;
4936+
4937+
case CheckTypeWitnessResult::Superclass:
4938+
diags.diagnose(
4939+
candidate.first,
4940+
diag::protocol_type_witness_unsatisfied_superclass,
4941+
candidate.first->getDeclaredInterfaceType(),
4942+
candidate.second.getRequirement());
4943+
break;
4944+
4945+
case CheckTypeWitnessResult::Tuple:
4946+
diags.diagnose(
4947+
candidate.first,
4948+
diag::protocol_type_witness_tuple,
4949+
candidate.first->getDeclaredInterfaceType(),
4950+
candidate.second.getRequirement());
4951+
break;
4952+
}
48904953
}
48914954
});
48924955

lib/Sema/TypeCheckProtocol.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ class CheckTypeWitnessResult {
8989

9090
/// Type witness does not satisfy a layout requirement on
9191
/// the associated type.
92-
Layout
92+
Layout,
93+
94+
/// Type witness of a tuple conformance does not have the form
95+
/// (repeat (each Element).A).
96+
Tuple
9397
} kind;
9498

9599
private:
@@ -123,6 +127,10 @@ class CheckTypeWitnessResult {
123127
return CheckTypeWitnessResult(Layout, reqt);
124128
}
125129

130+
static CheckTypeWitnessResult forTuple(Type reqt) {
131+
return CheckTypeWitnessResult(Tuple, reqt);
132+
}
133+
126134
Kind getKind() const { return kind; }
127135
Type getRequirement() const { return reqt; }
128136

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,13 +1936,43 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
19361936
failedDefaultedResult](NormalProtocolConformance *conformance) {
19371937
auto proto = conformance->getProtocol();
19381938
auto &diags = proto->getASTContext().Diags;
1939-
diags.diagnose(failedDefaultedAssocType,
1940-
diag::default_associated_type_req_fail,
1941-
failedDefaultedWitness,
1942-
failedDefaultedAssocType,
1943-
proto->getDeclaredInterfaceType(),
1944-
failedDefaultedResult.getRequirement(),
1945-
failedDefaultedResult.getKind() != CheckTypeWitnessResult::Superclass);
1939+
1940+
switch (failedDefaultedResult.getKind()) {
1941+
case CheckTypeWitnessResult::Success:
1942+
case CheckTypeWitnessResult::Error:
1943+
llvm_unreachable("Should not end up here");
1944+
1945+
case CheckTypeWitnessResult::Conformance:
1946+
case CheckTypeWitnessResult::Layout:
1947+
diags.diagnose(
1948+
failedDefaultedAssocType,
1949+
diag::default_associated_type_unsatisfied_conformance,
1950+
failedDefaultedWitness,
1951+
failedDefaultedAssocType,
1952+
proto->getDeclaredInterfaceType(),
1953+
failedDefaultedResult.getRequirement());
1954+
break;
1955+
1956+
case CheckTypeWitnessResult::Superclass:
1957+
diags.diagnose(
1958+
failedDefaultedAssocType,
1959+
diag::default_associated_type_unsatisfied_superclass,
1960+
failedDefaultedWitness,
1961+
failedDefaultedAssocType,
1962+
proto->getDeclaredInterfaceType(),
1963+
failedDefaultedResult.getRequirement());
1964+
break;
1965+
1966+
case CheckTypeWitnessResult::Tuple:
1967+
diags.diagnose(
1968+
failedDefaultedAssocType,
1969+
diag::default_associated_type_tuple,
1970+
failedDefaultedWitness,
1971+
failedDefaultedAssocType,
1972+
proto->getDeclaredInterfaceType(),
1973+
failedDefaultedResult.getRequirement());
1974+
break;
1975+
}
19461976
});
19471977

19481978
return true;
@@ -2026,11 +2056,36 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
20262056
continue;
20272057
}
20282058

2029-
diags.diagnose(failed.Witness,
2030-
diag::associated_type_deduction_witness_failed,
2031-
assocType, failed.TypeWitness,
2032-
failed.Result.getRequirement(),
2033-
failed.Result.getKind() != CheckTypeWitnessResult::Superclass);
2059+
switch (failed.Result.getKind()) {
2060+
case CheckTypeWitnessResult::Success:
2061+
case CheckTypeWitnessResult::Error:
2062+
llvm_unreachable("Should not end up here");
2063+
2064+
case CheckTypeWitnessResult::Conformance:
2065+
case CheckTypeWitnessResult::Layout:
2066+
diags.diagnose(
2067+
failed.Witness,
2068+
diag::associated_type_deduction_unsatisfied_conformance,
2069+
assocType, failed.TypeWitness,
2070+
failed.Result.getRequirement());
2071+
break;
2072+
2073+
case CheckTypeWitnessResult::Superclass:
2074+
diags.diagnose(
2075+
failed.Witness,
2076+
diag::associated_type_deduction_unsatisfied_superclass,
2077+
assocType, failed.TypeWitness,
2078+
failed.Result.getRequirement());
2079+
break;
2080+
2081+
case CheckTypeWitnessResult::Tuple:
2082+
diags.diagnose(
2083+
failed.Witness,
2084+
diag::associated_type_deduction_tuple,
2085+
assocType, failed.TypeWitness,
2086+
failed.Result.getRequirement());
2087+
break;
2088+
}
20342089
}
20352090
});
20362091

test/Generics/tuple-conformances.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ protocol P {
6868

6969
extension Tuple: P where repeat each Element: P {
7070
typealias A = (repeat (each Element).A)
71-
typealias B = Float
71+
typealias B = (repeat (each Element).B)
7272
func f() {}
7373
}
7474

@@ -85,7 +85,7 @@ func same<T>(_: T, _: T) {}
8585

8686
func useConformance() {
8787
same(returnsPA((1, 2, 3)), (Int, Int, Int).self)
88-
same(returnsPB((1, 2, 3)), Float.self)
88+
same(returnsPB((1, 2, 3)), (String, String, String).self)
8989

9090
(1, 2, 3).f()
9191
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TupleConformances
2+
3+
typealias Tuple<each T> = (repeat each T)
4+
5+
protocol P1 {
6+
associatedtype A // expected-note {{protocol requires nested type 'A'; add type alias 'A' with underlying type '(repeat (each T).A)' for conformance}}
7+
}
8+
9+
extension Tuple: P1 where repeat each T: P1 {} // expected-error {{type '(repeat each T)' does not conform to protocol 'P1'}}
10+
11+
protocol P2 {
12+
associatedtype A = Int // expected-note {{default type 'Int' for associated type 'A' (from protocol 'P2') is unsuitable for tuple conformance; the associated type requirement must be fulfilled by a type alias with underlying type '(repeat (each T).A)'}}
13+
}
14+
15+
extension Tuple: P2 where repeat each T: P2 {} // expected-error {{type '(repeat each T)' does not conform to protocol 'P2'}}
16+
17+
protocol P3 {
18+
associatedtype A // expected-note {{unable to infer associated type 'A' for protocol 'P3'}}
19+
func f() -> A
20+
}
21+
22+
extension Tuple: P3 where repeat each T: P3 { // expected-error {{type '(repeat each T)' does not conform to protocol 'P3'}}
23+
func f() -> Int {} // expected-note {{cannot infer 'A' = 'Int' in tuple conformance because the associated type requirement must be fulfilled by a type alias with underlying type '(repeat (each T).A)'}}
24+
}

0 commit comments

Comments
 (0)