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))
NOTE(candidate_types_same_shape_requirement,none,
"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))
NOTE(candidate_types_inheritance_requirement,none,
"candidate requires that %1 inherit from %2 "
"(requirement specified as %2 : %3)",
(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,
"requirement specified as %0 == %1%2", (Type, Type, StringRef))
ERROR(type_is_not_a_class,none,

View File

@@ -36,6 +36,10 @@ enum class CheckRequirementResult : uint8_t {
/// conditional requirements which must be checked.
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.
RequirementFailure,

View File

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

View File

@@ -82,12 +82,36 @@ CheckRequirementResult Requirement::checkRequirement(
if (hasError())
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()) {
case RequirementKind::Conformance: {
if (auto packType = firstType->getAs<PackType>()) {
return expandPackRequirement(packType);
}
auto *proto = getProtocolDecl();
auto *module = proto->getParentModule();
auto conformance = module->lookupConformance(
getFirstType(), proto, allowMissing);
firstType, proto, allowMissing);
if (!conformance)
return CheckRequirementResult::RequirementFailure;
@@ -99,7 +123,11 @@ CheckRequirementResult Requirement::checkRequirement(
}
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();
if (layout && layout.merge(getLayoutConstraint()))
return CheckRequirementResult::Success;
@@ -108,7 +136,7 @@ CheckRequirementResult Requirement::checkRequirement(
}
if (getLayoutConstraint()->isClass()) {
if (getFirstType()->satisfiesClassConstraint())
if (firstType->satisfiesClassConstraint())
return CheckRequirementResult::Success;
return CheckRequirementResult::RequirementFailure;
@@ -120,19 +148,23 @@ CheckRequirementResult Requirement::checkRequirement(
}
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::RequirementFailure;
case RequirementKind::SameType:
if (getFirstType()->isEqual(getSecondType()))
if (firstType->isEqual(getSecondType()))
return CheckRequirementResult::Success;
return CheckRequirementResult::RequirementFailure;
case RequirementKind::SameShape:
if (getFirstType()->getReducedShape() ==
if (firstType->getReducedShape() ==
getSecondType()->getReducedShape())
return CheckRequirementResult::Success;

View File

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

View File

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