diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 21146c1379a..f67e437ad23 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 419c4bace50..6cacb15874c 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 3f64b3781de..b371d3ab98d 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 33da06b49de..8c85d1eb467 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 f5d00884fd2..38d22f0d9f3 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 8bb2e18b548..017da5a8d2c 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 00000000000..52b942b8476 --- /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