From a71c31bd7480f3a07ce786c1c41f38ad218ae7d2 Mon Sep 17 00:00:00 2001 From: Azoy Date: Sun, 27 Sep 2020 16:29:14 -0400 Subject: [PATCH 1/6] [AST] Parse generic parameters for extension --- include/swift/AST/Decl.h | 2 ++ lib/AST/Decl.cpp | 8 +++++--- lib/ClangImporter/ImportDecl.cpp | 5 +++-- lib/Parse/ParseDecl.cpp | 16 ++++++++++++++++ lib/Serialization/Deserialization.cpp | 2 +- utils/gyb_syntax_support/DeclNodes.py | 6 ++++-- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 3c279e4a2e640..96f6c8bf06085 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1216,6 +1216,7 @@ class ExtensionDecl final : public GenericContext, public Decl, ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, MutableArrayRef inherited, + GenericParamList *genericParams, DeclContext *parent, TrailingWhereClause *trailingWhereClause); @@ -1241,6 +1242,7 @@ class ExtensionDecl final : public GenericContext, public Decl, static ExtensionDecl *create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, MutableArrayRef inherited, + GenericParamList *genericParams, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode = ClangNode()); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 75b354c0d29d9..1b76298febdac 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1103,9 +1103,10 @@ NominalTypeDecl::takeConformanceLoaderSlow() { ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, TypeRepr *extendedType, MutableArrayRef inherited, + GenericParamList *genericParams, DeclContext *parent, TrailingWhereClause *trailingWhereClause) - : GenericContext(DeclContextKind::ExtensionDecl, parent, nullptr), + : GenericContext(DeclContextKind::ExtensionDecl, parent, genericParams), Decl(DeclKind::Extension, parent), IterableDeclContext(IterableDeclContextKind::ExtensionDecl), ExtensionLoc(extensionLoc), @@ -1120,6 +1121,7 @@ ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc, ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, TypeRepr *extendedType, MutableArrayRef inherited, + GenericParamList *genericParams, DeclContext *parent, TrailingWhereClause *trailingWhereClause, ClangNode clangNode) { @@ -1130,8 +1132,8 @@ ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, // Construct the extension. auto result = ::new (declPtr) ExtensionDecl(extensionLoc, extendedType, - inherited, parent, - trailingWhereClause); + inherited, genericParams, + parent, trailingWhereClause); if (clangNode) result->setClangNode(clangNode); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 079ded2e02094..9a72e48eebc27 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4679,7 +4679,7 @@ namespace { auto result = ExtensionDecl::create( Impl.SwiftContext, loc, nullptr, - { }, dc, nullptr, decl); + { }, nullptr, dc, nullptr, decl); Impl.SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{result}, objcClass->getDeclaredType()); Impl.SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{result}, @@ -8382,7 +8382,8 @@ ClangImporter::Implementation::importDeclContextOf( // Create a new extension for this nominal type/Clang submodule pair. auto ext = ExtensionDecl::create(SwiftContext, SourceLoc(), nullptr, {}, - getClangModuleForDecl(decl), nullptr); + nullptr, getClangModuleForDecl(decl), + nullptr); SwiftContext.evaluator.cacheOutput(ExtendedTypeRequest{ext}, nominal->getDeclaredType()); SwiftContext.evaluator.cacheOutput(ExtendedNominalRequest{ext}, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index da749109d0fd9..7af96e5fa7701 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4648,6 +4648,19 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { DebuggerContextChange DCC (*this); + // Parse the generic params, if present. + Optional genericScope; + genericScope.emplace(this, ScopeKind::Generics); + GenericParamList *genericParams; + bool gpHasCodeCompletion = false; + + auto genericResult = maybeParseGenericParams(); + genericParams = genericResult.getPtrOrNull(); + gpHasCodeCompletion |= genericResult.hasCodeCompletion(); + + if (gpHasCodeCompletion && !CodeCompletion) + return makeParserCodeCompletionStatus(); + // Parse the type being extended. ParserStatus status; ParserResult extendedType = parseType(diag::extension_type_expected); @@ -4681,9 +4694,12 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { status |= whereStatus; } + diagnoseWhereClauseInGenericParamList(genericParams); + ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc, extendedType.getPtrOrNull(), Context.AllocateCopy(Inherited), + genericParams, CurDeclContext, trailingWhereClause); ext->getAttrs() = Attributes; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 78dd359d94238..7ff357c73e806 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3929,7 +3929,7 @@ class DeclDeserializer { return declOrOffset; auto extension = ExtensionDecl::create(ctx, SourceLoc(), nullptr, { }, - DC, nullptr); + nullptr, DC, nullptr); declOrOffset = extension; // Generic parameter lists are written from outermost to innermost. diff --git a/utils/gyb_syntax_support/DeclNodes.py b/utils/gyb_syntax_support/DeclNodes.py index c1811acbe5018..c6d1e4bb86400 100644 --- a/utils/gyb_syntax_support/DeclNodes.py +++ b/utils/gyb_syntax_support/DeclNodes.py @@ -263,8 +263,8 @@ ]), # extension-declaration -> attributes? access-level-modifier? - # 'extension' extended-type - # type-inheritance-clause? + # 'extension' generic-parameter-clause? + # extended-type type-inheritance-clause? # generic-where-clause? # '{' extension-members '}' # extension-name -> identifier @@ -275,6 +275,8 @@ Child('Modifiers', kind='ModifierList', collection_element_name='Modifier', is_optional=True), Child('ExtensionKeyword', kind='ExtensionToken'), + Child('GenericParameterClause', kind='GenericParameterClause', + is_optional=True), Child('ExtendedType', kind='Type'), Child('InheritanceClause', kind='TypeInheritanceClause', is_optional=True), From 51b6434d4b144e66052e18f05ffdb76d968b081b Mon Sep 17 00:00:00 2001 From: Azoy Date: Thu, 1 Oct 2020 13:58:34 -0400 Subject: [PATCH 2/6] [Sema] Basic support for parameterized extensions and extending specialized types --- include/swift/AST/Decl.h | 4 + include/swift/AST/DiagnosticsSema.def | 3 - lib/AST/Decl.cpp | 14 +++ lib/AST/DeclContext.cpp | 7 +- lib/AST/NameLookup.cpp | 26 +++++- lib/AST/NameLookupRequests.cpp | 7 ++ lib/Sema/TypeCheckDecl.cpp | 18 ---- lib/Sema/TypeCheckDeclPrimary.cpp | 12 +++ lib/Sema/TypeCheckGeneric.cpp | 4 + test/decl/ext/extensions.swift | 3 +- test/decl/ext/generic.swift | 2 - test/decl/ext/parameterized.swift | 130 ++++++++++++++++++++++++++ test/decl/typealias/generic.swift | 4 +- 13 files changed, 206 insertions(+), 28 deletions(-) create mode 100644 test/decl/ext/parameterized.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 96f6c8bf06085..5cdfb19af5551 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1347,6 +1347,10 @@ class ExtensionDecl final : public GenericContext, public Decl, /// resiliently moved into the original protocol itself. bool isEquivalentToExtendedContext() const; + /// Whether this extension contains unique generic parameters that differ from + /// the extended nominal's generic signature. + bool isParameterized() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() == DeclKind::Extension; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6bd1a32a6eb76..a39d0eea0bc4b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1797,9 +1797,6 @@ ERROR(extension_access_with_conformances,none, "protocol conformances", (DeclAttribute)) ERROR(extension_metatype,none, "cannot extend a metatype %0", (Type)) -ERROR(extension_specialization,none, - "constrained extension must be declared on the unspecialized generic " - "type %0 with constraints specified by a 'where' clause", (Identifier)) ERROR(extension_stored_property,none, "extensions must not contain stored properties", ()) NOTE(extension_stored_property_fixit,none, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1b76298febdac..e7eefa50ad310 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1231,6 +1231,20 @@ bool ExtensionDecl::isEquivalentToExtendedContext() const { && !getDeclaredInterfaceType()->isExistentialType(); } +bool ExtensionDecl::isParameterized() const { + if (!hasBeenBound()) + return getParsedGenericParams(); + + auto nominal = getExtendedNominal(); + if (!nominal && getParsedGenericParams()) + return true; + + if (!nominal) + return false; + + return getGenericContextDepth() != nominal->getGenericContextDepth(); +} + AccessLevel ExtensionDecl::getDefaultAccessLevel() const { ASTContext &ctx = getASTContext(); return evaluateOrDefault(ctx.evaluator, diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 8aa916c6e128e..dd13cc689d477 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -96,7 +96,12 @@ GenericTypeParamType *DeclContext::getProtocolSelfType() const { if (auto proto = dyn_cast(this)) { genericParams = proto->getGenericParams(); } else { - genericParams = cast(this)->getGenericParams(); + auto ext = cast(this); + genericParams = ext->getGenericParams(); + + if (ext->isParameterized()) { + genericParams = genericParams->getOuterParameters(); + } } if (genericParams == nullptr) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index bd2620d838768..ab4d176785506 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2382,6 +2382,13 @@ createExtensionGenericParams(ASTContext &ctx, NominalTypeDecl *nominal) { // Collect generic parameters from all outer contexts. SmallVector allGenericParams; + + // If this is a parameterized extension, set those generic params as innermost + // parameters. + if (auto gpList = ext->getParsedGenericParams()) { + allGenericParams.push_back(gpList); + } + nominal->forEachGenericContext([&](GenericParamList *gpList) { allGenericParams.push_back(gpList->clone(ext)); }); @@ -2405,6 +2412,12 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c if (!nominal) { return nullptr; } + + bool isParameterized = false; + if (ext->getParsedGenericParams()) { + isParameterized = true; + } + auto *genericParams = createExtensionGenericParams(ctx, ext, nominal); // Protocol extensions need an inheritance clause due to how name lookup @@ -2412,12 +2425,23 @@ GenericParamListRequest::evaluate(Evaluator &evaluator, GenericContext *value) c if (auto *proto = ext->getExtendedProtocolDecl()) { auto protoType = proto->getDeclaredInterfaceType(); TypeLoc selfInherited[1] = { TypeLoc::withoutLoc(protoType) }; - genericParams->getParams().front()->setInherited( + auto protoParams = genericParams; + + if (isParameterized) { + protoParams = protoParams->getOuterParameters(); + } + + protoParams->getParams().front()->setInherited( ctx.AllocateCopy(selfInherited)); } // Set the depth of every generic parameter. unsigned depth = nominal->getGenericContextDepth(); + + if (isParameterized) { + depth += 1; + } + for (auto *outerParams = genericParams; outerParams != nullptr; outerParams = outerParams->getOuterParameters()) diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 924d93e02049f..793d11b326172 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -212,6 +212,13 @@ void GetDestructorRequest::cacheResult(DestructorDecl *value) const { Optional GenericParamListRequest::getCachedResult() const { auto *decl = std::get<0>(getStorage()); + + // If this is an extension and we can get the parsed generic params, go ahead + // and go through the request evaluation because we still need to clone the + // nominal's generic param list. + if (isa(decl) && decl->getParsedGenericParams()) + return None; + if (auto *params = decl->GenericParamsAndBit.getPointer()) return params; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 4bec788c5daea..51c338d539443 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2769,14 +2769,6 @@ bool TypeChecker::isPassThroughTypealias(TypeAliasDecl *typealias, }); } -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; -} - Type ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { auto error = [&ext]() { @@ -2844,15 +2836,5 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); } - // Cannot extend a bound generic type, unless it's referenced via a - // non-generic typealias type. - if (extendedType->isSpecialized() && - !isNonGenericTypeAliasType(extendedType)) { - diags.diagnose(ext->getLoc(), diag::extension_specialization, - extendedType->getAnyNominal()->getName()) - .highlight(extendedRepr->getSourceRange()); - return error(); - } - return extendedType; } diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index bb7a11096c2a5..83e8b212a9440 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2408,6 +2408,18 @@ class DeclChecker : public DeclVisitor { } void visitExtensionDecl(ExtensionDecl *ED) { + // First, if this extension is parameterized and isn't invalid, go ahead and + // compute the generic signature. This is important because the interface + // type could contain a generic parameter and we need to set depths of each + // parameter. + if (ED->isParameterized()) { + if (!ED->isInvalid()) { + (void) ED->getGenericSignature(); + } else { + return; + } + } + // Produce any diagnostics for the extended type. auto extType = ED->getExtendedType(); diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 9c69020d726b0..9236a59c41880 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -531,6 +531,10 @@ static Type formExtensionInterfaceType( } else { auto currentBoundType = type->getAs(); + if (ext->isParameterized()) { + genericParams = genericParams->getOuterParameters(); + } + // Form the bound generic type with the type parameters provided. unsigned gpIndex = 0; for (auto gp : *genericParams) { diff --git a/test/decl/ext/extensions.swift b/test/decl/ext/extensions.swift index b6f5133acfd8d..c8224bf09036d 100644 --- a/test/decl/ext/extensions.swift +++ b/test/decl/ext/extensions.swift @@ -339,7 +339,8 @@ extension Tree.LimbContent.Contents { } extension Tree.BoughPayload.Contents { - // expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}} + // expected-error@-1 {{extension of type 'Tree.BoughPayload.Contents' (aka 'Nest') must be declared as an extension of 'Nest'}} + // expected-note@-2 {{did you mean to extend 'Nest' instead?}} {{11-37=Nest}} } // SR-10466 Check 'where' clause when referencing type defined inside extension diff --git a/test/decl/ext/generic.swift b/test/decl/ext/generic.swift index 9937853d7e2e3..9aa968a5a9f6b 100644 --- a/test/decl/ext/generic.swift +++ b/test/decl/ext/generic.swift @@ -19,11 +19,9 @@ extension Double : P2 { } extension X { -// expected-error@-1{{constrained extension must be declared on the unspecialized generic type 'X' with constraints specified by a 'where' clause}} let x = 0 // expected-error@-1 {{extensions must not contain stored properties}} static let x = 0 - // expected-error@-1 {{static stored properties not supported in generic types}} func f() -> Int {} class C {} } diff --git a/test/decl/ext/parameterized.swift b/test/decl/ext/parameterized.swift new file mode 100644 index 0000000000000..971793875b86b --- /dev/null +++ b/test/decl/ext/parameterized.swift @@ -0,0 +1,130 @@ +// RUN: %target-typecheck-verify-swift + +struct Pair { + var first: Element + var second: Element +} + +// Reject invalid extensions + +struct BadScope { + extension Pair {} // expected-error {{declaration is only valid at file scope}} +} + +// Basic parameterized extensions + +// Extend Pair where Element == Optional +extension Pair where Element == T? { + var firstUnwrapped: T { + first! + } +} + +let a = Pair(first: 316, second: nil) +_ = a.firstUnwrapped // ok + +let b = Pair(first: 316, second: 128) +_ = b.firstUnwrapped // expected-error {{type of expression is ambiguous without more context}} + +// Ensure we can extend types with same type requirements + +// Extend Pair where Element == T, T: FixedWidthInteger +extension Pair { // expected-note {{where 'Element' = 'Double'}} + var sum: T { + first + second + } +} + +let c = Pair(first: 316, second: 128) +_ = c.sum // ok + +let d = Pair(first: .pi, second: .zero) +_ = d.sum // expected-error {{property 'sum' requires that 'Double' conform to 'FixedWidthInteger'}} + +// Extend Pair where Element == Optional +extension Pair { + var secondUnwrapped: T { + second! + } +} + +let e = Pair(first: nil, second: 128) +_ = e.secondUnwrapped // ok + +let f = Pair(first: 316, second: 128) +_ = f.secondUnwrapped // expected-error {{type of expression is ambiguous without more context}} + +// Extend Pair where Element == Array +extension Pair<[T]> { // expected-note {{where 'Element' = 'Set'}} + // expected-note@-1 {{'T' declared as parameter to type 'Pair'}} + var concatenated: [T] { + first + second + } +} + +let g = Pair<[Int]>(first: [1, 2, 3], second: [4, 5, 6]) +_ = g.concatenated // ok + +let h = Pair>(first: [1, 2, 3], second: [4, 5, 6]) +_ = h.concatenated // expected-error {{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{property 'concatenated' requires the types 'Set' and '[T]' be equivalent}} + +// Ensure we can extend specialized types + +// Extend Pair where Element == String +extension Pair { // expected-note {{where 'Element' = '[Character]'}} + // expected-note@-1 {{where 'Element' = '[Character]'}} + var firstLowered: String { + first.lowercased() + } + + var secondLowered: String { + second.lowercased() + } +} + +let i = Pair(first: "Hello", second: "Hey") +_ = i.firstLowered // ok +_ = i.secondLowered // ok + +let j = Pair<[Character]>(first: ["H", "e", "l", "l", "o"], second: ["H", "e", "y"]) +_ = j.firstLowered // expected-error {{property 'firstLowered' requires the types '[Character]' and 'String' be equivalent}} +_ = j.secondLowered // expected-error {{property 'secondLowered' requires the types '[Character]' and 'String' be equivalent}} + +// Ensure we can extend sugar types + +// Extend Array where Element == Optional +extension [T?] { // expected-note {{where 'Element' = 'Int'}} + // expected-note@-1 {{'T' declared as parameter to type 'Array'}} + var someValues: [T] { + var result = [T]() + for opt in self { + if let value = opt { result.append(value) } + } + + return result + } +} + +let k = [1, 2, nil, 4] +_ = k.someValues // ok + +let l = [1, 2, 3, 4] +_ = l.someValues // expected-error {{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{property 'someValues' requires the types 'Int' and 'T?' be equivalent}} + +// Protocol Extensions + +extension Collection where Element == T? { // expected-note {{where 'Self.Element' = 'Int'}} + // expected-note@-1 {{'T' declared as parameter to type 'Collection'}} + func compacted() -> [T] { + compactMap { $0 } + } +} + +let m = [1, 2, nil, 4] +_ = m.compacted() // ok + +let n = [1, 2, 3, 4] +_ = n.compacted() // expected-error {{referencing instance method 'compacted()' on 'Collection' requires the types 'Int' and 'T?' be equivalent}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} diff --git a/test/decl/typealias/generic.swift b/test/decl/typealias/generic.swift index faf486048ec3a..a5ba335795f7e 100644 --- a/test/decl/typealias/generic.swift +++ b/test/decl/typealias/generic.swift @@ -302,9 +302,9 @@ func takesSugaredType2(m: GenericClass.TA) { extension A {} extension A {} // expected-error {{generic type 'A' specialized with too few type parameters (got 1, but expected 2)}} -extension A {} // expected-error {{constrained extension must be declared on the unspecialized generic type 'MyType' with constraints specified by a 'where' clause}} +extension A {} extension C {} // expected-error {{cannot find type 'T' in scope}} -extension C {} // expected-error {{constrained extension must be declared on the unspecialized generic type 'MyType' with constraints specified by a 'where' clause}} +extension C {} protocol ErrorQ { From 97ae737d3f6e2090ce5e6cfa0f14c754682d174c Mon Sep 17 00:00:00 2001 From: Azoy Date: Thu, 1 Oct 2020 17:42:59 -0400 Subject: [PATCH 3/6] [Sema] Diagnose extending generic parameters and concrete types for parameterized extensions --- include/swift/AST/DiagnosticsSema.def | 4 ++++ lib/Sema/TypeCheckDecl.cpp | 18 +++++++++++++++++ test/decl/ext/parameterized.swift | 29 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a39d0eea0bc4b..0e22ed67bee07 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1787,6 +1787,10 @@ ERROR(inferred_opaque_type,none, // Extensions ERROR(non_nominal_extension,none, "non-nominal type %0 cannot be extended", (Type)) +ERROR(generic_param_extension,none, + "cannot extend generic parameter type %0", (Identifier)) +ERROR(concrete_generic_extension,none, + "cannot have generic parameters when extending a concrete type", ()) WARNING(composition_in_extended_type,none, "extending a protocol composition is not supported; extending %0 " "instead", (Type)) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 51c338d539443..f628bfff73694 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2829,6 +2829,14 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); } + // Cannot extend generic type parameters. + if (auto gpTy = extendedType->getAs()) { + diags.diagnose(ext->getLoc(), diag::generic_param_extension, + gpTy->getName()) + .highlight(extendedRepr->getSourceRange()); + return error(); + } + // Cannot extend function types, tuple types, etc. if (!extendedType->getAnyNominal()) { diags.diagnose(ext->getLoc(), diag::non_nominal_extension, extendedType) @@ -2836,5 +2844,15 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); } + // Cannot extend a concrete type if the extension is parameterized. + bool isConcrete = !extendedType->getAnyNominal()->isGeneric(); + bool isSpecialized = extendedType->isSpecialized() && + !extendedType->hasTypeParameter(); + if ((isConcrete || isSpecialized) && ext->isParameterized()) { + diags.diagnose(ext->getLoc(), diag::concrete_generic_extension) + .highlight(extendedRepr->getSourceRange()); + return error(); + } + return extendedType; } diff --git a/test/decl/ext/parameterized.swift b/test/decl/ext/parameterized.swift index 971793875b86b..bc68ddefb5f6e 100644 --- a/test/decl/ext/parameterized.swift +++ b/test/decl/ext/parameterized.swift @@ -128,3 +128,32 @@ _ = m.compacted() // ok let n = [1, 2, 3, 4] _ = n.compacted() // expected-error {{referencing instance method 'compacted()' on 'Collection' requires the types 'Int' and 'T?' be equivalent}} // expected-error@-1 {{generic parameter 'T' could not be inferred}} + +// Cannot extend generic type parameters + +extension T { // expected-error {{cannot extend generic parameter type 'T'}} + func sayHello() { + print("Hello!") + } +} + +protocol X {} +protocol Y {} + +extension T: Y { // expected-error {{cannot extend generic parameter type 'T'}} + func sayGoodbye() { + print("Goodbye!") + } +} + +// Cannot extend concrete types (also specialized types) + +extension Int {} // expected-error {{cannot have generic parameters when extending a concrete type}} + +extension [Int] {} // expected-error {{cannot have generic parameters when extending a concrete type}} + +extension [Int] where Element == T? {} // expected-error {{cannot have generic parameters when extending a concrete type}} + +extension [T?] {} // ok + +extension [[[T?]]] {} // ok From 8fc71a96f4f331e2c38c18cfb505b8a1ca502173 Mon Sep 17 00:00:00 2001 From: Azoy Date: Thu, 1 Oct 2020 18:47:30 -0400 Subject: [PATCH 4/6] [AST] Support conditional conformance for parameterized extensions --- lib/AST/ProtocolConformance.cpp | 7 ++++--- test/decl/ext/parameterized.swift | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 62b4791e29c88..5896211d7f570 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -538,10 +538,11 @@ void NormalProtocolConformance::differenceAndStoreConditionalRequirements() // The extension signature should be a superset of the type signature, meaning // every thing in the type signature either is included too or is implied by // something else. The most important bit is having the same type - // parameters. (NB. if/when Swift gets parameterized extensions, this needs to - // change.) - assert(typeSig->getCanonicalSignature().getGenericParams() == + // parameters. If the extension is parameterized however, this is untrue. + if (!ext->isParameterized()) { + assert(typeSig->getCanonicalSignature().getGenericParams() == extensionSig->getCanonicalSignature().getGenericParams()); + } // Find the requirements in the extension that aren't proved by the original // type, these are the ones that make the conformance conditional. diff --git a/test/decl/ext/parameterized.swift b/test/decl/ext/parameterized.swift index bc68ddefb5f6e..dbbbb0b48e699 100644 --- a/test/decl/ext/parameterized.swift +++ b/test/decl/ext/parameterized.swift @@ -157,3 +157,19 @@ extension [Int] where Element == T? {} // expected-error {{cannot have generi extension [T?] {} // ok extension [[[T?]]] {} // ok + +// Conditional Conformance (!!!) + +struct Wrapped { + let value: T +} + +extension Pair: Equatable {} + +let o = Pair(first: 316, second: 128) +let p = Pair(first: 316, second: 128) +_ = o == p // ok + +let q = Pair>(first: .init(value: 316), second: .init(value: 128)) +let r = Pair>(first: .init(value: 316), second: .init(value: 128)) +_ = q == r // expected-error {{binary operator '==' cannot be applied to two 'Pair>' operands}} From 226d90dd03d580a7398f45e9eff5c8de0bee7fa0 Mon Sep 17 00:00:00 2001 From: Azoy Date: Mon, 5 Oct 2020 13:18:39 -0400 Subject: [PATCH 5/6] [Sema] Support nested types in parameterized extensions --- include/swift/AST/DeclContext.h | 9 +++ lib/AST/ASTDumper.cpp | 3 + lib/AST/ASTPrinter.cpp | 10 ++- lib/AST/DeclContext.cpp | 11 ++++ lib/AST/Type.cpp | 105 +++++++++++++++++++++++++----- test/decl/ext/parameterized.swift | 15 +++++ 6 files changed, 134 insertions(+), 19 deletions(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index e71f5d96c95fd..2d93cf9f79c95 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -448,6 +448,15 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { const_cast(this)->getInnermostSkippedFunctionContext(); } + /// Returns the outermost context based off syntactic depth, right below the + /// module context. E.g. for a nested type in a nested type in an extension, + /// this returns that extension. + LLVM_READONLY + DeclContext *getOutermostSyntacticContext(); + const DeclContext *getOutermostSyntacticContext() const { + return const_cast(this)->getOutermostSyntacticContext(); + } + /// Returns the semantic parent of this context. A context has a /// parent if and only if it is not a module context. DeclContext *getParent() const { diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index dd12a1570f09b..056d5b5ceabd4 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -630,6 +630,9 @@ namespace { void visitExtensionDecl(ExtensionDecl *ED) { printCommon(ED, "extension_decl", ExtensionColor); + if (ED->isParameterized()) { + printGenericParameters(OS, ED->getParsedGenericParams()); + } OS << ' '; ED->getExtendedType().print(OS); printCommonPost(ED); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e07febf037f29..232b6ee064570 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2315,9 +2315,15 @@ void PrintAST::printExtension(ExtensionDecl *decl) { if (Options.BracketOptions.shouldOpenExtension(decl)) { printDocumentationComment(decl); printAttributes(decl); - Printer << "extension "; + Printer << tok::kw_extension; + + if (decl->isParameterized()) { + printGenericDeclGenericParams(decl); + } + + Printer << " "; + recordDeclLoc(decl, [&]{ - // We cannot extend sugared types. Type extendedType = decl->getExtendedType(); if (!extendedType) { // Fallback to TypeRepr. diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index dd13cc689d477..5e36165b80914 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -247,6 +247,17 @@ DeclContext *DeclContext::getInnermostSkippedFunctionContext() { return nullptr; } +DeclContext *DeclContext::getOutermostSyntacticContext() { + auto depth = getSyntacticDepth() - 1; + auto dc = this; + + for (auto i = 0; i != depth; i += 1) { + dc = dc->getParent(); + } + + return dc; +} + DeclContext *DeclContext::getParentForLookup() const { if (isa(this) || isa(this)) { // If we are inside a protocol or an extension, skip directly diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 129efae29ed8c..4a5025fd4c902 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3995,9 +3995,8 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, // Gather all of the substitutions for all levels of generic arguments. auto params = genericSig->getGenericParams(); - unsigned n = params.size(); - while (baseTy && n > 0) { + while (baseTy) { if (baseTy->is()) break; @@ -4005,21 +4004,24 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, // argument substitutions. if (auto boundGeneric = baseTy->getAs()) { auto args = boundGeneric->getGenericArgs(); - for (unsigned i = 0, e = args.size(); i < e; ++i) { - substitutions[params[n - e + i]->getCanonicalType() - ->castTo()] = args[i]; + auto type = boundGeneric->getAnyGeneric(); + auto params = type->getGenericParams()->getParams(); + + for (unsigned i : indices(args)) { + auto genericTy = params[i]->getDeclaredInterfaceType() + ->getCanonicalType() + ->castTo(); + substitutions[genericTy] = args[i]; } // Continue looking into the parent. baseTy = boundGeneric->getParent(); - n -= args.size(); continue; } // Continue looking into the parent. if (auto protocolTy = baseTy->getAs()) { baseTy = protocolTy->getParent(); - --n; continue; } @@ -4034,16 +4036,85 @@ TypeBase::getContextSubstitutions(const DeclContext *dc, break; } - while (n > 0) { - auto *gp = params[--n]; - auto substTy = (genericEnv - ? genericEnv->mapTypeIntoContext(gp) - : gp); - auto result = substitutions.insert( - {gp->getCanonicalType()->castTo(), - substTy}); - assert(result.second); - (void) result; + // Record a list of generic params that didn't have a concrete replacement. + SmallVector missingSubs; + for (auto param : params) { + auto genericTy = param->getCanonicalType()->castTo(); + + if (substitutions.find(genericTy) != substitutions.end()) { + continue; + } + + auto replacement = genericEnv ? genericEnv->mapTypeIntoContext(param) + : param; + + missingSubs.push_back(genericTy); + substitutions[genericTy] = replacement; + } + + // Attempt to fish out a concrete type for missing subs, but only if we have a + // concrete type and the context is a parameterized extension. (This only + // occurs in parameterized extensions because those generic params don't show + // up in the type signature.) + auto outermostDC = dc->getOutermostSyntacticContext(); + auto ext = dyn_cast(outermostDC); + auto isParameterized = ext ? ext->isParameterized() : false; + bool isSpecialized = this->isSpecialized() && !this->hasTypeParameter(); + + if (isParameterized && isSpecialized && !missingSubs.empty()) { + // FIXME: This is O(n*m) (where n is the size of missing subs and m is the + // size of generic requirements) which feels really bad. Is there a better + // way to do this? + for (auto missingSub : missingSubs) { + for (auto req : genericSig->getRequirements()) { + if (req.getKind() != RequirementKind::SameType) { + continue; + } + + if (!req.getSecondType()->hasTypeParameter()) { + continue; + } + + auto firstTy = req.getFirstType(); + auto secondTy = req.getSecondType(); + auto canTy = firstTy->getCanonicalType() + ->castTo(); + auto replace = substitutions[canTy]; + + if (replace->hasTypeParameter()) { + continue; + } + + // Walk the second type in the same type requirement and look for the + // missing substitution type. + auto wasFound = secondTy.findIf([&](Type type) { + return type->isEqual(missingSub); + }); + + if (!wasFound) { + continue; + } + + secondTy = genericSig->getGenericEnvironment() + ->mapTypeIntoContext(secondTy); + + // If we found the missing substitution within the second type, walk the + // replaced type in parallel as the original type, looking for the + // missing substitution. + secondTy->substituteBindingsTo(replace, + [&](ArchetypeType *origTy, + CanType substTy, ArchetypeType *, + ArrayRef) { + // If the original generic parameter is equal to this missing + // substitution, then we found the substitution. + if (origTy->getInterfaceType()->isEqual(missingSub)) { + substitutions[missingSub] = substTy; + } + + return substTy; + }); + } + } } return substitutions; diff --git a/test/decl/ext/parameterized.swift b/test/decl/ext/parameterized.swift index dbbbb0b48e699..8f5bea4ce2274 100644 --- a/test/decl/ext/parameterized.swift +++ b/test/decl/ext/parameterized.swift @@ -173,3 +173,18 @@ _ = o == p // ok let q = Pair>(first: .init(value: 316), second: .init(value: 128)) let r = Pair>(first: .init(value: 316), second: .init(value: 128)) _ = q == r // expected-error {{binary operator '==' cannot be applied to two 'Pair>' operands}} + +// Nested Types in Parameterized Extensions + +extension Pair { // expected-note {{requirement specified as 'Element' == 'T?' [with Element = Int, T = T]}} + struct NestedPair { + let first: T + let second: T + } +} + +let s = Pair.NestedPair(first: 128, second: 316) // ok + +let t = Pair.NestedPair(first: 128, second: 316) // expected-error {{'Pair.NestedPair' requires the types 'Int' and 'T?' be equivalent}} + +let u = Pair.NestedPair(first: nil, second: 316) //expected-error {{'nil' is not compatible with expected argument type 'Int'}} From 86c524a6ab4a427b4eba973399756ca8147d20c7 Mon Sep 17 00:00:00 2001 From: Azoy Date: Mon, 5 Oct 2020 13:20:17 -0400 Subject: [PATCH 6/6] [Mangling] Support parameterized extensions as a new mangling node --- include/swift/AST/ASTDemangler.h | 2 +- include/swift/Demangling/DemangleNodes.def | 1 + include/swift/Demangling/Demangler.h | 2 +- include/swift/Demangling/TypeDecoder.h | 1 + lib/AST/ASTDemangler.cpp | 38 ++++++++++++++++++---- lib/AST/ASTMangler.cpp | 23 ++++++++----- lib/Demangling/Demangler.cpp | 20 +++++++++--- lib/Demangling/NodePrinter.cpp | 2 ++ lib/Demangling/OldDemangler.cpp | 18 +++++++++- lib/Demangling/OldRemangler.cpp | 17 +++++++--- lib/Demangling/Remangler.cpp | 14 ++++++-- test/Demangle/Inputs/manglings.txt | 2 ++ test/TypeDecoder/extensions.swift | 21 ++++++++++++ 13 files changed, 132 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index f527b6a0d49b7..188232e8f41b0 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -153,7 +153,7 @@ class ASTBuilder { bool validateParentType(TypeDecl *decl, Type parent); CanGenericSignature demangleGenericSignature( NominalTypeDecl *nominalDecl, - NodePointer node); + NodePointer node, bool isParameterizedExtension); DeclContext *findDeclContext(NodePointer node); ModuleDecl *findModule(NodePointer node); Demangle::NodePointer findModuleNode(NodePointer node); diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 1c3c612f9e98a..6ec2a4a3111ef 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -297,6 +297,7 @@ NODE(NoncanonicalSpecializedGenericTypeMetadataCache) NODE(GlobalVariableOnceFunction) NODE(GlobalVariableOnceToken) NODE(GlobalVariableOnceDeclList) +CONTEXT_NODE(GenericExtension) #undef CONTEXT_NODE #undef NODE diff --git a/include/swift/Demangling/Demangler.h b/include/swift/Demangling/Demangler.h index 3616ff426af86..6b803a4a37571 100644 --- a/include/swift/Demangling/Demangler.h +++ b/include/swift/Demangling/Demangler.h @@ -503,7 +503,7 @@ class Demangler : public NodeFactory { NodePointer popTypeAndGetAnyGeneric(); NodePointer demangleBuiltinType(); NodePointer demangleAnyGenericType(Node::Kind kind); - NodePointer demangleExtensionContext(); + NodePointer demangleExtensionContext(bool isParameterized); NodePointer demanglePlainFunction(); NodePointer popFunctionType(Node::Kind kind); NodePointer popFunctionParams(Node::Kind kind); diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index fb33edf878252..3094689c63de6 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -1078,6 +1078,7 @@ class TypeDecoder { case Node::Kind::Module: break; case Node::Kind::Extension: + case Node::Kind::GenericExtension: // Decode the type being extended. if (parentContext->getNumChildren() < 2) return MAKE_NODE_TYPE_ERROR(parentContext, diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 68428f45bd27b..62d06aba71668 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -840,13 +840,33 @@ ASTBuilder::getForeignModuleKind(NodePointer node) { CanGenericSignature ASTBuilder::demangleGenericSignature( NominalTypeDecl *nominalDecl, - NodePointer node) { + NodePointer node, + bool isParameterizedExtension) { + SmallVector gpTypes; SmallVector requirements; for (auto &child : *node) { - if (child->getKind() == - Demangle::Node::Kind::DependentGenericParamCount) + if (child->getKind() == Demangle::Node::Kind::DependentGenericParamCount) { + // If this is not a parameterized extension, just ignore this node. + if (!isParameterizedExtension) { + continue; + } + + // However, if it is a parameterized extension, this node tells us exactly + // how many generic parameters the extension introduced. Create that many + // generic parameter types for the generic signature. + + // The extension's generic depth is 1 higher than the context it's + // extending. + unsigned depth = nominalDecl->getGenericContextDepth() + 1; + + for (uint64_t i = 0; i != child->getIndex(); i += 1) { + auto gpTy = GenericTypeParamType::get(depth, i, Ctx); + gpTypes.push_back(gpTy); + } + continue; + } if (child->getNumChildren() != 2) return CanGenericSignature(); @@ -930,7 +950,7 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( return evaluateOrDefault(Ctx.evaluator, AbstractGenericSignatureRequest{ nominalDecl->getGenericSignature().getPointer(), - {}, + std::move(gpTypes), std::move(requirements)}, GenericSignature()) .getCanonicalSignature(); @@ -1018,7 +1038,8 @@ ASTBuilder::findDeclContext(NodePointer node) { case Demangle::Node::Kind::Global: return findDeclContext(node->getChild(0)); - case Demangle::Node::Kind::Extension: { + case Demangle::Node::Kind::Extension: + case Demangle::Node::Kind::GenericExtension: { auto *moduleDecl = dyn_cast_or_null( findDeclContext(node->getChild(0))); if (!moduleDecl) @@ -1029,9 +1050,14 @@ ASTBuilder::findDeclContext(NodePointer node) { if (!nominalDecl) return nullptr; + bool isParameterized = false; + if (node->getKind() == Demangle::Node::Kind::GenericExtension) + isParameterized = true; + CanGenericSignature genericSig; if (node->getNumChildren() > 2) - genericSig = demangleGenericSignature(nominalDecl, node->getChild(2)); + genericSig = demangleGenericSignature(nominalDecl, node->getChild(2), + isParameterized); for (auto *ext : nominalDecl->getExtensions()) { if (ext->getParentModule() != moduleDecl) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 48fb9337f6459..48d3eec55c31d 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1920,13 +1920,13 @@ void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName) return; case DeclContextKind::ExtensionDecl: { - auto ExtD = cast(ctx); - auto decl = ExtD->getExtendedNominal(); + auto ext = cast(ctx); + auto decl = ext->getExtendedNominal(); // Recover from erroneous extension. if (!decl) - return appendContext(ExtD->getDeclContext(), useModuleName); + return appendContext(ext->getDeclContext(), useModuleName); - if (!ExtD->isEquivalentToExtendedContext()) { + if (!ext->isEquivalentToExtendedContext()) { // Mangle the extension if: // - the extension is defined in a different module from the original // nominal type decl, @@ -1936,17 +1936,22 @@ void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName) // "extension is to a protocol" would no longer be a reason to use the // extension mangling, because an extension method implementation could be // resiliently moved into the original protocol itself. - auto sig = ExtD->getGenericSignature(); + auto sig = ext->getGenericSignature(); // If the extension is constrained, mangle the generic signature that // constrains it. appendAnyGenericType(decl); - appendModule(ExtD->getParentModule(), useModuleName); - if (sig && ExtD->isConstrainedExtension()) { - Mod = ExtD->getModuleContext(); - auto nominalSig = ExtD->getSelfNominalTypeDecl() + appendModule(ext->getParentModule(), useModuleName); + if (sig && ext->isConstrainedExtension()) { + Mod = ext->getModuleContext(); + auto nominalSig = ext->getSelfNominalTypeDecl() ->getGenericSignatureOfContext(); appendGenericSignature(sig, nominalSig); } + + if (ext->isParameterized()) { + return appendOperator("J"); + } + return appendOperator("E"); } return appendAnyGenericType(decl); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index d6a06b003f6da..bb016cefd1ee5 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -740,7 +740,7 @@ NodePointer Demangler::demangleOperator() { case 'B': return demangleBuiltinType(); case 'C': return demangleAnyGenericType(Node::Kind::Class); case 'D': return demangleTypeMangling(); - case 'E': return demangleExtensionContext(); + case 'E': return demangleExtensionContext(/*parameterized*/ false); case 'F': return demanglePlainFunction(); case 'G': return demangleBoundGenericType(); case 'H': @@ -762,6 +762,7 @@ NodePointer Demangler::demangleOperator() { } case 'I': return demangleImplFunctionType(); + case 'J': return demangleExtensionContext(/*parameterized*/ true); case 'K': return createNode(Node::Kind::ThrowsAnnotation); case 'L': return demangleLocalIdentifier(); case 'M': return demangleMetatype(); @@ -1199,13 +1200,21 @@ NodePointer Demangler::demangleAnyGenericType(Node::Kind kind) { return NTy; } -NodePointer Demangler::demangleExtensionContext() { +NodePointer Demangler::demangleExtensionContext(bool isParameterized) { NodePointer GenSig = popNode(Node::Kind::DependentGenericSignature); NodePointer Module = popModule(); NodePointer Type = popTypeAndGetAnyGeneric(); - NodePointer Ext = createWithChildren(Node::Kind::Extension, Module, Type); + NodePointer Ext; + + if (isParameterized) { + Ext = createWithChildren(Node::Kind::GenericExtension, Module, Type); + } else { + Ext = createWithChildren(Node::Kind::Extension, Module, Type); + } + if (GenSig) Ext = addChild(Ext, GenSig); + return Ext; } @@ -1639,10 +1648,11 @@ NodePointer Demangler::demangleBoundGenericArgs(NodePointer Nominal, if (TypeListIdx < TypeLists.size()) { NodePointer BoundParent = nullptr; - if (Context->getKind() == Node::Kind::Extension) { + if (Context->getKind() == Node::Kind::Extension || + Context->getKind() == Node::Kind::GenericExtension) { BoundParent = demangleBoundGenericArgs(Context->getChild(1), TypeLists, TypeListIdx); - BoundParent = createWithChildren(Node::Kind::Extension, + BoundParent = createWithChildren(Context->getKind(), Context->getFirstChild(), BoundParent); if (Context->getNumChildren() == 3) { diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 5287239caffc5..6c7a05c8f58d1 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -556,6 +556,7 @@ class NodePrinter { case Node::Kind::GlobalVariableOnceDeclList: case Node::Kind::GlobalVariableOnceFunction: case Node::Kind::GlobalVariableOnceToken: + case Node::Kind::GenericExtension: return false; } printer_unreachable("bad node kind"); @@ -1166,6 +1167,7 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { } return nullptr; case Node::Kind::Extension: + case Node::Kind::GenericExtension: assert((Node->getNumChildren() == 2 || Node->getNumChildren() == 3) && "Extension expects 2 or 3 children."); if (Options.QualifyEntities && Options.DisplayExtensionContexts) { diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index 575750012f1c7..e84599f975c2b 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -1011,7 +1011,8 @@ class OldDemangler { if (parentOrModule->getKind() != Node::Kind::Module && parentOrModule->getKind() != Node::Kind::Function && - parentOrModule->getKind() != Node::Kind::Extension) { + parentOrModule->getKind() != Node::Kind::Extension && + parentOrModule->getKind() != Node::Kind::GenericExtension) { parentOrModule = demangleBoundGenericArgs(parentOrModule); if (!parentOrModule) return nullptr; @@ -1082,6 +1083,7 @@ class OldDemangler { // context ::= entity // context ::= 'E' module context (extension defined in a different module) // context ::= 'e' module context generic-signature (constrained extension) + // context ::= 'J' module context generic-signature (parameterized extension) if (!Mangled) return nullptr; if (Mangled.nextIf('E')) { NodePointer ext = Factory.createNode(Node::Kind::Extension); @@ -1110,6 +1112,20 @@ class OldDemangler { ext->addChild(sig, Factory); return ext; } + if (Mangled.nextIf('J')) { + NodePointer ext = Factory.createNode(Node::Kind::GenericExtension); + NodePointer def_module = demangleModule(); + if (!def_module) return nullptr; + NodePointer sig = demangleGenericSignature(); + if (!sig) return nullptr; + NodePointer type = demangleContext(); + if (!type) return nullptr; + + ext->addChild(def_module, Factory); + ext->addChild(type, Factory); + ext->addChild(sig, Factory); + return ext; + } if (Mangled.nextIf('S')) return demangleSubstitutionIndex(); if (Mangled.nextIf('s')) diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index d402cc7bcbb0f..86e4b865a1075 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1576,12 +1576,20 @@ void Remangler::mangleExtension(Node *node, EntityContext &ctx) { } else { Buffer << 'E'; } - mangleEntityContext(node->begin()[0], ctx); // module + mangleEntityContext(node->getChild(0), ctx); // module if (node->getNumChildren() == 3) { - mangleDependentGenericSignature(node->begin()[2]); // generic sig + mangleDependentGenericSignature(node->getChild(2)); // generic sig } - mangleEntityContext(node->begin()[1], ctx); // context + mangleEntityContext(node->getChild(1), ctx); // context +} + +void Remangler::mangleGenericExtension(Node *node, EntityContext &ctx) { + assert(node->getNumChildren() == 3); + Buffer << 'J'; + mangleEntityContext(node->getChild(0), ctx); // module + mangleDependentGenericSignature(node->getChild(2)); // generic sig + mangleEntityContext(node->getChild(1), ctx); // context } void Remangler::mangleAnonymousContext(Node *node, EntityContext &ctx) { @@ -1734,7 +1742,8 @@ void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx) { } case Node::Kind::AnonymousContext: - case Node::Kind::Extension: { + case Node::Kind::Extension: + case Node::Kind::GenericExtension: { mangleGenericArgs(node->getChild(1), ctx); break; } diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 0b0fdec171717..5cb1fc94fd6ed 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -573,6 +573,7 @@ void Remangler::mangleGenericArgs(Node *node, char &Separator, } case Node::Kind::Extension: + case Node::Kind::GenericExtension: mangleGenericArgs(node->getChild(1), Separator, fullSubstitutionMap); break; @@ -1023,6 +1024,13 @@ void Remangler::mangleExtension(Node *node) { Buffer << 'E'; } +void Remangler::mangleGenericExtension(Node *node) { + mangleChildNode(node, 1); // context + mangleChildNode(node, 0); // type + mangleChildNode(node, 2); // generic signature + Buffer << 'J'; +} + void Remangler::mangleAnonymousContext(Node *node) { mangleChildNode(node, 1); mangleChildNode(node, 0); @@ -2662,6 +2670,7 @@ bool Demangle::isSpecialized(Node *node) { return isSpecialized(node->getChild(0)); case Node::Kind::Extension: + case Node::Kind::GenericExtension: return isSpecialized(node->getChild(1)); default: @@ -2732,11 +2741,12 @@ NodePointer Demangle::getUnspecialized(Node *node, NodeFactory &Factory) { return unboundFunction; } - case Node::Kind::Extension: { + case Node::Kind::Extension: + case Node::Kind::GenericExtension: { NodePointer parent = node->getChild(1); if (!isSpecialized(parent)) return node; - NodePointer result = Factory.createNode(Node::Kind::Extension); + NodePointer result = Factory.createNode(node->getKind()); result->addChild(node->getFirstChild(), Factory); result->addChild(getUnspecialized(parent, Factory), Factory); if (node->getNumChildren() == 3) { diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 294e7a08a34c2..25af239635498 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -370,3 +370,5 @@ $s7example1fyyYF -> example.f() async -> () $s7example1fyyYKF -> example.f() async throws -> () $s4main20receiveInstantiationyySo34__CxxTemplateInst12MagicWrapperIiEVzF ---> main.receiveInstantiation(inout __C.__CxxTemplateInst12MagicWrapperIiE) -> () $s4main19returnInstantiationSo34__CxxTemplateInst12MagicWrapperIiEVyF ---> main.returnInstantiation() -> __C.__CxxTemplateInst12MagicWrapperIiE +_TtCJ4mainRxzGSqqd___rVS_4Pair6Nested ---> (extension in main):main.Pair.Nested +$s4main4PairVAAqd__SgRszlJ6NestedC ---> (extension in main):main.Pair.Nested diff --git a/test/TypeDecoder/extensions.swift b/test/TypeDecoder/extensions.swift index a0fbc4c44ea91..b1250a7dadbee 100644 --- a/test/TypeDecoder/extensions.swift +++ b/test/TypeDecoder/extensions.swift @@ -58,3 +58,24 @@ extension Generic where T: AnyObject { // DEMANGLE-DECL: $s10extensions7GenericVAARlzClE18NestedViaAnyObjectV // CHECK-DECL: extensions.(file).Generic extension.NestedViaAnyObject +// Parameterized Extensions + +extension Generic { + struct Nested3 {} +} + +extension Generic { + struct Nested4 {} +} + +// DEMANGLE-TYPE: $s10extensions7GenericVAAqd__SgRszlJ7Nested3VyAD_GD +// CHECK-TYPE: Generic>.Nested3 + +// DEMANGLE-TYPE: $s10extensions7GenericVA2A5ProtoRzqd__RszlJ7Nested4Vyx_GD +// CHECK-TYPE: Generic<τ_0_0>.Nested4 + +// DEMANGLE-DECL: $s10extensions7GenericVAAqd__SgRszlJ7Nested3V +// CHECK-DECL: extensions.(file).Generic extension.Nested3 + +// DEMANGLE-DECL: $s10extensions7GenericVA2A5ProtoRzqd__RszlJ7Nested4V +// CHECK-DECL: extensions.(file).Generic extension.Nested4