mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
AST: Checking of pack requirements
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
39
test/Generics/variadic_generic_requirements.swift
Normal file
39
test/Generics/variadic_generic_requirements.swift
Normal 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}}
|
||||||
Reference in New Issue
Block a user