Skip to content

Commit b6b51cf

Browse files
committed
AST: Checking of pack requirements
1 parent d27231a commit b6b51cf

File tree

7 files changed

+146
-28
lines changed

7 files changed

+146
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2272,12 +2272,14 @@ NOTE(candidate_types_equal_requirement,none,
22722272
(Type, Type, Type, Type))
22732273
NOTE(candidate_types_same_shape_requirement,none,
22742274
"candidate requires that the type packs %0 and %1 have the same shape "
2275-
"(requirement specified as %2.shape == %3.shape)",
2275+
"(same-shape requirement inferred between %2 and %3)",
22762276
(Type, Type, Type, Type))
22772277
NOTE(candidate_types_inheritance_requirement,none,
22782278
"candidate requires that %1 inherit from %2 "
22792279
"(requirement specified as %2 : %3)",
22802280
(Type, Type, Type, Type))
2281+
NOTE(same_shape_requirement,none,
2282+
"same-shape requirement inferred between %0 and %1%2", (Type, Type, StringRef))
22812283
NOTE(types_not_equal_requirement,none,
22822284
"requirement specified as %0 == %1%2", (Type, Type, StringRef))
22832285
ERROR(type_is_not_a_class,none,

include/swift/AST/Requirement.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ enum class CheckRequirementResult : uint8_t {
3636
/// conditional requirements which must be checked.
3737
ConditionalConformance,
3838

39+
/// The subject type is a pack type; the sub-requirements are the
40+
/// element-wise requirements which must be checked.
41+
PackRequirement,
42+
3943
/// The requirement cannot ever be satisfied.
4044
RequirementFailure,
4145

lib/AST/GenericSignature.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ bool GenericSignatureImpl::isRequirementSatisfied(
414414
// FIXME: Need to check conditional requirements here.
415415
return true;
416416

417+
case CheckRequirementResult::PackRequirement:
418+
// FIXME
419+
assert(false && "Refactor this");
420+
return true;
421+
417422
case CheckRequirementResult::RequirementFailure:
418423
case CheckRequirementResult::SubstitutionFailure:
419424
return false;

lib/AST/Requirement.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,36 @@ CheckRequirementResult Requirement::checkRequirement(
8282
if (hasError())
8383
return CheckRequirementResult::SubstitutionFailure;
8484

85+
auto firstType = getFirstType();
86+
87+
auto expandPackRequirement = [&](PackType *packType) {
88+
for (auto eltType : packType->getElementTypes()) {
89+
// FIXME: Doesn't seem right
90+
if (auto *expansionType = eltType->getAs<PackExpansionType>())
91+
eltType = expansionType->getPatternType();
92+
93+
auto kind = getKind();
94+
if (kind == RequirementKind::Layout) {
95+
subReqs.emplace_back(kind, eltType,
96+
getLayoutConstraint());
97+
} else {
98+
subReqs.emplace_back(kind, eltType,
99+
getSecondType());
100+
}
101+
}
102+
return CheckRequirementResult::PackRequirement;
103+
};
104+
85105
switch (getKind()) {
86106
case RequirementKind::Conformance: {
107+
if (auto packType = firstType->getAs<PackType>()) {
108+
return expandPackRequirement(packType);
109+
}
110+
87111
auto *proto = getProtocolDecl();
88112
auto *module = proto->getParentModule();
89113
auto conformance = module->lookupConformance(
90-
getFirstType(), proto, allowMissing);
114+
firstType, proto, allowMissing);
91115
if (!conformance)
92116
return CheckRequirementResult::RequirementFailure;
93117

@@ -99,7 +123,11 @@ CheckRequirementResult Requirement::checkRequirement(
99123
}
100124

101125
case RequirementKind::Layout: {
102-
if (auto *archetypeType = getFirstType()->getAs<ArchetypeType>()) {
126+
if (auto packType = firstType->getAs<PackType>()) {
127+
return expandPackRequirement(packType);
128+
}
129+
130+
if (auto *archetypeType = firstType->getAs<ArchetypeType>()) {
103131
auto layout = archetypeType->getLayoutConstraint();
104132
if (layout && layout.merge(getLayoutConstraint()))
105133
return CheckRequirementResult::Success;
@@ -108,7 +136,7 @@ CheckRequirementResult Requirement::checkRequirement(
108136
}
109137

110138
if (getLayoutConstraint()->isClass()) {
111-
if (getFirstType()->satisfiesClassConstraint())
139+
if (firstType->satisfiesClassConstraint())
112140
return CheckRequirementResult::Success;
113141

114142
return CheckRequirementResult::RequirementFailure;
@@ -120,19 +148,23 @@ CheckRequirementResult Requirement::checkRequirement(
120148
}
121149

122150
case RequirementKind::Superclass:
123-
if (getSecondType()->isExactSuperclassOf(getFirstType()))
151+
if (auto packType = firstType->getAs<PackType>()) {
152+
return expandPackRequirement(packType);
153+
}
154+
155+
if (getSecondType()->isExactSuperclassOf(firstType))
124156
return CheckRequirementResult::Success;
125157

126158
return CheckRequirementResult::RequirementFailure;
127159

128160
case RequirementKind::SameType:
129-
if (getFirstType()->isEqual(getSecondType()))
161+
if (firstType->isEqual(getSecondType()))
130162
return CheckRequirementResult::Success;
131163

132164
return CheckRequirementResult::RequirementFailure;
133165

134166
case RequirementKind::SameShape:
135-
if (getFirstType()->getReducedShape() ==
167+
if (firstType->getReducedShape() ==
136168
getSecondType()->getReducedShape())
137169
return CheckRequirementResult::Success;
138170

lib/IDE/IDETypeChecking.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,11 @@ struct SynthesizedExtensionAnalyzer::Implementation {
354354
// FIXME: Need to handle conditional requirements here!
355355
break;
356356

357+
case CheckRequirementResult::PackRequirement:
358+
// FIXME
359+
assert(false && "Refactor this");
360+
return true;
361+
357362
case CheckRequirementResult::SubstitutionFailure:
358363
return true;
359364

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -789,27 +789,45 @@ static std::string gatherGenericParamBindingsText(
789789
return "";
790790

791791
SmallString<128> result;
792+
llvm::raw_svector_ostream OS(result);
793+
792794
for (auto gp : genericParams) {
793795
auto canonGP = gp->getCanonicalType()->castTo<GenericTypeParamType>();
794796
if (!knownGenericParams.count(canonGP))
795797
continue;
796798

797799
if (result.empty())
798-
result += " [with ";
800+
OS << " [with ";
799801
else
800-
result += ", ";
801-
result += gp->getName().str();
802-
result += " = ";
802+
OS << "; ";
803+
804+
if (gp->isParameterPack())
805+
OS << "each ";
806+
807+
OS << gp->getName().str();
808+
OS << " = ";
803809

804810
auto type = substitutions(canonGP);
805811
if (!type)
806812
return "";
807813

808-
result += type.getString();
814+
if (auto *packType = type->getAs<PackType>()) {
815+
bool first = true;
816+
for (auto eltType : packType->getElementTypes()) {
817+
if (first)
818+
first = false;
819+
else
820+
OS << ", ";
821+
822+
OS << eltType;
823+
}
824+
} else {
825+
OS << type.getString();
826+
}
809827
}
810828

811-
result += "]";
812-
return result.str().str();
829+
OS << "]";
830+
return std::string(result.str());
813831
}
814832

815833
void TypeChecker::diagnoseRequirementFailure(
@@ -828,7 +846,9 @@ void TypeChecker::diagnoseRequirementFailure(
828846
const auto reqKind = req.getKind();
829847
switch (reqKind) {
830848
case RequirementKind::SameShape:
831-
llvm_unreachable("Same-shape requirement not supported here");
849+
diagnostic = diag::types_not_same_shape;
850+
diagnosticNote = diag::same_shape_requirement;
851+
break;
832852

833853
case RequirementKind::Conformance: {
834854
diagnoseConformanceFailure(substReq.getFirstType(),
@@ -891,30 +911,31 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
891911
/// (if any).
892912
Requirement Req;
893913

914+
/// The substituted requirement.
915+
Requirement SubstReq;
916+
894917
/// The chain of conditional conformances that leads to the above
895918
/// requirement set.
896919
ParentConditionalConformances Path;
897920

898-
WorklistItem(Requirement Req, ParentConditionalConformances Path)
899-
: Req(Req), Path(Path) {}
921+
WorklistItem(Requirement Req, Requirement SubstReq,
922+
ParentConditionalConformances Path)
923+
: Req(Req), SubstReq(SubstReq), Path(Path) {}
900924
};
901925

902926
bool hadSubstFailure = false;
903927
SmallVector<WorklistItem, 4> worklist;
904928

905-
for (auto req : llvm::reverse(requirements))
906-
worklist.emplace_back(req, ParentConditionalConformances{});
929+
for (auto req : llvm::reverse(requirements)) {
930+
auto substReq = req.subst(substitutions, LookUpConformanceInModule(module));
931+
worklist.emplace_back(req, substReq, ParentConditionalConformances{});
932+
}
907933

908934
while (!worklist.empty()) {
909935
const auto item = worklist.pop_back_val();
910936

911937
auto req = item.Req;
912-
auto substReq = item.Req;
913-
if (item.Path.empty()) {
914-
// Primary requirements do not have substitutions applied.
915-
substReq =
916-
req.subst(substitutions, LookUpConformanceInModule(module));
917-
}
938+
auto substReq = item.SubstReq;
918939

919940
SmallVector<Requirement, 2> subReqs;
920941
switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true)) {
@@ -927,14 +948,23 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
927948
auto reqsPath = item.Path;
928949
reqsPath.push_back({substReq.getFirstType(), substReq.getProtocolDecl()});
929950

930-
for (auto subReq : subReqs)
931-
worklist.emplace_back(subReq, reqsPath);
951+
for (auto subReq : llvm::reverse(subReqs))
952+
worklist.emplace_back(subReq, subReq, reqsPath);
953+
break;
954+
}
955+
956+
case CheckRequirementResult::PackRequirement: {
957+
for (auto subReq : llvm::reverse(subReqs)) {
958+
// Note: we keep the original unsubstituted pack requirement here for
959+
// the diagnostic
960+
worklist.emplace_back(req, subReq, item.Path);
961+
}
932962
break;
933963
}
934964

935965
case CheckRequirementResult::RequirementFailure:
936966
return CheckGenericArgumentsResult::createRequirementFailure(
937-
req, substReq, std::move(item.Path));
967+
req, substReq, item.Path);
938968

939969
case CheckRequirementResult::SubstitutionFailure:
940970
hadSubstFailure = true;
@@ -965,6 +995,7 @@ CheckGenericArgumentsResult::Kind TypeChecker::checkGenericArguments(
965995
switch (req.checkRequirement(worklist, /*allowMissing=*/true)) {
966996
case CheckRequirementResult::Success:
967997
case CheckRequirementResult::ConditionalConformance:
998+
case CheckRequirementResult::PackRequirement:
968999
break;
9691000

9701001
case CheckRequirementResult::RequirementFailure:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature VariadicGenerics
2+
3+
// REQUIRES: asserts
4+
5+
struct Conformance<each T: Equatable> {}
6+
7+
_ = Conformance<Int, String>.self // ok
8+
_ = Conformance<AnyObject, Character>.self // expected-error {{type 'AnyObject' does not conform to protocol 'Equatable'}}
9+
10+
class Class {}
11+
class OtherClass {}
12+
class Subclass: Class {}
13+
14+
struct Superclass<each T: Class> {} // expected-note {{requirement specified as 'T' : 'Class' [with each T = OtherClass]}}
15+
16+
_ = Superclass<Class, Subclass>.self // ok
17+
_ = Superclass<OtherClass>.self // expected-error {{'Superclass' requires that 'OtherClass' inherit from 'Class'}}
18+
19+
struct Layout<each T: AnyObject> {} // expected-note {{requirement specified as 'T' : 'AnyObject' [with each T = Int, String]}}
20+
21+
_ = Layout<Class, Subclass>.self // ok
22+
_ = Layout<Int, String>.self // expected-error {{'Layout' requires that 'Int' be a class type}}
23+
24+
struct Outer<each T: Sequence> {
25+
struct Inner<each U: Sequence> where each T.Element == each U.Element {}
26+
// expected-note@-1 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array<Int>, Array<String>; each U = Set<String>, Set<Int>]}}
27+
// expected-note@-2 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array<Int>; each U = Set<Int>, Set<String>]}}
28+
29+
struct InnerShape<each U: Sequence> where (repeat (each T, each U)): Any {}
30+
// expected-note@-1 {{same-shape requirement inferred between 'T' and 'U' [with each T = Array<Int>; each U = Set<Int>, Set<String>]}}
31+
32+
}
33+
34+
_ = Outer<Array<Int>, Array<String>>.Inner<Set<Int>, Set<String>>.self // ok
35+
_ = Outer<Array<Int>, Array<String>>.Inner<Set<String>, Set<Int>>.self // expected-error {{'Outer<Array<Int>, Array<String>>.Inner' requires the types 'Pack{Int, String}' and 'Pack{String, Int}' be equivalent}}
36+
_ = Outer<Array<Int>>.Inner<Set<Int>, Set<String>>.self // expected-error {{'Outer<Array<Int>>.Inner' requires the types 'Pack{Int}' and 'Pack{Int, String}' be equivalent}}
37+
38+
_ = Outer<Array<Int>, Array<String>>.InnerShape<Set<String>, Set<Int>>.self // ok
39+
_ = Outer<Array<Int>>.InnerShape<Set<Int>, Set<String>>.self // expected-error {{'Outer<Array<Int>>.InnerShape' requires the type packs 'Pack{Array<Int>}' and 'Pack{Set<Int>, Set<String>}' have the same shape}}

0 commit comments

Comments
 (0)