From d27231a91cb8030d0eaffdafc5909a8ba7af2fae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 16 Mar 2023 21:33:49 -0400 Subject: [PATCH 1/6] AST: Generalize Requirement::isSatisfied() to Requirement::checkRequirement() --- include/swift/AST/Requirement.h | 27 ++++++-- lib/AST/GenericSignature.cpp | 15 ++++- lib/AST/Requirement.cpp | 50 +++++++++++---- lib/IDE/IDETypeChecking.cpp | 17 +++-- lib/Sema/TypeCheckGeneric.cpp | 109 ++++++++++++++++---------------- 5 files changed, 139 insertions(+), 79 deletions(-) diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index 7053f8c55f52f..419c4bace5028 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -27,6 +27,24 @@ namespace swift { +/// Return type of Requirement::checkRequirement(). +enum class CheckRequirementResult : uint8_t { + /// The requirement was fully satisfied. + Success, + + /// The subject type conforms conditionally; the sub-requirements are + /// conditional requirements which must be checked. + ConditionalConformance, + + /// The requirement cannot ever be satisfied. + RequirementFailure, + + /// Some other requirement is expected to fail, or there was an invalid + /// conformance and an error should be diagnosed elsewhere, so this + /// requirement does not need to be diagnosed. + SubstitutionFailure +}; + /// A single requirement placed on the type parameters (or associated /// types thereof) of a class Requirement { @@ -155,11 +173,12 @@ class Requirement { /// Determines if this substituted requirement is satisfied. /// - /// \param conditionalRequirements An out parameter initialized to an - /// array of requirements that the caller must check to ensure this + /// \param subReqs An out parameter initialized to a list of simpler + /// requirements which the caller must check to ensure this /// requirement is completely satisfied. - bool isSatisfied(ArrayRef &conditionalRequirements, - bool allowMissing = false) const; + CheckRequirementResult checkRequirement( + SmallVectorImpl &subReqs, + bool allowMissing = false) const; /// Determines if this substituted requirement can ever be satisfied, /// possibly with additional substitutions. diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index 1347b0a12dfd1..3f64b3781de0c 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -405,10 +405,19 @@ bool GenericSignatureImpl::isRequirementSatisfied( LookUpConformanceInSignature(this)); } - // FIXME: Need to check conditional requirements here. - ArrayRef conditionalRequirements; + SmallVector subReqs; + switch (requirement.checkRequirement(subReqs, allowMissing)) { + case CheckRequirementResult::Success: + return true; + + case CheckRequirementResult::ConditionalConformance: + // FIXME: Need to check conditional requirements here. + return true; - return requirement.isSatisfied(conditionalRequirements, allowMissing); + case CheckRequirementResult::RequirementFailure: + case CheckRequirementResult::SubstitutionFailure: + return false; + } } SmallVector diff --git a/lib/AST/Requirement.cpp b/lib/AST/Requirement.cpp index d9e5f89753e7f..33da06b49decc 100644 --- a/lib/AST/Requirement.cpp +++ b/lib/AST/Requirement.cpp @@ -76,9 +76,12 @@ ProtocolDecl *Requirement::getProtocolDecl() const { return getSecondType()->castTo()->getDecl(); } -bool -Requirement::isSatisfied(ArrayRef &conditionalRequirements, - bool allowMissing) const { +CheckRequirementResult Requirement::checkRequirement( + SmallVectorImpl &subReqs, + bool allowMissing) const { + if (hasError()) + return CheckRequirementResult::SubstitutionFailure; + switch (getKind()) { case RequirementKind::Conformance: { auto *proto = getProtocolDecl(); @@ -86,35 +89,54 @@ Requirement::isSatisfied(ArrayRef &conditionalRequirements, auto conformance = module->lookupConformance( getFirstType(), proto, allowMissing); if (!conformance) - return false; + return CheckRequirementResult::RequirementFailure; - conditionalRequirements = conformance.getConditionalRequirements(); - return true; + auto condReqs = conformance.getConditionalRequirements(); + if (condReqs.empty()) + return CheckRequirementResult::Success; + subReqs.append(condReqs.begin(), condReqs.end()); + return CheckRequirementResult::ConditionalConformance; } case RequirementKind::Layout: { if (auto *archetypeType = getFirstType()->getAs()) { auto layout = archetypeType->getLayoutConstraint(); - return (layout && layout.merge(getLayoutConstraint())); + if (layout && layout.merge(getLayoutConstraint())) + return CheckRequirementResult::Success; + + return CheckRequirementResult::RequirementFailure; } - if (getLayoutConstraint()->isClass()) - return getFirstType()->satisfiesClassConstraint(); + if (getLayoutConstraint()->isClass()) { + if (getFirstType()->satisfiesClassConstraint()) + return CheckRequirementResult::Success; + + return CheckRequirementResult::RequirementFailure; + } // TODO: Statically check other layout constraints, once they can // be spelled in Swift. - return true; + return CheckRequirementResult::Success; } case RequirementKind::Superclass: - return getSecondType()->isExactSuperclassOf(getFirstType()); + if (getSecondType()->isExactSuperclassOf(getFirstType())) + return CheckRequirementResult::Success; + + return CheckRequirementResult::RequirementFailure; case RequirementKind::SameType: - return getFirstType()->isEqual(getSecondType()); + if (getFirstType()->isEqual(getSecondType())) + return CheckRequirementResult::Success; + + return CheckRequirementResult::RequirementFailure; case RequirementKind::SameShape: - return (getFirstType()->getReducedShape() == - getSecondType()->getReducedShape()); + if (getFirstType()->getReducedShape() == + getSecondType()->getReducedShape()) + return CheckRequirementResult::Success; + + return CheckRequirementResult::RequirementFailure; } llvm_unreachable("Bad requirement kind"); diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index 5eea4c23d1eb1..f5d00884fd26b 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -344,16 +344,25 @@ struct SynthesizedExtensionAnalyzer::Implementation { return type; }, LookUpConformanceInModule(M)); - if (SubstReq.hasError()) + + SmallVector subReqs; + switch (SubstReq.checkRequirement(subReqs)) { + case CheckRequirementResult::Success: + break; + + case CheckRequirementResult::ConditionalConformance: + // FIXME: Need to handle conditional requirements here! + break; + + case CheckRequirementResult::SubstitutionFailure: return true; - // FIXME: Need to handle conditional requirements here! - ArrayRef conditionalRequirements; - if (!SubstReq.isSatisfied(conditionalRequirements)) { + case CheckRequirementResult::RequirementFailure: if (!SubstReq.canBeSatisfied()) return true; MergeInfo.addRequirement(Req); + break; } } return false; diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index b1502153b27c1..8bb2e18b5482b 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -886,66 +886,64 @@ CheckGenericArgumentsResult TypeChecker::checkGenericArgumentsForDiagnostics( SmallVector; struct WorklistItem { - /// The set of requirements to check. These are either the primary set - /// of requirements, or the conditional requirements of the last conformance - /// in \c ReqsPath (if any). - ArrayRef Requirements; + /// The requirement to check. This is either a top-level requirement or + /// a conditional requirements of the last conformancein \c ReqsPath + /// (if any). + Requirement Req; /// The chain of conditional conformances that leads to the above /// requirement set. - ParentConditionalConformances ReqsPath; + ParentConditionalConformances Path; - WorklistItem(ArrayRef Requirements, - ParentConditionalConformances ReqsPath) - : Requirements(Requirements), ReqsPath(ReqsPath) {} + WorklistItem(Requirement Req, ParentConditionalConformances Path) + : Req(Req), Path(Path) {} }; bool hadSubstFailure = false; SmallVector worklist; - worklist.emplace_back(requirements, ParentConditionalConformances{}); + for (auto req : llvm::reverse(requirements)) + worklist.emplace_back(req, ParentConditionalConformances{}); + while (!worklist.empty()) { const auto item = worklist.pop_back_val(); - const bool isPrimaryReq = item.ReqsPath.empty(); - for (const auto &req : item.Requirements) { - Requirement substReq = req; - if (isPrimaryReq) { - // Primary requirements do not have substitutions applied. - auto resolved = - req.subst(substitutions, LookUpConformanceInModule(module)); - if (!resolved.hasError()) { - substReq = resolved; - } else { - // Another requirement might fail later; just continue. - hadSubstFailure = true; - continue; - } - } - - ArrayRef conditionalRequirements; - if (!substReq.isSatisfied(conditionalRequirements, - /*allowMissing=*/true)) { - return CheckGenericArgumentsResult::createRequirementFailure( - req, substReq, std::move(item.ReqsPath)); - } + 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)); + } - if (conditionalRequirements.empty()) { - continue; - } + SmallVector subReqs; + switch (substReq.checkRequirement(subReqs, /*allowMissing=*/true)) { + case CheckRequirementResult::Success: + break; - assert(req.getKind() == RequirementKind::Conformance); + case CheckRequirementResult::ConditionalConformance: { + assert(substReq.getKind() == RequirementKind::Conformance); - auto reqsPath = item.ReqsPath; + auto reqsPath = item.Path; reqsPath.push_back({substReq.getFirstType(), substReq.getProtocolDecl()}); - worklist.emplace_back(conditionalRequirements, std::move(reqsPath)); + for (auto subReq : subReqs) + worklist.emplace_back(subReq, reqsPath); + break; + } + + case CheckRequirementResult::RequirementFailure: + return CheckGenericArgumentsResult::createRequirementFailure( + req, substReq, std::move(item.Path)); + + case CheckRequirementResult::SubstitutionFailure: + hadSubstFailure = true; + break; } } - if (hadSubstFailure) { + if (hadSubstFailure) return CheckGenericArgumentsResult::createSubstitutionFailure(); - } return CheckGenericArgumentsResult::createSuccess(); } @@ -954,31 +952,34 @@ CheckGenericArgumentsResult::Kind TypeChecker::checkGenericArguments( ModuleDecl *module, ArrayRef requirements, TypeSubstitutionFn substitutions, SubstOptions options) { SmallVector worklist; - bool valid = true; + + bool hadSubstFailure = false; for (auto req : requirements) { - auto resolved = req.subst(substitutions, - LookUpConformanceInModule(module), options); - if (!resolved.hasError()) { - worklist.push_back(resolved); - } else { - valid = false; - } + worklist.push_back(req.subst(substitutions, + LookUpConformanceInModule(module), options)); } while (!worklist.empty()) { auto req = worklist.pop_back_val(); - ArrayRef conditionalRequirements; - if (!req.isSatisfied(conditionalRequirements, /*allowMissing=*/true)) + switch (req.checkRequirement(worklist, /*allowMissing=*/true)) { + case CheckRequirementResult::Success: + case CheckRequirementResult::ConditionalConformance: + break; + + case CheckRequirementResult::RequirementFailure: return CheckGenericArgumentsResult::RequirementFailure; - worklist.append(conditionalRequirements.begin(), - conditionalRequirements.end()); + case CheckRequirementResult::SubstitutionFailure: + hadSubstFailure = true; + break; + } } - if (valid) - return CheckGenericArgumentsResult::Success; - return CheckGenericArgumentsResult::SubstitutionFailure; + if (hadSubstFailure) + return CheckGenericArgumentsResult::SubstitutionFailure; + + return CheckGenericArgumentsResult::Success; } Requirement From b6b51cf4cfbb15a45d0458540fad8ec388ee1dc2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 17 Mar 2023 17:48:35 -0400 Subject: [PATCH 2/6] AST: Checking of pack requirements --- include/swift/AST/DiagnosticsSema.def | 4 +- include/swift/AST/Requirement.h | 4 + lib/AST/GenericSignature.cpp | 5 ++ lib/AST/Requirement.cpp | 44 +++++++++-- lib/IDE/IDETypeChecking.cpp | 5 ++ lib/Sema/TypeCheckGeneric.cpp | 73 +++++++++++++------ .../variadic_generic_requirements.swift | 39 ++++++++++ 7 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 test/Generics/variadic_generic_requirements.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 21146c1379aac..f67e437ad2383 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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, diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index 419c4bace5028..6cacb15874cdf 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -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, diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index 3f64b3781de0c..b371d3ab98d99 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -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; diff --git a/lib/AST/Requirement.cpp b/lib/AST/Requirement.cpp index 33da06b49decc..8c85d1eb467a9 100644 --- a/lib/AST/Requirement.cpp +++ b/lib/AST/Requirement.cpp @@ -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()) + 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()) { + 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()) { + if (auto packType = firstType->getAs()) { + return expandPackRequirement(packType); + } + + if (auto *archetypeType = firstType->getAs()) { 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()) { + 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; diff --git a/lib/IDE/IDETypeChecking.cpp b/lib/IDE/IDETypeChecking.cpp index f5d00884fd26b..38d22f0d9f383 100644 --- a/lib/IDE/IDETypeChecking.cpp +++ b/lib/IDE/IDETypeChecking.cpp @@ -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; diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 8bb2e18b5482b..017da5a8d2ce1 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -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(); 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()) { + 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 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 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: diff --git a/test/Generics/variadic_generic_requirements.swift b/test/Generics/variadic_generic_requirements.swift new file mode 100644 index 0000000000000..52b942b8476ec --- /dev/null +++ b/test/Generics/variadic_generic_requirements.swift @@ -0,0 +1,39 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature VariadicGenerics + +// REQUIRES: asserts + +struct Conformance {} + +_ = Conformance.self // ok +_ = Conformance.self // expected-error {{type 'AnyObject' does not conform to protocol 'Equatable'}} + +class Class {} +class OtherClass {} +class Subclass: Class {} + +struct Superclass {} // expected-note {{requirement specified as 'T' : 'Class' [with each T = OtherClass]}} + +_ = Superclass.self // ok +_ = Superclass.self // expected-error {{'Superclass' requires that 'OtherClass' inherit from 'Class'}} + +struct Layout {} // expected-note {{requirement specified as 'T' : 'AnyObject' [with each T = Int, String]}} + +_ = Layout.self // ok +_ = Layout.self // expected-error {{'Layout' requires that 'Int' be a class type}} + +struct Outer { + struct Inner where each T.Element == each U.Element {} + // expected-note@-1 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array, Array; each U = Set, Set]}} + // expected-note@-2 {{requirement specified as 'T.Element' == 'U.Element' [with each T = Array; each U = Set, Set]}} + + struct InnerShape where (repeat (each T, each U)): Any {} + // expected-note@-1 {{same-shape requirement inferred between 'T' and 'U' [with each T = Array; each U = Set, Set]}} + +} + +_ = Outer, Array>.Inner, Set>.self // ok +_ = Outer, Array>.Inner, Set>.self // expected-error {{'Outer, Array>.Inner' requires the types 'Pack{Int, String}' and 'Pack{String, Int}' be equivalent}} +_ = Outer>.Inner, Set>.self // expected-error {{'Outer>.Inner' requires the types 'Pack{Int}' and 'Pack{Int, String}' be equivalent}} + +_ = Outer, Array>.InnerShape, Set>.self // ok +_ = Outer>.InnerShape, Set>.self // expected-error {{'Outer>.InnerShape' requires the type packs 'Pack{Array}' and 'Pack{Set, Set}' have the same shape}} \ No newline at end of file From d17baed985954467a894a1856f019811fa891ea1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 17 Mar 2023 21:34:17 -0400 Subject: [PATCH 3/6] Runtime: Check superclass pack requirements in checkGenericPackRequirement() --- stdlib/public/runtime/ProtocolConformance.cpp | 46 ++++++++++++++----- test/Interpreter/variadic_generic_types.swift | 13 +++++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 7d194ef25fbe5..dd2773c65e573 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1275,6 +1275,20 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } +static bool isSubclassOrExistential(const Metadata *subclass, + const Metadata *superclass) { + // If the type which is constrained to a base class is an existential + // type, and if that existential type includes a superclass constraint, + // just require that the superclass by which the existential is + // constrained is a subclass of the base class. + if (auto *existential = dyn_cast(subclass)) { + if (auto *superclassConstraint = existential->getSuperclassConstraint()) + subclass = superclassConstraint; + } + + return isSubclass(subclass, superclass); +} + SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI bool swift::_swift_class_isSubclass(const Metadata *subclass, @@ -1367,16 +1381,7 @@ checkGenericRequirement(const GenericRequirementDescriptor &req, return *result.getError(); auto baseType = result.getType().getMetadata(); - // If the type which is constrained to a base class is an existential - // type, and if that existential type includes a superclass constraint, - // just require that the superclass by which the existential is - // constrained is a subclass of the base class. - if (auto *existential = dyn_cast(subjectType)) { - if (auto *superclassConstraint = existential->getSuperclassConstraint()) - subjectType = superclassConstraint; - } - - if (!isSubclass(subjectType, baseType)) + if (!isSubclassOrExistential(subjectType, baseType)) return TYPE_LOOKUP_ERROR_FMT( "%.*s is not subclass of %.*s", (int)req.getParam().size(), req.getParam().data(), (int)req.getMangledTypeName().size(), @@ -1464,7 +1469,26 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, } case GenericRequirementKind::BaseClass: { - llvm_unreachable("Implement me"); + // Demangle the base type under the given substitutions. + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto baseType = result.getType().getMetadata(); + + // Check that each pack element inherits from the base class. + for (unsigned i = 0, e = subjectType.getNumElements(); i < e; ++i) { + const Metadata *elt = subjectType.getElements()[i]; + + if (!isSubclassOrExistential(elt, baseType)) + return TYPE_LOOKUP_ERROR_FMT( + "%.*s is not subclass of %.*s", (int)req.getParam().size(), + req.getParam().data(), (int)req.getMangledTypeName().size(), + req.getMangledTypeName().data()); + } + + return llvm::None; } case GenericRequirementKind::SameConformance: { diff --git a/test/Interpreter/variadic_generic_types.swift b/test/Interpreter/variadic_generic_types.swift index f5b2807937e8f..70f4910f14940 100644 --- a/test/Interpreter/variadic_generic_types.swift +++ b/test/Interpreter/variadic_generic_types.swift @@ -71,7 +71,18 @@ types.test("ConformanceReq") { expectEqual("main.ConformanceReq", _typeName(ConformanceReq.self)) } -// FIXME: Test superclass, layout and same-type pack requirements once more stuff is plumbed through +public class Base {} +public class Derived: Base {} + +public struct SuperclassReq {} + +types.test("SuperclassReq") { + expectEqual("main.SuperclassReq", _typeName(SuperclassReq< >.self)) + expectEqual("main.SuperclassReq", _typeName(SuperclassReq.self)) + expectEqual("main.SuperclassReq", _typeName(SuperclassReq.self)) +} + +// FIXME: Test layout and same-type pack requirements once more stuff is plumbed through // // Stored property layout tests From a21b0d9749481e9672e1f161c66b6ecebbf38e54 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 17 Mar 2023 21:48:42 -0400 Subject: [PATCH 4/6] Runtime: Check layout pack requirements in checkGenericPackRequirement() --- stdlib/public/runtime/ProtocolConformance.cpp | 38 ++++++++++++------- test/Interpreter/variadic_generic_types.swift | 10 ++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index dd2773c65e573..fe97643200705 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1289,6 +1289,23 @@ static bool isSubclassOrExistential(const Metadata *subclass, return isSubclass(subclass, superclass); } +static llvm::Optional +satisfiesLayoutConstraint(const GenericRequirementDescriptor &req, + const Metadata *subjectType) { + switch (req.getLayout()) { + case GenericRequirementLayoutKind::Class: + if (!subjectType->satisfiesClassConstraint()) { + return TYPE_LOOKUP_ERROR_FMT( + "subject type %.*s does not satisfy class constraint", + (int)req.getParam().size(), req.getParam().data()); + } + return llvm::None; + } + + // Unknown layout. + return TYPE_LOOKUP_ERROR_FMT("unknown layout kind %u", req.getLayout()); +} + SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI bool swift::_swift_class_isSubclass(const Metadata *subclass, @@ -1358,18 +1375,7 @@ checkGenericRequirement(const GenericRequirementDescriptor &req, } case GenericRequirementKind::Layout: { - switch (req.getLayout()) { - case GenericRequirementLayoutKind::Class: - if (!subjectType->satisfiesClassConstraint()) { - return TYPE_LOOKUP_ERROR_FMT( - "subject type %.*s does not satisfy class constraint", - (int)req.getParam().size(), req.getParam().data()); - } - return llvm::None; - } - - // Unknown layout. - return TYPE_LOOKUP_ERROR_FMT("unknown layout kind %u", req.getLayout()); + return satisfiesLayoutConstraint(req, subjectType); } case GenericRequirementKind::BaseClass: { @@ -1465,7 +1471,13 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, } case GenericRequirementKind::Layout: { - llvm_unreachable("Implement me"); + for (unsigned i = 0, e = subjectType.getNumElements(); i < e; ++i) { + const Metadata *elt = subjectType.getElements()[i]; + if (auto result = satisfiesLayoutConstraint(req, elt)) + return result; + } + + return llvm::None; } case GenericRequirementKind::BaseClass: { diff --git a/test/Interpreter/variadic_generic_types.swift b/test/Interpreter/variadic_generic_types.swift index 70f4910f14940..614a054aa5df0 100644 --- a/test/Interpreter/variadic_generic_types.swift +++ b/test/Interpreter/variadic_generic_types.swift @@ -82,7 +82,15 @@ types.test("SuperclassReq") { expectEqual("main.SuperclassReq", _typeName(SuperclassReq.self)) } -// FIXME: Test layout and same-type pack requirements once more stuff is plumbed through +public struct LayoutReq {} + +types.test("LayoutReq") { + expectEqual("main.LayoutReq", _typeName(LayoutReq< >.self)) + expectEqual("main.LayoutReq", _typeName(LayoutReq.self)) + expectEqual("main.LayoutReq", _typeName(LayoutReq.self)) +} + +// FIXME: Test same-type pack requirements once more stuff is plumbed through // // Stored property layout tests From f12be70da5255513a7dd69724d5e66e3ad4960b7 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 17 Mar 2023 22:04:00 -0400 Subject: [PATCH 5/6] Runtime: Demangling DependentMemberType whose base type is a pack type --- stdlib/public/runtime/MetadataLookup.cpp | 63 ++++++++++++++---------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 921fe47d10bef..426b3a412a8b8 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -1958,37 +1958,50 @@ class DecodedMetadataBuilder { return BuiltType(); #endif - // FIXME: variadic-generics - auto swiftProtocol = protocol.getSwiftProtocol(); - auto witnessTable = swift_conformsToProtocol(base.getMetadata(), swiftProtocol); - if (!witnessTable) - return BuiltType(); // Look for the named associated type within the protocol. auto assocType = findAssociatedTypeByName(swiftProtocol, name); if (!assocType) return BuiltType(); - // Call the associated type access function. -#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES - auto tbl = reinterpret_cast( - const_cast(witnessTable)); - return BuiltType( - swift_getAssociatedTypeWitnessRelative( - MetadataState::Abstract, - tbl, - base.getMetadata(), - swiftProtocol->getRequirementBaseDescriptor(), - *assocType)); -#else - return BuiltType( - swift_getAssociatedTypeWitness( - MetadataState::Abstract, - const_cast(witnessTable), - base.getMetadata(), - swiftProtocol->getRequirementBaseDescriptor(), - *assocType)); -#endif + auto projectDependentMemberType = [&](const Metadata *baseMetadata) -> const Metadata * { + auto witnessTable = swift_conformsToProtocol(baseMetadata, swiftProtocol); + if (!witnessTable) + return nullptr; + + // Call the associated type access function. + #if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES + auto tbl = reinterpret_cast( + const_cast(witnessTable)); + return swift_getAssociatedTypeWitnessRelative( + MetadataState::Abstract, + tbl, + baseMetadata, + swiftProtocol->getRequirementBaseDescriptor(), + *assocType).Value; + #else + return swift_getAssociatedTypeWitness( + MetadataState::Abstract, + const_cast(witnessTable), + baseMetadata, + swiftProtocol->getRequirementBaseDescriptor(), + *assocType).Value; + #endif + }; + + if (base.isMetadata()) { + return BuiltType(projectDependentMemberType(base.getMetadata())); + } else { + MetadataPackPointer basePack = base.getMetadataPack(); + + llvm::SmallVector packElts; + for (size_t i = 0, e = basePack.getNumElements(); i < e; ++i) { + auto *projectedElt = projectDependentMemberType(basePack.getElements()[i]); + packElts.push_back(projectedElt); + } + + return BuiltType(swift_allocateMetadataPack(packElts.data(), packElts.size())); + } } #define REF_STORAGE(Name, ...) \ From c50e8bb5dee7963446075cbac767509741f093af Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 17 Mar 2023 22:09:57 -0400 Subject: [PATCH 6/6] Runtime: Check same-type pack requirements in checkGenericPackRequirement() --- stdlib/public/runtime/ProtocolConformance.cpp | 46 +++++++++++++++---- test/Interpreter/variadic_generic_types.swift | 11 ++++- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index fe97643200705..72e185d380745 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1438,7 +1438,7 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, llvm::SmallVector witnessTables; // Look up the conformance of each pack element to the protocol. - for (unsigned i = 0, e = subjectType.getNumElements(); i < e; ++i) { + for (size_t i = 0, e = subjectType.getNumElements(); i < e; ++i) { const Metadata *elt = subjectType.getElements()[i]; const WitnessTable *witnessTable = nullptr; @@ -1447,8 +1447,8 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, const char *protoName = req.getProtocol() ? req.getProtocol().getName() : ""; return TYPE_LOOKUP_ERROR_FMT( - "subject type %.*s does not conform to protocol %s", - (int)req.getParam().size(), req.getParam().data(), protoName); + "subject type %.*s does not conform to protocol %s at pack index %lu", + (int)req.getParam().size(), req.getParam().data(), protoName, i); } if (req.getProtocol().needsWitnessTable()) @@ -1467,11 +1467,40 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, } case GenericRequirementKind::SameType: { - llvm_unreachable("Implement me"); + // Resolve the constraint generic parameter. + auto result = swift::getTypePackByMangledName( + req.getMangledTypeName(), extraArguments.data(), + substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + MetadataPackPointer constraintType = result.getType(); + assert(constraintType.getLifetime() == PackLifetime::OnHeap); + + if (subjectType.getNumElements() != constraintType.getNumElements()) { + return TYPE_LOOKUP_ERROR_FMT( + "mismatched pack lengths in same-type pack requirement %.*s: %lu vs %lu", + (int)req.getParam().size(), req.getParam().data(), + subjectType.getNumElements(), constraintType.getNumElements()); + } + + for (size_t i = 0, e = subjectType.getNumElements(); i < e; ++i) { + auto *subjectElt = subjectType.getElements()[i]; + auto *constraintElt = constraintType.getElements()[i]; + + if (subjectElt != constraintElt) { + return TYPE_LOOKUP_ERROR_FMT( + "subject type %.*s does not match %.*s at pack index %lu", + (int)req.getParam().size(), + req.getParam().data(), (int)req.getMangledTypeName().size(), + req.getMangledTypeName().data(), i); + } + } + + return llvm::None; } case GenericRequirementKind::Layout: { - for (unsigned i = 0, e = subjectType.getNumElements(); i < e; ++i) { + for (size_t i = 0, e = subjectType.getNumElements(); i < e; ++i) { const Metadata *elt = subjectType.getElements()[i]; if (auto result = satisfiesLayoutConstraint(req, elt)) return result; @@ -1490,14 +1519,15 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, auto baseType = result.getType().getMetadata(); // Check that each pack element inherits from the base class. - for (unsigned i = 0, e = subjectType.getNumElements(); i < e; ++i) { + for (size_t i = 0, e = subjectType.getNumElements(); i < e; ++i) { const Metadata *elt = subjectType.getElements()[i]; if (!isSubclassOrExistential(elt, baseType)) return TYPE_LOOKUP_ERROR_FMT( - "%.*s is not subclass of %.*s", (int)req.getParam().size(), + "%.*s is not subclass of %.*s at pack index %lu", + (int)req.getParam().size(), req.getParam().data(), (int)req.getMangledTypeName().size(), - req.getMangledTypeName().data()); + req.getMangledTypeName().data(), i); } return llvm::None; diff --git a/test/Interpreter/variadic_generic_types.swift b/test/Interpreter/variadic_generic_types.swift index 614a054aa5df0..b16e7abe441bb 100644 --- a/test/Interpreter/variadic_generic_types.swift +++ b/test/Interpreter/variadic_generic_types.swift @@ -90,7 +90,16 @@ types.test("LayoutReq") { expectEqual("main.LayoutReq", _typeName(LayoutReq.self)) } -// FIXME: Test same-type pack requirements once more stuff is plumbed through +public struct OuterSeq { + public struct InnerSeq where each T.Element == each U.Element {} +} + +types.test("SameTypeReq") { + expectEqual("main.OuterSeq.InnerSeq", _typeName(OuterSeq< >.InnerSeq< >.self)) + expectEqual("main.OuterSeq}>.InnerSeq}>", _typeName(OuterSeq>.InnerSeq>.self)) + expectEqual("main.OuterSeq, Swift.Set}>.InnerSeq, Swift.Array}>", _typeName(OuterSeq, Set>.InnerSeq, Array>.self)) +} + // // Stored property layout tests