From 612991e903eee0ef8cf9a3b6ad16c4294382fddd Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 22 Jun 2016 13:32:47 -0700 Subject: [PATCH 1/5] Add two test cases with an unclear diagnostics --- test/Constraints/diagnostics.swift | 5 ++++- test/Generics/invalid.swift | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index a2ff8ccea9efb..764d5c89ecd85 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -777,4 +777,7 @@ class SR1594 { } } - +// FIXME: Bad diagnostic +func secondArgumentNotLabeled(a:Int, _ b: Int) { } +secondArgumentNotLabeled(10, 20) +// expected-error@-1 {{argument '_' must precede unnamed parameter #0}} diff --git a/test/Generics/invalid.swift b/test/Generics/invalid.swift index 01b126ab27a75..0893ed40f0a64 100644 --- a/test/Generics/invalid.swift +++ b/test/Generics/invalid.swift @@ -71,3 +71,11 @@ func badDiagnostic2() { // expected-note@-2 {{overloads for 'eatDinnerConcrete' exist with these partially matching parameter lists: (d: Pizzas.NewYork, t: Deli.Pepperoni), (d: Pizzas.DeepDish, t: Deli.Pepperoni)}} } + +// Real error is that we cannot infer the generic parameter from context + +func takesAny(_ a: Any) {} + +func badDiagnostic3() { + takesAny(Deli.self) // expected-error {{argument type 'Deli<_>.Type' does not conform to expected type 'Any' (aka 'protocol<>')}} +} From 7e6ece67996e18b1d2a092c06af50e0b3a363599 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 18 Jun 2016 20:35:19 -0700 Subject: [PATCH 2/5] IRGen: It appears parent type metadata is filled in correctly now --- lib/IRGen/Fulfillment.cpp | 3 --- lib/IRGen/GenMeta.cpp | 1 - 2 files changed, 4 deletions(-) diff --git a/lib/IRGen/Fulfillment.cpp b/lib/IRGen/Fulfillment.cpp index c70bdfe1ccbef..46c844cbf2eab 100644 --- a/lib/IRGen/Fulfillment.cpp +++ b/lib/IRGen/Fulfillment.cpp @@ -212,9 +212,6 @@ bool FulfillmentMap::searchParentTypeMetadata(IRGenModule &IGM, // We might not have a parent type. if (!parent) return false; - // Only class types properly initialize their parent type. - if (!isa(decl)) return false; - // If we do, it has to be nominal one way or another. path.addNominalParentComponent(); return searchTypeMetadata(IGM, parent, IsExact, source, std::move(path),keys); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index f5b72b4bc839d..7dad9a5a88138 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -157,7 +157,6 @@ namespace { /// the fill operations that the compiler emits for the bound decl. /// /// FIXME: Rework to use GenericSignature instead of AllArchetypes - /// FIXME: Rework for nested generics struct GenericArguments { /// The values to use to initialize the arguments structure. SmallVector Values; From 8ffa51485a2502b13b82f41ca6ef4e31c1a4c22d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 2 Jun 2016 15:59:14 -0700 Subject: [PATCH 3/5] AST: Fix mangling for nested generic types Change the 'G' mangling to include generic parameters from all levels of nested nominal types, and not just the innermost. Note that the raw mangling syntax is something like this for a nested type 'A.B': - bound_generic - struct 'B' - struct 'A' - module 'M' - args - Int - args - String However, the actual mangling tree is more along the lines of: - bound_generic_struct 'B' - bound_generic_struct 'A' - module 'M' - args - Int - args - String This arrangement improves the quality of substitutions (we are more likely to have a substitution for the entire unbound generic type name 'A.B' around), and simplifies a few other details. Unfortunately, the remangling logic becomes slightly grotesque. A simple SILGen test for nested generics exercises the mangling, and ensures that Sema and SILGen do not crash with nested generics. More detailed SILGen tests, as well as IRGen support for nested generics is next. --- include/swift/AST/Mangle.h | 1 + lib/AST/Mangle.cpp | 67 +++++--- lib/Basic/Demangle.cpp | 107 ++++++++----- lib/Basic/Remangle.cpp | 160 +++++++++++++++---- test/Demangle/Inputs/manglings.txt | 1 + test/SILGen/mangling_ext_structA.swift | 16 +- test/SILGen/nested_generics.swift | 206 +++++++++++++++++++++++++ 7 files changed, 471 insertions(+), 87 deletions(-) create mode 100644 test/SILGen/nested_generics.swift diff --git a/include/swift/AST/Mangle.h b/include/swift/AST/Mangle.h index 707fc40756f6d..128547db1bf05 100644 --- a/include/swift/AST/Mangle.h +++ b/include/swift/AST/Mangle.h @@ -92,6 +92,7 @@ class Mangler { void mangleClosureEntity(const AbstractClosureExpr *closure, unsigned uncurryingLevel); void mangleNominalType(const NominalTypeDecl *decl); + void mangleBoundGenericType(Type type); void mangleProtocolDecl(const ProtocolDecl *protocol); void mangleType(Type type, unsigned uncurryingLevel); void mangleDirectness(bool isIndirect); diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index eb69cd9266847..314a9a4c78995 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -940,32 +940,17 @@ void Mangler::mangleType(Type type, unsigned uncurryLevel) { Buffer << '_'; return; - case TypeKind::UnboundGeneric: { - // We normally reject unbound types in IR-generation, but there - // are several occasions in which we'd like to mangle them in the - // abstract. - auto decl = cast(tybase)->getDecl(); - mangleNominalType(cast(decl)); - return; - } - + case TypeKind::UnboundGeneric: case TypeKind::Class: case TypeKind::Enum: - case TypeKind::Struct: { - return mangleNominalType(cast(tybase)->getDecl()); - } - + case TypeKind::Struct: case TypeKind::BoundGenericClass: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: { - // type ::= 'G' + '_' - auto *boundType = cast(tybase); - Buffer << 'G'; - mangleNominalType(boundType->getDecl()); - for (auto arg : boundType->getGenericArgs()) { - mangleType(arg, /*uncurry*/ 0); - } - Buffer << '_'; + if (type->isSpecialized()) + mangleBoundGenericType(type); + else + mangleNominalType(tybase->getAnyNominal()); return; } @@ -1332,6 +1317,46 @@ void Mangler::mangleNominalType(const NominalTypeDecl *decl) { addSubstitution(key); } +static void +collectBoundGenericArgs(Type type, + SmallVectorImpl> &genericArgs) { + if (auto *unboundType = type->getAs()) { + if (auto parent = unboundType->getParent()) + collectBoundGenericArgs(parent, genericArgs); + genericArgs.push_back({}); + } else if (auto *nominalType = type->getAs()) { + if (auto parent = nominalType->getParent()) + collectBoundGenericArgs(parent, genericArgs); + genericArgs.push_back({}); + } else { + auto *boundType = type->castTo(); + if (auto parent = boundType->getParent()) + collectBoundGenericArgs(parent, genericArgs); + + SmallVector args; + for (auto arg : boundType->getGenericArgs()) + args.push_back(arg); + genericArgs.push_back(args); + } +} + +void Mangler::mangleBoundGenericType(Type type) { + // type ::= 'G' (+ '_')+ + Buffer << 'G'; + auto *nominal = type->getAnyNominal(); + mangleNominalType(nominal); + + SmallVector, 2> genericArgs; + collectBoundGenericArgs(type, genericArgs); + assert(!genericArgs.empty()); + + for (auto args : genericArgs) { + for (auto arg : args) + mangleType(arg, /*uncurry*/ 0); + Buffer << '_'; + } +} + void Mangler::mangleProtocolDecl(const ProtocolDecl *protocol) { Buffer << 'P'; mangleContextOf(protocol); diff --git a/lib/Basic/Demangle.cpp b/lib/Basic/Demangle.cpp index 365eb1f21eac9..eb69bbe154b39 100644 --- a/lib/Basic/Demangle.cpp +++ b/lib/Basic/Demangle.cpp @@ -1154,6 +1154,73 @@ class Demangler { return nullptr; } + NodePointer demangleBoundGenericArgs(NodePointer nominalType) { + // Generic arguments for the outermost type come first. + NodePointer parentOrModule = nominalType->getChild(0); + if (parentOrModule->getKind() != Node::Kind::Module) { + parentOrModule = demangleBoundGenericArgs(parentOrModule); + + // Rebuild this type with the new parent type, which may have + // had its generic arguments applied. + NodePointer result = NodeFactory::create(nominalType->getKind()); + result->addChild(parentOrModule); + result->addChild(nominalType->getChild(1)); + + nominalType = result; + } + + NodePointer args = NodeFactory::create(Node::Kind::TypeList); + while (!Mangled.nextIf('_')) { + NodePointer type = demangleType(); + if (!type) + return nullptr; + args->addChild(type); + if (Mangled.isEmpty()) + return nullptr; + } + + // If there were no arguments at this level there is nothing left + // to do. + if (args->getNumChildren() == 0) + return nominalType; + + // Otherwise, build a bound generic type node from the unbound + // type and arguments. + NodePointer unboundType = NodeFactory::create(Node::Kind::Type); + unboundType->addChild(nominalType); + + Node::Kind kind; + switch (nominalType->getKind()) { // look through Type node + case Node::Kind::Class: + kind = Node::Kind::BoundGenericClass; + break; + case Node::Kind::Structure: + kind = Node::Kind::BoundGenericStructure; + break; + case Node::Kind::Enum: + kind = Node::Kind::BoundGenericEnum; + break; + default: + return nullptr; + } + NodePointer result = NodeFactory::create(kind); + result->addChild(unboundType); + result->addChild(args); + return result; + } + + NodePointer demangleBoundGenericType() { + // bound-generic-type ::= 'G' nominal-type (args+ '_')+ + // + // Each level of nominal type nesting has its own list of arguments. + + NodePointer nominalType = demangleNominalType(); + if (!nominalType) + return nullptr; + + return demangleBoundGenericArgs(nominalType); + } + NodePointer demangleContext() { // context ::= module // context ::= entity @@ -1191,6 +1258,8 @@ class Demangler { return demangleSubstitutionIndex(); if (Mangled.nextIf('s')) return NodeFactory::create(Node::Kind::Module, STDLIB_NAME); + if (Mangled.nextIf('G')) + return demangleBoundGenericType(); if (isStartOfEntity(Mangled.peek())) return demangleEntity(); return demangleModule(); @@ -1872,37 +1941,7 @@ class Demangler { return demangleFunctionType(Node::Kind::UncurriedFunctionType); } if (c == 'G') { - NodePointer unboundType = demangleType(); - if (!unboundType) - return nullptr; - NodePointer type_list = NodeFactory::create(Node::Kind::TypeList); - while (!Mangled.nextIf('_')) { - NodePointer type = demangleType(); - if (!type) - return nullptr; - type_list->addChild(type); - if (Mangled.isEmpty()) - return nullptr; - } - Node::Kind bound_type_kind; - switch (unboundType->getChild(0)->getKind()) { // look through Type node - case Node::Kind::Class: - bound_type_kind = Node::Kind::BoundGenericClass; - break; - case Node::Kind::Structure: - bound_type_kind = Node::Kind::BoundGenericStructure; - break; - case Node::Kind::Enum: - bound_type_kind = Node::Kind::BoundGenericEnum; - break; - default: - return nullptr; - } - NodePointer type_application = - NodeFactory::create(bound_type_kind); - type_application->addChild(unboundType); - type_application->addChild(type_list); - return type_application; + return demangleBoundGenericType(); } if (c == 'X') { if (Mangled.nextIf('b')) { @@ -2049,10 +2088,8 @@ class Demangler { return nullptr; } - if (isStartOfNominalType(c)) { - NodePointer nominal_type = demangleDeclarationName(nominalTypeMarkerToNodeKind(c)); - return nominal_type; - } + if (isStartOfNominalType(c)) + return demangleDeclarationName(nominalTypeMarkerToNodeKind(c)); return nullptr; } diff --git a/lib/Basic/Remangle.cpp b/lib/Basic/Remangle.cpp index 7483de82355a4..db14b83bafdce 100644 --- a/lib/Basic/Remangle.cpp +++ b/lib/Basic/Remangle.cpp @@ -208,14 +208,11 @@ namespace { }; class Remangler { - struct ArchetypeInfo { - Node::IndexType Index; - Node::IndexType AbsoluteDepth; - }; - DemanglerPrinter &Out; - std::unordered_map Archetypes; - unsigned AbsoluteArchetypeDepth = 0; + + // We have to cons up temporary nodes sometimes when remangling + // nested generics. This vector owns them. + std::vector TemporaryNodes; std::unordered_map Substitutions; @@ -223,19 +220,8 @@ namespace { Remangler(DemanglerPrinter &out) : Out(out) {} class EntityContext { - Remangler &R; - unsigned SavedAbsoluteDepth; bool AsContext = false; public: - EntityContext(Remangler &R) - : R(R), SavedAbsoluteDepth(R.AbsoluteArchetypeDepth) { - } - - ~EntityContext() { - assert(R.AbsoluteArchetypeDepth >= SavedAbsoluteDepth); - R.AbsoluteArchetypeDepth = SavedAbsoluteDepth; - } - bool isAsContext() const { return AsContext; } @@ -263,6 +249,10 @@ namespace { unreachable("bad demangling tree node"); } + NodePointer getUnspecialized(Node *node); + void mangleGenericArgs(Node *node, EntityContext &ctx); + void mangleAnyNominalType(Node *node, EntityContext &ctx); + #define NODE(ID) \ void mangle##ID(Node *node); #define CONTEXT_NODE(ID) \ @@ -318,7 +308,7 @@ namespace { #define NODE(ID) #define CONTEXT_NODE(ID) \ void Remangler::mangle##ID(Node *node) { \ - EntityContext ctx(*this); \ + EntityContext ctx; \ mangle##ID(node, ctx); \ } #include "swift/Basic/DemangleNodes.def" @@ -1531,20 +1521,132 @@ void Remangler::mangleProtocolWithoutPrefix(Node *node) { } assert(node->getKind() == Node::Kind::Protocol); - EntityContext ctx(*this); + EntityContext ctx; mangleNominalType(node, '\0', ctx); } +static bool isSpecialized(Node *node) { + switch (node->getKind()) { + case Node::Kind::BoundGenericStructure: + case Node::Kind::BoundGenericEnum: + case Node::Kind::BoundGenericClass: + return true; + + case Node::Kind::Structure: + case Node::Kind::Enum: + case Node::Kind::Class: { + Node *parentOrModule = node->getChild(0).get(); + if (isSpecialized(parentOrModule)) + return true; + + return false; + } + + default: + return false; + } +} + +NodePointer Remangler::getUnspecialized(Node *node) { + switch (node->getKind()) { + case Node::Kind::Structure: + case Node::Kind::Enum: + case Node::Kind::Class: { + NodePointer result = NodeFactory::create(node->getKind()); + NodePointer parentOrModule = node->getChild(0); + if (isSpecialized(parentOrModule.get())) + result->addChild(getUnspecialized(parentOrModule.get())); + else + result->addChild(parentOrModule); + result->addChild(node->getChild(1)); + return result; + } + + case Node::Kind::BoundGenericStructure: + case Node::Kind::BoundGenericEnum: + case Node::Kind::BoundGenericClass: { + NodePointer unboundType = node->getChild(0); + assert(unboundType->getKind() == Node::Kind::Type); + NodePointer nominalType = unboundType->getChild(0); + if (isSpecialized(nominalType.get())) + return getUnspecialized(nominalType.get()); + else + return nominalType; + } + + default: + unreachable("bad nominal type kind"); + } +} + +void Remangler::mangleGenericArgs(Node *node, EntityContext &ctx) { + switch (node->getKind()) { + case Node::Kind::Structure: + case Node::Kind::Enum: + case Node::Kind::Class: { + NodePointer parentOrModule = node->getChild(0); + mangleGenericArgs(parentOrModule.get(), ctx); + + // No generic arguments at this level + Out << '_'; + break; + } + + case Node::Kind::BoundGenericStructure: + case Node::Kind::BoundGenericEnum: + case Node::Kind::BoundGenericClass: { + NodePointer unboundType = node->getChild(0); + assert(unboundType->getKind() == Node::Kind::Type); + NodePointer nominalType = unboundType->getChild(0); + NodePointer parentOrModule = nominalType->getChild(0); + mangleGenericArgs(parentOrModule.get(), ctx); + + mangleTypeList(node->getChild(1).get()); + break; + } + + default: + break; + } +} + +void Remangler::mangleAnyNominalType(Node *node, EntityContext &ctx) { + if (isSpecialized(node)) { + Out << 'G'; + + NodePointer unboundType = getUnspecialized(node); + TemporaryNodes.push_back(unboundType); + + mangleAnyNominalType(unboundType.get(), ctx); + mangleGenericArgs(node, ctx); + return; + } + + switch (node->getKind()) { + case Node::Kind::Structure: + mangleNominalType(node, 'V', ctx); + break; + case Node::Kind::Enum: + mangleNominalType(node, 'O', ctx); + break; + case Node::Kind::Class: + mangleNominalType(node, 'C', ctx); + break; + default: + unreachable("bad nominal type kind"); + } +} + void Remangler::mangleStructure(Node *node, EntityContext &ctx) { - mangleNominalType(node, 'V', ctx); + mangleAnyNominalType(node, ctx); } void Remangler::mangleEnum(Node *node, EntityContext &ctx) { - mangleNominalType(node, 'O', ctx); + mangleAnyNominalType(node, ctx); } void Remangler::mangleClass(Node *node, EntityContext &ctx) { - mangleNominalType(node, 'C', ctx); + mangleAnyNominalType(node, ctx); } void Remangler::mangleNominalType(Node *node, char kind, EntityContext &ctx) { @@ -1555,18 +1657,18 @@ void Remangler::mangleNominalType(Node *node, char kind, EntityContext &ctx) { } void Remangler::mangleBoundGenericClass(Node *node) { - Out << 'G'; - mangleChildNodes(node); // type, type list + EntityContext ctx; + mangleAnyNominalType(node, ctx); } void Remangler::mangleBoundGenericStructure(Node *node) { - Out << 'G'; - mangleChildNodes(node); // type, type list + EntityContext ctx; + mangleAnyNominalType(node, ctx); } void Remangler::mangleBoundGenericEnum(Node *node) { - Out << 'G'; - mangleChildNodes(node); // type, type list + EntityContext ctx; + mangleAnyNominalType(node, ctx); } void Remangler::mangleTypeList(Node *node) { diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index c0f11c172e4a8..113458f8af098 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -215,3 +215,4 @@ _TFE1a ---> _TFE1a _TF21$__lldb_module_for_E0au3$E0Ps13ErrorProtocol_ ---> $__lldb_module_for_E0.$E0.unsafeMutableAddressor : Swift.ErrorProtocol _TMps10Comparable ---> protocol descriptor for Swift.Comparable _TFC4testP33_83378C430F65473055F1BD53F3ADCDB71C5doFoofT_T_ ---> test.(C in _83378C430F65473055F1BD53F3ADCDB7).doFoo () -> () +_TFVV15nested_generics5Lunch6DinnerCfT11firstCoursex12secondCourseGSqqd___9leftoversx14transformationFxqd___GS1_x_qd___ ---> nested_generics.Lunch.Dinner.init (firstCourse : A, secondCourse : A1?, leftovers : A, transformation : (A) -> A1) -> nested_generics.Lunch.Dinner diff --git a/test/SILGen/mangling_ext_structA.swift b/test/SILGen/mangling_ext_structA.swift index b7986cf039e0b..5f4e9f035eb53 100644 --- a/test/SILGen/mangling_ext_structA.swift +++ b/test/SILGen/mangling_ext_structA.swift @@ -3,16 +3,28 @@ // RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_structA.swift // RUN: %target-swift-frontend -emit-silgen -module-name ext_structA -I %t %s | FileCheck %s +// Ensure that members of extensions of types from another module are mangled +// correctly. + import def_structA extension A { mutating func test() { a = 1 } + + struct NestedType { + func test() {} + } } func markUsed(_ t: T) {} // CHECK-LABEL: sil hidden @_TFE11ext_structAV11def_structA1A4testfT_T_ -var ao = A() -markUsed(ao.test()) +var a = A() +markUsed(a.test()) + +// CHECK-LABEL: sil hidden @_TFVE11ext_structAV11def_structA1A10NestedType4testfT_T_ + +var nestedType = A.NestedType() +markUsed(nestedType.test()) diff --git a/test/SILGen/nested_generics.swift b/test/SILGen/nested_generics.swift new file mode 100644 index 0000000000000..45c5e7a7a53d7 --- /dev/null +++ b/test/SILGen/nested_generics.swift @@ -0,0 +1,206 @@ +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-silgen -enable-experimental-nested-generic-types -parse-as-library %s | FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-sil -enable-experimental-nested-generic-types -parse-as-library %s > /dev/null +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-sil -O -enable-experimental-nested-generic-types -parse-as-library %s > /dev/null + +// TODO: +// - test generated SIL -- mostly we're just testing mangling here +// - class_method calls +// - witness_method calls +// - inner generic parameters on protocol requirements +// - generic parameter list on method in nested type +// - types nested inside unconstrained extensions of generic types +// - add IRGen support and tests, and ensure this test runs to completion with -emit-ir + +protocol Pizza : class { + associatedtype Topping +} + +protocol HotDog { + associatedtype Condiment +} + +protocol CuredMeat {} + +// Generic nested inside generic + +struct Lunch { + struct Dinner.Mustard> { + let firstCourse: T + let secondCourse: U? + + var leftovers: T + var transformation: (T) -> U + + func coolCombination(t: T.Topping, u: U.Condiment) { + func nestedGeneric(x: X, y: Y) -> (X, Y) { + return (x, y) + } + _ = nestedGeneric(x: t, y: u) + } + } +} + +// CHECK-LABEL: // nested_generics.Lunch.Dinner.coolCombination (t : A.Topping, u : A1.Condiment) -> () +// CHECK-LABEL: sil hidden @_TFVV15nested_generics5Lunch6Dinner15coolCombinationfT1twx7Topping1uwd__9Condiment_T_ : $@convention(method) .Mustard> (@in T.Topping, Deli.Mustard, @in_guaranteed Lunch.Dinner) -> () + +// CHECK-LABEL: // nested_generics.Lunch.Dinner.(coolCombination (t : A.Topping, u : A1.Condiment) -> ()).(nestedGeneric #1) .Mustard> (x : A2, y : B2) -> (A2, B2) +// CHECK-LABEL: sil shared @_TFFVV15nested_generics5Lunch6Dinner15coolCombinationFT1twx7Topping1uwd__9Condiment_T_L_13nestedGenericu__0_RxS_5Pizzad__S_6HotDogwxS2_S_9CuredMeatwd__S3_zGOCS_4Deli7MustardVS_6Pepper__rFT1xqd0__1yqd0_0__Tqd0__qd0_0__ : $@convention(thin) .Mustard> (@in X, @in Y) -> (@out X, @out Y) + +// CHECK-LABEL: // nested_generics.Lunch.Dinner.init (firstCourse : A, secondCourse : Swift.Optional, leftovers : A, transformation : (A) -> A1) -> nested_generics.Lunch.Dinner +// CHECK-LABEL: sil hidden @_TFVV15nested_generics5Lunch6DinnerCfT11firstCoursex12secondCourseGSqqd___9leftoversx14transformationFxqd___GS1_x_qd___ : $@convention(method) .Mustard> (@owned T, @in Optional, @owned T, @owned @callee_owned (@owned T) -> @out U, @thin Lunch.Dinner.Type) -> @out Lunch.Dinner + +// Non-generic nested inside generic + +class Deli : CuredMeat { + + class Pepperoni : CuredMeat {} + struct Sausage : CuredMeat {} + + enum Mustard { + case Yellow + case Dijon + case DeliStyle(Spices) + } +} + +// CHECK-LABEL: // nested_generics.Deli.Pepperoni.init () -> nested_generics.Deli.Pepperoni +// CHECK-LABEL: sil hidden @_TFCC15nested_generics4Deli9PepperonicfT_GS1_x__ : $@convention(method) (@owned Deli.Pepperoni) -> @owned Deli.Pepperoni + +// Typealiases referencing outer generic parameters + +struct Pizzas { + class NewYork : Pizza { + typealias Topping = Deli.Pepperoni + } + + class DeepDish : Pizza { + typealias Topping = Deli.Sausage + } +} + +class HotDogs { + struct Bratwurst : HotDog { + typealias Condiment = Deli.Mustard + } + struct American : HotDog { + typealias Condiment = Deli.Mustard + } +} + +struct Pepper {} +struct ChiliFlakes {} + +// CHECK-LABEL: // nested_generics.eatDinnerGeneric .Mustard> (d : inout nested_generics.Lunch.Dinner, t : A.Topping, u : B.Condiment) -> () +// CHECK-LABEL: sil hidden @_TF15nested_generics16eatDinnerGenericu0_RxS_5Pizza_S_6HotDogwx7ToppingS_9CuredMeatw_9CondimentzGOCS_4Deli7MustardVS_6Pepper__rFT1dRGVVS_5Lunch6Dinnerx_q__1twxS2_1uw_S4__T_ : $@convention(thin) .Mustard> (@inout Lunch.Dinner, @in T.Topping, Deli.Mustard) -> () + +func eatDinnerGeneric(d: inout Lunch.Dinner, t: T.Topping, u: U.Condiment) { + // Method call + _ = d.coolCombination(t: t, u: u) + + // Read a let, store into var + d.leftovers = d.firstCourse + + // Read a var + let _ = d.secondCourse + + // Call property of function type + _ = d.transformation(d.leftovers) +} + +// Overloading concrete function with different bound generic arguments in parent type + +// CHECK-LABEL: // nested_generics.eatDinnerConcrete (d : inout nested_generics.Lunch.NewYork>.Dinner, t : nested_generics.Deli.Pepperoni, u : nested_generics.Deli.Mustard) -> () +// CHECK-LABEL: sil hidden @_TF15nested_generics17eatDinnerConcreteFT1dRGVVS_5Lunch6DinnerGCVS_6Pizzas7NewYorkVS_11ChiliFlakes___VCS_7HotDogs8American_1tGCCS_4Deli9PepperoniS4___1uGOS7_7MustardVS_6Pepper___T_ : $@convention(thin) (@inout Lunch.NewYork>.Dinner, @owned Deli.Pepperoni, Deli.Mustard) -> () + +func eatDinnerConcrete(d: inout Lunch.NewYork>.Dinner, + t: Deli.Pepperoni, + u: Deli.Mustard) { + // Method call + _ = d.coolCombination(t: t, u: u) + + // Read a let, store into var + d.leftovers = d.firstCourse + + // Read a var + let _ = d.secondCourse + + // Call property of function type + _ = d.transformation(d.leftovers) +} + +// CHECK-LABEL: // reabstraction thunk helper from @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@out nested_generics.HotDogs.American) to @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@unowned nested_generics.HotDogs.American) +// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFo_oGCV15nested_generics6Pizzas7NewYorkVS_11ChiliFlakes___iVCS_7HotDogs8American_XFo_oGS1_S2____dS4__ : $@convention(thin) (@owned Pizzas.NewYork, @owned @callee_owned (@owned Pizzas.NewYork) -> @out HotDogs.American) -> HotDogs.American + +// CHECK-LABEL: // nested_generics.eatDinnerConcrete (d : inout nested_generics.Lunch.NewYork>.Dinner, t : nested_generics.Deli.Pepperoni, u : nested_generics.Deli.Mustard) -> () +// CHECK-LABEL: sil hidden @_TF15nested_generics17eatDinnerConcreteFT1dRGVVS_5Lunch6DinnerGCVS_6Pizzas7NewYorkVS_6Pepper___VCS_7HotDogs8American_1tGCCS_4Deli9PepperoniS4___1uGOS7_7MustardS4____T_ : $@convention(thin) (@inout Lunch.NewYork>.Dinner, @owned Deli.Pepperoni, Deli.Mustard) -> () + +func eatDinnerConcrete(d: inout Lunch.NewYork>.Dinner, + t: Deli.Pepperoni, + u: Deli.Mustard) { + // Method call + _ = d.coolCombination(t: t, u: u) + + // Read a let, store into var + d.leftovers = d.firstCourse + + // Read a var + let _ = d.secondCourse + + // Call property of function type + _ = d.transformation(d.leftovers) +} + +// CHECK-LABEL: // reabstraction thunk helper from @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@out nested_generics.HotDogs.American) to @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@unowned nested_generics.HotDogs.American) +// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFo_oGCV15nested_generics6Pizzas7NewYorkVS_6Pepper___iVCS_7HotDogs8American_XFo_oGS1_S2____dS4__ : $@convention(thin) (@owned Pizzas.NewYork, @owned @callee_owned (@owned Pizzas.NewYork) -> @out HotDogs.American) -> HotDogs.American + +// CHECK-LABEL: // nested_generics.(calls () -> ()).(closure #1) +// CHECK-LABEL: sil shared @_TFF15nested_generics5callsFT_T_U_FGCVS_6Pizzas7NewYorkVS_6Pepper__VCS_7HotDogs8American : $@convention(thin) (@owned Pizzas.NewYork) -> HotDogs.American + +func calls() { + + let firstCourse = Pizzas.NewYork() + let secondCourse = HotDogs.American() + + var dinner = Lunch.NewYork>.Dinner( + firstCourse: firstCourse, + secondCourse: secondCourse, + leftovers: firstCourse, + transformation: { _ in HotDogs.American() }) + + let topping = Deli.Pepperoni() + + let condiment1 = Deli.Mustard.Dijon + let condiment2 = Deli.Mustard.DeliStyle(Pepper()) + + eatDinnerGeneric(d: &dinner, t: topping, u: condiment1) + eatDinnerConcrete(d: &dinner, t: topping, u: condiment2) + +} + +// CHECK-LABEL: // reabstraction thunk helper from @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@unowned nested_generics.HotDogs.American) to @callee_owned (@owned nested_generics.Pizzas.NewYork) -> (@out nested_generics.HotDogs.American) +// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFo_oGCV15nested_generics6Pizzas7NewYorkVS_6Pepper___dVCS_7HotDogs8American_XFo_oGS1_S2____iS4__ : $@convention(thin) (@owned Pizzas.NewYork, @owned @callee_owned (@owned Pizzas.NewYork) -> HotDogs.American) -> @out HotDogs.American + +// CHECK: sil_witness_table hidden Deli.Pepperoni: CuredMeat module nested_generics { +// CHECK: } + +// CHECK: sil_witness_table hidden Deli.Sausage: CuredMeat module nested_generics { +// CHECK: } + +// CHECK: sil_witness_table hidden Deli: CuredMeat module nested_generics { +// CHECK: } + +// CHECK: sil_witness_table hidden Pizzas.NewYork: Pizza module nested_generics { +// CHECK: associated_type Topping: Deli.Pepperoni +// CHECK: } + +// CHECK: sil_witness_table hidden Pizzas.DeepDish: Pizza module nested_generics { +// CHECK: associated_type Topping: Deli.Sausage +// CHECK: } + +// CHECK: sil_witness_table hidden HotDogs.Bratwurst: HotDog module nested_generics { +// CHECK: associated_type Condiment: Deli.Mustard +// CHECK: } + +// CHECK: sil_witness_table hidden HotDogs.American: HotDog module nested_generics { +// CHECK: associated_type Condiment: Deli.Mustard +// CHECK: } From 4aa1aa72029718ce81f507d0f45832bcefa72c09 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 18 Jun 2016 20:02:50 -0700 Subject: [PATCH 4/5] IRGen: Preliminary support for nested generic types For now, just run the existing SILGen test to completion. I'll work on more tests later, I wanted to check this stuff in before it bitrots any further. --- lib/IRGen/GenDecl.cpp | 2 +- lib/IRGen/GenMeta.cpp | 32 ++++++++++++++++++++----------- lib/IRGen/GenType.cpp | 4 ++-- lib/IRGen/GenValueWitness.cpp | 5 +---- test/SILGen/nested_generics.swift | 2 +- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index fc5c12a4e1303..814022e0442df 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2338,7 +2338,7 @@ IRGenModule::getAddrOfGenericTypeMetadataAccessFunction( assert(nominal->isGenericContext()); auto type = nominal->getDeclaredType()->getCanonicalType(); - assert(isa(type)); + assert(type->hasUnboundGenericType()); LinkEntity entity = LinkEntity::forTypeMetadataAccessFunction(type); llvm::Function *&entry = GlobalFuncs[entity]; if (entry) { diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 7dad9a5a88138..2c9bfebdbed38 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -189,11 +189,23 @@ namespace { } } - void collect(IRGenFunction &IGF, CanBoundGenericType type) { - GenericTypeRequirements requirements(IGF.IGM, type->getDecl()); + void collect(IRGenFunction &IGF, CanType type) { + NominalTypeDecl *decl; + CanType parentType; + + if (auto nominalType = dyn_cast(type)) { + decl = nominalType->getDecl(); + parentType = nominalType.getParent(); + } else { + auto boundType = cast(type); + decl = boundType->getDecl(); + parentType = boundType.getParent(); + } + + GenericTypeRequirements requirements(IGF.IGM, decl); if (requirements.hasParentType()) { - Values.push_back(IGF.emitTypeMetadataRef(type.getParent())); + Values.push_back(IGF.emitTypeMetadataRef(parentType)); } auto subs = @@ -208,7 +220,7 @@ namespace { } }); - collectTypes(IGF.IGM, type->getDecl()); + collectTypes(IGF.IGM, decl); assert(Types.size() == Values.size()); } }; @@ -372,8 +384,8 @@ static llvm::Value *emitNominalMetadataRef(IRGenFunction &IGF, } // We are applying generic parameters to a generic type. - auto boundGeneric = cast(theType); - assert(boundGeneric->getDecl() == theDecl); + assert(theType->isSpecialized() && + theType->getAnyNominal() == theDecl); // Check to see if we've maybe got a local reference already. if (auto cache = IGF.tryGetLocalTypeData(theType, @@ -382,7 +394,7 @@ static llvm::Value *emitNominalMetadataRef(IRGenFunction &IGF, // Grab the substitutions. GenericArguments genericArgs; - genericArgs.collect(IGF, boundGeneric); + genericArgs.collect(IGF, theType); assert(genericArgs.Values.size() > 0 && "no generic args?!"); // Call the generic metadata accessor function. @@ -451,8 +463,6 @@ bool irgen::isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type) { // access if it contains a resilient type. if (isa(type) || isa(type)) { auto nominalType = cast(type); - assert(!nominalType->getDecl()->isGenericContext() && - "shouldn't be called for a generic type"); // Imported type metadata always requires an accessor. if (nominalType->getDecl()->hasClangNode()) @@ -529,9 +539,9 @@ irgen::getTypeMetadataAccessStrategy(IRGenModule &IGM, CanType type) { // Metadata accessors for fully-substituted generic types are // emitted with shared linkage. if (nominal->isGenericContext() && !nominal->isObjC()) { - if (isa(type)) + if (type->isSpecialized()) return MetadataAccessStrategy::NonUniqueAccessor; - assert(isa(type)); + assert(type->hasUnboundGenericType()); } // If the type doesn't guarantee that it has an access function, diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index 72666ae9418fe..040d945f67a0f 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -1558,7 +1558,7 @@ TypeCacheEntry TypeConverter::convertAnyNominalType(CanType type, llvm_unreachable("bad declaration kind"); } - assert(decl->getGenericParams()); + assert(decl->isGenericContext()); // Look to see if we've already emitted this type under a different // set of arguments. We cache under the unbound type, which should @@ -1567,7 +1567,7 @@ TypeCacheEntry TypeConverter::convertAnyNominalType(CanType type, // FIXME: this isn't really inherently good; we might want to use // different type implementations for different applications. assert(decl->getDeclaredType()->isCanonical()); - assert(decl->getDeclaredType()->is()); + assert(decl->getDeclaredType()->hasUnboundGenericType()); TypeBase *key = decl->getDeclaredType().getPointer(); auto &Cache = Types.IndependentCache; auto entry = Cache.find(key); diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 3dacf1cb755b2..a0d5043181dd5 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1334,10 +1334,7 @@ static void addValueWitnesses(IRGenModule &IGM, FixedPacking packing, /// Currently, this is true if the size and/or alignment of the type is /// dependent on its generic parameters. bool irgen::hasDependentValueWitnessTable(IRGenModule &IGM, CanType ty) { - if (auto ugt = dyn_cast(ty)) - ty = ugt->getDecl()->getDeclaredTypeInContext()->getCanonicalType(); - - return !IGM.getTypeInfoForUnlowered(ty).isFixedSize(); + return !IGM.getTypeInfoForUnlowered(getFormalTypeInContext(ty)).isFixedSize(); } static void addValueWitnessesForAbstractType(IRGenModule &IGM, diff --git a/test/SILGen/nested_generics.swift b/test/SILGen/nested_generics.swift index 45c5e7a7a53d7..996e81f7b36ba 100644 --- a/test/SILGen/nested_generics.swift +++ b/test/SILGen/nested_generics.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-silgen -enable-experimental-nested-generic-types -parse-as-library %s | FileCheck %s // RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-sil -enable-experimental-nested-generic-types -parse-as-library %s > /dev/null // RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-sil -O -enable-experimental-nested-generic-types -parse-as-library %s > /dev/null +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-ir -enable-experimental-nested-generic-types -parse-as-library %s > /dev/null // TODO: // - test generated SIL -- mostly we're just testing mangling here @@ -9,7 +10,6 @@ // - inner generic parameters on protocol requirements // - generic parameter list on method in nested type // - types nested inside unconstrained extensions of generic types -// - add IRGen support and tests, and ensure this test runs to completion with -emit-ir protocol Pizza : class { associatedtype Topping From cdc0b7787e42bb28df5615eb70decd81a49b86f4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 22 Jun 2016 17:04:52 -0700 Subject: [PATCH 5/5] Preliminary executable test for nested generic types --- test/Interpreter/nested_generics.swift | 102 ++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/test/Interpreter/nested_generics.swift b/test/Interpreter/nested_generics.swift index 3766cd651861f..3af5558be42f0 100644 --- a/test/Interpreter/nested_generics.swift +++ b/test/Interpreter/nested_generics.swift @@ -1,20 +1,28 @@ -// RUN: %target-run-simple-swift | FileCheck %s +// RUN: mkdir -p %t +// RUN: %target-build-swift %s -Xfrontend -enable-experimental-nested-generic-types -o %t/a.out +// RUN: %target-run %t/a.out | FileCheck %s // REQUIRES: executable_test protocol MyPrintable { func print() } -struct PrintableValue : MyPrintable { - init(_ value: String) { - self.value = value +extension Int : MyPrintable { + func print() { + Swift.print(self, terminator: "") } +} +extension String : MyPrintable { func print() { - Swift.print(value, terminator: "") + Swift.print(self, terminator: "") } +} - var value: String +extension Array : MyPrintable { + func print() { + Swift.print(self, terminator: "") + } } class Foo { @@ -34,7 +42,81 @@ class Foo { } // CHECK: init 1 two -var foo = Foo(PrintableValue("1"), PrintableValue("two")) -// CHECK: bar 3 -var c = PrintableValue("3") -foo.bar(c) +// CHECK: bar [1] +var foo = Foo(1, "two") +foo.bar([1]) + +struct OuterStruct { + let t: T + + struct InnerStruct { + let u: U + + func printBoth(t: T) { + t.print() + print(" ", terminator: "") + u.print() + } + + static func printBoth(t: T, u: U) { + t.print() + print(" ", terminator: "") + u.print() + } + + func printAllThree(t: T, v: V) { + printBoth(t: t) + print(" ", terminator: "") + v.print() + } + } + + class InnerClass { + let u: U + + init(u: U) { + self.u = u + } + + func printBoth(t: T) { + t.print() + print(" ", terminator: "") + u.print() + } + + static func printBoth(t: T, u: U) { + t.print() + print(" ", terminator: "") + u.print() + } + + func printAllThree(t: T, v: V) { + printBoth(t: t) + print(" ", terminator: "") + v.print() + } + } +} + +class SubClass : OuterStruct.InnerClass { + override func printBoth(t: Y) { + print("override ", terminator: "") + super.printBoth(t: t) + } + + // FIXME: Does not work! + /* override func printAllThree(t: Y, v: Z) { + print("super ", terminator: "") + super.printAllThree(t: t, v: v) + } */ +} + +// CHECK: 1 two +// CHECK: 1 two +// CHECK: 1 two [3] +OuterStruct.InnerStruct(u: "two").printBoth(t: 1) +OuterStruct.InnerStruct.printBoth(t: 1, u: "two") +OuterStruct.InnerStruct(u: "two").printAllThree(t: 1, v: [3]) + +// CHECK: override 1 two [3] +SubClass(u: "two").printAllThree(t: 1, v: [3])