diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index c019ea5d60231..fa45363a8d9f3 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -850,12 +850,11 @@ class alignas(1 << TypeAlignInBits) TypeBase /// type variables referenced by this type. void getTypeVariables(SmallPtrSetImpl &typeVariables); -private: +public: /// If the receiver is a `DependentMemberType`, returns its root. Otherwise, /// returns the receiver. Type getDependentMemberRoot(); -public: /// Determine whether this type is a type parameter, which is either a /// GenericTypeParamType or a DependentMemberType. /// diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 27675a08699d0..269280c946d70 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7099,6 +7099,20 @@ static bool isTupleWithUnresolvedPackExpansion(Type type) { return false; } +static bool isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions( + ConstraintSystem &cs, Type type) { + if (!type->is()) + return false; + + auto baseTy = cs.getFixedTypeRecursive(type->getDependentMemberRoot(), + /*wantRValue=*/true); + llvm::SmallPtrSet typeVars; + baseTy->getTypeVariables(typeVars); + return llvm::any_of(typeVars, [](const TypeVariableType *typeVar) { + return typeVar->getImpl().isPackExpansion(); + }); +} + ConstraintSystem::TypeMatchResult ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, @@ -7135,7 +7149,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // // along any unsolved path. No other returns should produce // SolutionKind::Unsolved or inspect TMF_GenerateConstraints. - auto formUnsolvedResult = [&] { + auto formUnsolvedResult = [&](bool useOriginalTypes = false) { // If we're supposed to generate constraints (i.e., this is a // newly-generated constraint), do so now. if (flags.contains(TMF_GenerateConstraints)) { @@ -7144,8 +7158,13 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // this new constraint will be solved at a later point. // Obviously, this must not happen at the top level, or the // algorithm would not terminate. - addUnsolvedConstraint(Constraint::create(*this, kind, type1, type2, - getConstraintLocator(locator))); + if (useOriginalTypes) { + addUnsolvedConstraint(Constraint::create( + *this, kind, origType1, origType2, getConstraintLocator(locator))); + } else { + addUnsolvedConstraint(Constraint::create( + *this, kind, type1, type2, getConstraintLocator(locator))); + } return getTypeMatchSuccess(); } @@ -7396,6 +7415,29 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } } + // Dependent members cannot be simplified if base type contains unresolved + // pack expansion type variables because they don't give enough information + // to substitution logic to form a correct type. For example: + // + // ``` + // protocol P { associatedtype V } + // struct S : P { typealias V = (repeat (each T)?) } + // ``` + // + // If pack expansion is represented as `$T1` and its pattern is `$T2`, a + // reference to `V` would get a type `S.V` and simplified version + // would be `Optional` instead of `Pack{repeat Optional<$T2>}` + // because `$T1` is treated as a substitution for `each T` until bound. + if (isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions( + *this, origType1) || + isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions( + *this, origType2)) { + // It's important to preserve the original types here because any attempt + // at simplification or canonicalization wouldn't produce a correct type + // util pack expansion type variables are bound. + return formUnsolvedResult(/*useOriginalTypes=*/true); + } + llvm::SmallVector conversionsOrFixes; // Decompose parallel structure. diff --git a/test/Constraints/pack-expansion-expressions.swift b/test/Constraints/pack-expansion-expressions.swift index c9b6d65fae32a..aac7b76880256 100644 --- a/test/Constraints/pack-expansion-expressions.swift +++ b/test/Constraints/pack-expansion-expressions.swift @@ -360,7 +360,7 @@ func test_pack_expansion_specialization(tuple: (Int, String, Float)) { } // rdar://107280056 - "Ambiguous without more context" with opaque return type + variadics -protocol Q { +protocol Q { associatedtype B } @@ -815,3 +815,21 @@ func testPackToScalarShortFormConstructor() { S(repeat each xs) // expected-error {{cannot pass value pack expansion to non-pack parameter of type 'Int'}} } } + + +func test_dependent_members() { + struct Variadic: Q { + typealias B = (repeat (each T)?) + + init(_: repeat each T) {} + static func f(_: repeat each T) -> Self {} + } + + func test_init(_ c1: C1, _ c2: C2) -> some Q<(C1?, C2?)> { + return Variadic(c1, c2) // Ok + } + + func test_static(_ c1: C1, _ c2: C2) -> some Q<(C1?, C2?)> { + return Variadic.f(c1, c2) // Ok + } +}