From 691905eda12d8d7cd91aaf87869e2072bcb3f28c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Dec 2018 13:31:15 -0800 Subject: [PATCH 1/2] [Type checker] Allow extensions of typealiases naming generic specializations. When a (non-generic) typealias refers to a specialization of a generic type, e.g. ```swift typealias simd_float3 = SIMD3 ``` treat an extension of the typealias as an extension of the underlying type with same-type constraints between the generic parameters and the specific arguments, e.g., ```swift extension simd_float3 { } ``` is treated as ```swift extension SIMD3 where Scalar == Float { } ``` This addresses a source-compatibility problem with SE-0229, where existing types such as simd3_float (which were separate structs) became specializations of a generic SIMD type. Fixes rdar://problem/46604664 and rdar://problem/46604370. (cherry picked from commit b88a87560835164a4d7028c3ed0aa347117b0984) --- lib/Sema/TypeCheckDecl.cpp | 81 +++++++++++++++++++++++------------ test/decl/ext/generic.swift | 3 +- test/decl/ext/typealias.swift | 80 ++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 test/decl/ext/typealias.swift diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 0928bcb5f0076..a9f253f94cac8 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -4844,26 +4844,21 @@ static bool isPassThroughTypealias(TypeAliasDecl *typealias) { /// Form the interface type of an extension from the raw type and the /// extension's list of generic parameters. -static Type formExtensionInterfaceType(TypeChecker &tc, ExtensionDecl *ext, - Type type, - GenericParamList *genericParams, - bool &mustInferRequirements) { +static Type formExtensionInterfaceType( + TypeChecker &tc, ExtensionDecl *ext, + Type type, + GenericParamList *genericParams, + SmallVectorImpl> &sameTypeReqs, + bool &mustInferRequirements) { if (type->is()) return type; // Find the nominal type declaration and its parent type. - Type parentType; - GenericTypeDecl *genericDecl; - if (auto unbound = type->getAs()) { - parentType = unbound->getParent(); - genericDecl = unbound->getDecl(); - } else { - if (type->is()) - type = type->getCanonicalType(); - auto nominalType = type->castTo(); - parentType = nominalType->getParent(); - genericDecl = nominalType->getDecl(); - } + if (type->is()) + type = type->getCanonicalType(); + + Type parentType = type->getNominalParent(); + GenericTypeDecl *genericDecl = type->getAnyGeneric(); // Reconstruct the parent, if there is one. if (parentType) { @@ -4873,7 +4868,7 @@ static Type formExtensionInterfaceType(TypeChecker &tc, ExtensionDecl *ext, : genericParams; parentType = formExtensionInterfaceType(tc, ext, parentType, parentGenericParams, - mustInferRequirements); + sameTypeReqs, mustInferRequirements); } // Find the nominal type. @@ -4891,9 +4886,20 @@ static Type formExtensionInterfaceType(TypeChecker &tc, ExtensionDecl *ext, resultType = NominalType::get(nominal, parentType, nominal->getASTContext()); } else { + auto currentBoundType = type->getAs(); + // Form the bound generic type with the type parameters provided. + unsigned gpIndex = 0; for (auto gp : *genericParams) { - genericArgs.push_back(gp->getDeclaredInterfaceType()); + SWIFT_DEFER { ++gpIndex; }; + + auto gpType = gp->getDeclaredInterfaceType(); + genericArgs.push_back(gpType); + + if (currentBoundType) { + sameTypeReqs.push_back({gpType, + currentBoundType->getGenericArgs()[gpIndex]}); + } } resultType = BoundGenericType::get(nominal, parentType, genericArgs); @@ -4930,8 +4936,9 @@ checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext, Type type, // Form the interface type of the extension. bool mustInferRequirements = false; + SmallVector, 4> sameTypeReqs; Type extInterfaceType = - formExtensionInterfaceType(tc, ext, type, genericParams, + formExtensionInterfaceType(tc, ext, type, genericParams, sameTypeReqs, mustInferRequirements); // Local function used to infer requirements from the extended type. @@ -4943,6 +4950,13 @@ checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext, Type type, extInterfaceType, nullptr, source); + + for (const auto &sameTypeReq : sameTypeReqs) { + builder.addRequirement( + Requirement(RequirementKind::SameType, sameTypeReq.first, + sameTypeReq.second), + source, ext->getModuleContext()); + } }; // Validate the generic type signature. @@ -4950,11 +4964,20 @@ checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext, Type type, ext->getDeclContext(), nullptr, /*allowConcreteGenericParams=*/true, ext, inferExtendedTypeReqs, - mustInferRequirements); + (mustInferRequirements || + !sameTypeReqs.empty())); return { env, extInterfaceType }; } +static bool isNonGenericTypeAliasType(Type type) { + // A non-generic typealias can extend a specialized type. + if (auto *aliasType = dyn_cast(type.getPointer())) + return aliasType->getDecl()->getGenericContextDepth() == (unsigned)-1; + + return false; +} + static void validateExtendedType(ExtensionDecl *ext, TypeChecker &tc) { // If we didn't parse a type, fill in an error type and bail out. if (!ext->getExtendedTypeLoc().getTypeRepr()) { @@ -4998,20 +5021,22 @@ static void validateExtendedType(ExtensionDecl *ext, TypeChecker &tc) { return; } - // Cannot extend a bound generic type. - if (extendedType->isSpecialized()) { - tc.diagnose(ext->getLoc(), diag::extension_specialization, - extendedType->getAnyNominal()->getName()) + // Cannot extend function types, tuple types, etc. + if (!extendedType->getAnyNominal()) { + tc.diagnose(ext->getLoc(), diag::non_nominal_extension, extendedType) .highlight(ext->getExtendedTypeLoc().getSourceRange()); ext->setInvalid(); ext->getExtendedTypeLoc().setInvalidType(tc.Context); return; } - // Cannot extend function types, tuple types, etc. - if (!extendedType->getAnyNominal()) { - tc.diagnose(ext->getLoc(), diag::non_nominal_extension, extendedType) - .highlight(ext->getExtendedTypeLoc().getSourceRange()); + // Cannot extend a bound generic type, unless it's referenced via a + // non-generic typealias type. + if (extendedType->isSpecialized() && + !isNonGenericTypeAliasType(extendedType)) { + tc.diagnose(ext->getLoc(), diag::extension_specialization, + extendedType->getAnyNominal()->getName()) + .highlight(ext->getExtendedTypeLoc().getSourceRange()); ext->setInvalid(); ext->getExtendedTypeLoc().setInvalidType(tc.Context); return; diff --git a/test/decl/ext/generic.swift b/test/decl/ext/generic.swift index 1ffb54344bd6b..9eb79279daeef 100644 --- a/test/decl/ext/generic.swift +++ b/test/decl/ext/generic.swift @@ -30,7 +30,7 @@ extension X { typealias GGG = X -extension GGG { } // expected-error{{constrained extension must be declared on the unspecialized generic type 'X' with constraints specified by a 'where' clause}} +extension GGG { } // okay through a typealias // Lvalue check when the archetypes are not the same. struct LValueCheck { @@ -209,4 +209,3 @@ extension A.B { extension A.B.D { func g() { } } - diff --git a/test/decl/ext/typealias.swift b/test/decl/ext/typealias.swift new file mode 100644 index 0000000000000..c5917a0a0d565 --- /dev/null +++ b/test/decl/ext/typealias.swift @@ -0,0 +1,80 @@ +// RUN: %target-typecheck-verify-swift + +struct Foo { + var maybeT: T? { return nil } +} + +extension Foo { + struct Bar { + var maybeT: T? { return nil } + var maybeU: U? { return nil } + var maybeV: V? { return nil } + + struct Inner { + var maybeT: T? { return nil } + var maybeU: U? { return nil } + var maybeV: V? { return nil } + } + } +} + +typealias FooInt = Foo + +extension FooInt { + func goodT() -> Int { + return maybeT! + } + + func badT() -> Float { + return maybeT! // expected-error{{cannot convert return expression of type 'Int' to return type 'Float'}} + } +} + +typealias FooIntBarFloatDouble = Foo.Bar + +extension FooIntBarFloatDouble { + func goodT() -> Int { + return maybeT! + } + func goodU() -> Float { + return maybeU! + } + func goodV() -> Double { + return maybeV! + } + + func badT() -> Float { + return maybeT! // expected-error{{cannot convert return expression of type 'Int' to return type 'Float'}} + } + func badU() -> Int { + return maybeU! // expected-error{{cannot convert return expression of type 'Float' to return type 'Int'}} + } + func badV() -> Int { + return maybeV! // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}} + } +} + +typealias FooIntBarFloatDoubleInner = Foo.Bar.Inner + +extension FooIntBarFloatDoubleInner { + func goodT() -> Int { + return maybeT! + } + func goodU() -> Float { + return maybeU! + } + func goodV() -> Double { + return maybeV! + } + + func badT() -> Float { + return maybeT! // expected-error{{cannot convert return expression of type 'Int' to return type 'Float'}} + } + func badU() -> Int { + return maybeU! // expected-error{{cannot convert return expression of type 'Float' to return type 'Int'}} + } + func badV() -> Int { + return maybeV! // expected-error{{cannot convert return expression of type 'Double' to return type 'Int'}} + } +} + From 3ff5339f60f228c2ea8d425b52625f69e3a0395b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Dec 2018 16:31:25 -0800 Subject: [PATCH 2/2] [Type checker] Update for 5.0 branch. --- lib/Sema/TypeCheckDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index a9f253f94cac8..6048d0c54911e 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -4973,7 +4973,7 @@ checkExtensionGenericParams(TypeChecker &tc, ExtensionDecl *ext, Type type, static bool isNonGenericTypeAliasType(Type type) { // A non-generic typealias can extend a specialized type. if (auto *aliasType = dyn_cast(type.getPointer())) - return aliasType->getDecl()->getGenericContextDepth() == (unsigned)-1; + return aliasType->getDecl()->getGenericParamsOfContext() == nullptr; return false; }