AST: Checking of pack requirements

This commit is contained in:
Slava Pestov
2023-03-17 17:48:35 -04:00
parent d27231a91c
commit b6b51cf4cf
7 changed files with 146 additions and 28 deletions

View File

@@ -2272,12 +2272,14 @@ NOTE(candidate_types_equal_requirement,none,
(Type, Type, Type, Type)) (Type, Type, Type, Type))
NOTE(candidate_types_same_shape_requirement,none, NOTE(candidate_types_same_shape_requirement,none,
"candidate requires that the type packs %0 and %1 have the same shape " "candidate requires that the type packs %0 and %1 have the same shape "
"(requirement specified as %2.shape == %3.shape)", "(same-shape requirement inferred between %2 and %3)",
(Type, Type, Type, Type)) (Type, Type, Type, Type))
NOTE(candidate_types_inheritance_requirement,none, NOTE(candidate_types_inheritance_requirement,none,
"candidate requires that %1 inherit from %2 " "candidate requires that %1 inherit from %2 "
"(requirement specified as %2 : %3)", "(requirement specified as %2 : %3)",
(Type, Type, Type, Type)) (Type, Type, Type, Type))
NOTE(same_shape_requirement,none,
"same-shape requirement inferred between %0 and %1%2", (Type, Type, StringRef))
NOTE(types_not_equal_requirement,none, NOTE(types_not_equal_requirement,none,
"requirement specified as %0 == %1%2", (Type, Type, StringRef)) "requirement specified as %0 == %1%2", (Type, Type, StringRef))
ERROR(type_is_not_a_class,none, ERROR(type_is_not_a_class,none,

View File

@@ -36,6 +36,10 @@ enum class CheckRequirementResult : uint8_t {
/// conditional requirements which must be checked. /// conditional requirements which must be checked.
ConditionalConformance, ConditionalConformance,
/// The subject type is a pack type; the sub-requirements are the
/// element-wise requirements which must be checked.
PackRequirement,
/// The requirement cannot ever be satisfied. /// The requirement cannot ever be satisfied.
RequirementFailure, RequirementFailure,

View File

@@ -414,6 +414,11 @@ bool GenericSignatureImpl::isRequirementSatisfied(
// FIXME: Need to check conditional requirements here. // FIXME: Need to check conditional requirements here.
return true; return true;
case CheckRequirementResult::PackRequirement:
// FIXME
assert(false && "Refactor this");
return true;
case CheckRequirementResult::RequirementFailure: case CheckRequirementResult::RequirementFailure:
case CheckRequirementResult::SubstitutionFailure: case CheckRequirementResult::SubstitutionFailure:
return false; return false;

View File

@@ -82,12 +82,36 @@ CheckRequirementResult Requirement::checkRequirement(
if (hasError()) if (hasError())
return CheckRequirementResult::SubstitutionFailure; return CheckRequirementResult::SubstitutionFailure;
auto firstType = getFirstType();
auto expandPackRequirement = [&](PackType *packType) {
for (auto eltType : packType->getElementTypes()) {
// FIXME: Doesn't seem right
if (auto *expansionType = eltType->getAs<PackExpansionType>())
eltType = expansionType->getPatternType();
auto kind = getKind();
if (kind == RequirementKind::Layout) {
subReqs.emplace_back(kind, eltType,
getLayoutConstraint());
} else {
subReqs.emplace_back(kind, eltType,
getSecondType());
}
}
return CheckRequirementResult::PackRequirement;
};
switch (getKind()) { switch (getKind()) {
case RequirementKind::Conformance: { case RequirementKind::Conformance: {
if (auto packType = firstType->getAs<PackType>()) {
return expandPackRequirement(packType);
}
auto *proto = getProtocolDecl(); auto *proto = getProtocolDecl();
auto *module = proto->getParentModule(); auto *module = proto->getParentModule();
auto conformance = module->lookupConformance( auto conformance = module->lookupConformance(
getFirstType(), proto, allowMissing); firstType, proto, allowMissing);
if (!conformance) if (!conformance)
return CheckRequirementResult::RequirementFailure; return CheckRequirementResult::RequirementFailure;
@@ -99,7 +123,11 @@ CheckRequirementResult Requirement::checkRequirement(
} }
case RequirementKind::Layout: { case RequirementKind::Layout: {
if (auto *archetypeType = getFirstType()->getAs<ArchetypeType>()) { if (auto packType = firstType->getAs<PackType>()) {
return expandPackRequirement(packType);
}
if (auto *archetypeType = firstType->getAs<ArchetypeType>()) {
auto layout = archetypeType->getLayoutConstraint(); auto layout = archetypeType->getLayoutConstraint();
if (layout && layout.merge(getLayoutConstraint())) if (layout && layout.merge(getLayoutConstraint()))
return CheckRequirementResult::Success; return CheckRequirementResult::Success;
@@ -108,7 +136,7 @@ CheckRequirementResult Requirement::checkRequirement(
} }
if (getLayoutConstraint()->isClass()) { if (getLayoutConstraint()->isClass()) {
if (getFirstType()->satisfiesClassConstraint()) if (firstType->satisfiesClassConstraint())
return CheckRequirementResult::Success; return CheckRequirementResult::Success;
return CheckRequirementResult::RequirementFailure; return CheckRequirementResult::RequirementFailure;
@@ -120,19 +148,23 @@ CheckRequirementResult Requirement::checkRequirement(
} }
case RequirementKind::Superclass: case RequirementKind::Superclass:
if (getSecondType()->isExactSuperclassOf(getFirstType())) if (auto packType = firstType->getAs<PackType>()) {
return expandPackRequirement(packType);
}
if (getSecondType()->isExactSuperclassOf(firstType))
return CheckRequirementResult::Success; return CheckRequirementResult::Success;
return CheckRequirementResult::RequirementFailure; return CheckRequirementResult::RequirementFailure;
case RequirementKind::SameType: case RequirementKind::SameType:
if (getFirstType()->isEqual(getSecondType())) if (firstType->isEqual(getSecondType()))
return CheckRequirementResult::Success; return CheckRequirementResult::Success;
return CheckRequirementResult::RequirementFailure; return CheckRequirementResult::RequirementFailure;
case RequirementKind::SameShape: case RequirementKind::SameShape:
if (getFirstType()->getReducedShape() == if (firstType->getReducedShape() ==
getSecondType()->getReducedShape()) getSecondType()->getReducedShape())
return CheckRequirementResult::Success; return CheckRequirementResult::Success;

View File

@@ -354,6 +354,11 @@ struct SynthesizedExtensionAnalyzer::Implementation {
// FIXME: Need to handle conditional requirements here! // FIXME: Need to handle conditional requirements here!
break; break;
case CheckRequirementResult::PackRequirement:
// FIXME
assert(false && "Refactor this");
return true;
case CheckRequirementResult::SubstitutionFailure: case CheckRequirementResult::SubstitutionFailure:
return true; return true;

View File

@@ -789,27 +789,45 @@ static std::string gatherGenericParamBindingsText(
return ""; return "";
SmallString<128> result; SmallString<128> result;
llvm::raw_svector_ostream OS(result);
for (auto gp : genericParams) { for (auto gp : genericParams) {
auto canonGP = gp->getCanonicalType()->castTo<GenericTypeParamType>(); auto canonGP = gp->getCanonicalType()->castTo<GenericTypeParamType>();
if (!knownGenericParams.count(canonGP)) if (!knownGenericParams.count(canonGP))
continue; continue;
if (result.empty()) if (result.empty())
result += " [with "; OS << " [with ";
else else
result += ", "; OS << "; ";
result += gp->getName().str();
result += " = "; if (gp->isParameterPack())
OS << "each ";
OS << gp->getName().str();
OS << " = ";
auto type = substitutions(canonGP); auto type = substitutions(canonGP);
if (!type) if (!type)
return ""; return "";
result += type.getString(); if (auto *packType = type->getAs<PackType>()) {
bool first = true;
for (auto eltType : packType->getElementTypes()) {
if (first)
first = false;
else
OS << ", ";
OS << eltType;
}
} else {
OS << type.getString();
}
} }
result += "]"; OS << "]";
return result.str().str(); return std::string(result.str());
} }
void TypeChecker::diagnoseRequirementFailure( void TypeChecker::diagnoseRequirementFailure(
@@ -828,7 +846,9 @@ void TypeChecker::diagnoseRequirementFailure(
const auto reqKind = req.getKind(); const auto reqKind = req.getKind();
switch (reqKind) { switch (reqKind) {
case RequirementKind::SameShape: case RequirementKind::SameShape:
llvm_unreachable("Same-shape requirement not supported here"); diagnostic = diag::types_not_same_shape;
diagnosticNote = diag::same_shape_requirement;
break;
case RequirementKind::Conformance: { case RequirementKind::Conformance: {
diagnoseConformanceFailure(substReq.getFirstType(), diagnoseConformanceFailure(substReq.getFirstType(),
@@ -891,30 +911,31 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
/// (if any). /// (if any).
Requirement Req; Requirement Req;
/// The substituted requirement.
Requirement SubstReq;
/// The chain of conditional conformances that leads to the above /// The chain of conditional conformances that leads to the above
/// requirement set. /// requirement set.
ParentConditionalConformances Path; ParentConditionalConformances Path;
WorklistItem(Requirement Req, ParentConditionalConformances Path) WorklistItem(Requirement Req, Requirement SubstReq,
: Req(Req), Path(Path) {} ParentConditionalConformances Path)
: Req(Req), SubstReq(SubstReq), Path(Path) {}
}; };
bool hadSubstFailure = false; bool hadSubstFailure = false;
SmallVector<WorklistItem, 4> worklist; SmallVector<WorklistItem, 4> worklist;
for (auto req : llvm::reverse(requirements)) for (auto req : llvm::reverse(requirements)) {
worklist.emplace_back(req, ParentConditionalConformances{}); auto substReq = req.subst(substitutions, LookUpConformanceInModule(module));
worklist.emplace_back(req, substReq, ParentConditionalConformances{});
}
while (!worklist.empty()) { while (!worklist.empty()) {
const auto item = worklist.pop_back_val(); const auto item = worklist.pop_back_val();
auto req = item.Req; auto req = item.Req;
auto substReq = item.Req; auto substReq = item.SubstReq;
if (item.Path.empty()) {
// Primary requirements do not have substitutions applied.
substReq =
req.subst(substitutions, LookUpConformanceInModule(module));
}
SmallVector<Requirement, 2> subReqs; SmallVector<Requirement, 2> subReqs;
switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true)) { switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true)) {
@@ -927,14 +948,23 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics(
auto reqsPath = item.Path; auto reqsPath = item.Path;
reqsPath.push_back({substReq.getFirstType(), substReq.getProtocolDecl()}); reqsPath.push_back({substReq.getFirstType(), substReq.getProtocolDecl()});
for (auto subReq : subReqs) for (auto subReq : llvm::reverse(subReqs))
worklist.emplace_back(subReq, reqsPath); worklist.emplace_back(subReq, subReq, reqsPath);
break;
}
case CheckRequirementResult::PackRequirement: {
for (auto subReq : llvm::reverse(subReqs)) {
// Note: we keep the original unsubstituted pack requirement here for
// the diagnostic
worklist.emplace_back(req, subReq, item.Path);
}
break; break;
} }
case CheckRequirementResult::RequirementFailure: case CheckRequirementResult::RequirementFailure:
return CheckGenericArgumentsResult::createRequirementFailure( return CheckGenericArgumentsResult::createRequirementFailure(
req, substReq, std::move(item.Path)); req, substReq, item.Path);
case CheckRequirementResult::SubstitutionFailure: case CheckRequirementResult::SubstitutionFailure:
hadSubstFailure = true; hadSubstFailure = true;
@@ -965,6 +995,7 @@ CheckGenericArgumentsResult::Kind TypeChecker::checkGenericArguments(
switch (req.checkRequirement(worklist, /*allowMissing=*/true)) { switch (req.checkRequirement(worklist, /*allowMissing=*/true)) {
case CheckRequirementResult::Success: case CheckRequirementResult::Success:
case CheckRequirementResult::ConditionalConformance: case CheckRequirementResult::ConditionalConformance:
case CheckRequirementResult::PackRequirement:
break; break;
case CheckRequirementResult::RequirementFailure: case CheckRequirementResult::RequirementFailure:

View File

@@ -0,0 +1,39 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature VariadicGenerics
// REQUIRES: asserts
struct Conformance<each T: Equatable> {}
_ = Conformance<Int, String>.self // ok
_ = Conformance<AnyObject, Character>.self // expected-error {{type 'AnyObject' does not conform to protocol 'Equatable'}}
class Class {}
class OtherClass {}
class Subclass: Class {}
struct Superclass<each T: Class> {} // expected-note {{requirement specified as 'T' : 'Class' [with each T = OtherClass]}}
_ = Superclass<Class, Subclass>.self // ok
_ = Superclass<OtherClass>.self // expected-error {{'Superclass' requires that 'OtherClass' inherit from 'Class'}}
struct Layout<each T: AnyObject> {} // expected-note {{requirement specified as 'T' : 'AnyObject' [with each T = Int, String]}}
_ = Layout<Class, Subclass>.self // ok
_ = Layout<Int, String>.self // expected-error {{'Layout' requires that 'Int' be a class type}}
struct Outer<each T: Sequence> {
struct Inner<each U: Sequence> where each T.Element == each U.Element {}
// expected-note@-1 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array<Int>, Array<String>; each U = Set<String>, Set<Int>]}}
// expected-note@-2 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array<Int>; each U = Set<Int>, Set<String>]}}
struct InnerShape<each U: Sequence> where (repeat (each T, each U)): Any {}
// expected-note@-1 {{same-shape requirement inferred between 'T' and 'U' [with each T = Array<Int>; each U = Set<Int>, Set<String>]}}
}
_ = Outer<Array<Int>, Array<String>>.Inner<Set<Int>, Set<String>>.self // ok
_ = 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}}
_ = 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}}
_ = Outer<Array<Int>, Array<String>>.InnerShape<Set<String>, Set<Int>>.self // ok
_ = 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}}