diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index cd9425be16612..298911b9c93ff 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5944,6 +5944,46 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var, return initArg; } +/// Writes a tuple expression where each element is either `nil` or another such +/// tuple of nils. +/// This comes up when printing default arguments for memberwise initializers +/// that were created implicitly. +/// For example, this var: +/// ``` +/// var x: (Int?, (Int?, Int?, ())) +/// ``` +/// will produce `(nil, (nil, nil, ()))` +static void writeTupleOfNils(TupleType *type, llvm::raw_ostream &os) { + os << '('; + for (unsigned i = 0; i < type->getNumElements(); ++i) { + auto &elt = type->getElement(i); + if (elt.hasName()) { + os << elt.getName().str() << ": "; + } + + if (elt.getType()->getOptionalObjectType()) { + os << "nil"; + } else { + writeTupleOfNils(elt.getType()->castTo(), os); + } + if (i < type->getNumElements() - 1) { + os << ", "; + } + } + os << ')'; +} + +/// Determines if the given type is a potentially nested tuple of optional +/// types. +static bool isTupleOfOptionals(Type type) { + auto tuple = type->getAs(); + if (!tuple) return false; + for (auto elt : tuple->getElementTypes()) + if (!elt->getOptionalObjectType() && !isTupleOfOptionals(elt)) + return false; + return true; +} + StringRef ParamDecl::getDefaultValueStringRepresentation( SmallVectorImpl &scratch) const { @@ -6010,8 +6050,24 @@ ParamDecl::getDefaultValueStringRepresentation( } } + auto init = var->getParentInitializer(); + if (!init || !init->getSourceRange().isValid()) { + // Special case: There are two possible times where we will synthesize a + // default initial value for a stored property: if the type + // is Optional, or if it's a (potentially nested) tuple of + // all Optional elements. If it's Optional, we'll set + // the DefaultArgumentKind to NilLiteral, but if we're still + // handling a StoredProperty, then we know it's a tuple. + if (isTupleOfOptionals(getInterfaceType())) { + llvm::raw_svector_ostream os(scratch); + writeTupleOfNils(getInterfaceType()->castTo(), os); + return os.str(); + } + return "<>"; + } + return extractInlinableText(getASTContext().SourceMgr, - var->getParentInitializer(), + init, scratch); } case DefaultArgumentKind::Inherited: return "super"; diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 31033c0b8b0f5..911fa7119441b 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -1347,6 +1347,20 @@ class FooClassComputed { // PASS_PRINT_AST: } } +// PASS_PRINT_AST: struct HasDefaultTupleOfNils { +// PASS_PRINT_AST: var x: (Int?, Int?) +// PASS_PRINT_AST: var y: Int? +// PASS_PRINT_AST: var z: Int +// PASS_PRINT_AST: var w: ((Int?, (), Int?), (Int?, Int?)) +// PASS_PRINT_AST: init(x: (Int?, Int?) = (nil, nil), y: Int? = nil, z: Int, w: ((Int?, (), Int?), (Int?, Int?)) = ((nil, (), nil), (nil, nil))) +// PASS_PRINT_AST: } +struct HasDefaultTupleOfNils { + var x: (Int?, Int?) + var y: Int? + var z: Int + var w: ((Int?, (), Int?), (Int?, Int?)) +} + // Protocol extensions protocol ProtocolToExtend { diff --git a/test/SILGen/implicit_property_initializers.swift b/test/SILGen/implicit_property_initializers.swift new file mode 100644 index 0000000000000..6a4f4dcc3d8db --- /dev/null +++ b/test/SILGen/implicit_property_initializers.swift @@ -0,0 +1,82 @@ +// RUN: %target-swift-emit-silgen -module-name implicit_property_initializers -Xllvm -sil-full-demangle -enable-testing %s | %FileCheck %s + +// CHECK: struct HasDefaultTupleOfNils { +// CHECK: @_hasStorage @_hasInitialValue var x: (Int?, Int?) +// CHECK: @_hasStorage @_hasInitialValue var y: Int? +// CHECK: @_hasStorage var z: Int +// CHECK: @_hasStorage @_hasInitialValue var w: ((Int?, (), Int?), (Int?, Int?)) +// CHECK: init(x: (Int?, Int?) = (nil, nil), +// CHECK-SAME: y: Int? = nil, +// CHECK-SAME: z: Int, +// CHECK-SAME: w: ((Int?, (), Int?), (Int?, Int?)) = ((nil, (), nil), (nil, nil))) +// CHECK: } +struct HasDefaultTupleOfNils { + var x: (Int?, Int?) + var y: Int? + var z: Int + var w: ((Int?, (), Int?), (Int?, Int?)) +} + +// The default value initializer for 'x' should have type (Optional, Optional) + +// CHECK: sil [transparent] [ossa] @$[[X_VALUE_INIT:s30implicit_property_initializers21HasDefaultTupleOfNilsV1xSiSg_AEtvpfi]] : $@convention(thin) () -> (Optional, Optional) { +// CHECK: bb0: +// CHECK: %0 = enum $Optional, #Optional.none!enumelt +// CHECK: %1 = enum $Optional, #Optional.none!enumelt +// CHECK: %2 = tuple (%0 : $Optional, %1 : $Optional) +// CHECK: return %2 : $(Optional, Optional) +// CHECK: } + +// The default value initializer for 'y' should have type Optional + +//CHECK: sil [transparent] [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1ySiSgvpfi : $@convention(thin) () -> Optional { +//CHECK: bb0: +//CHECK: %0 = enum $Optional, #Optional.none!enumelt // user: %1 +//CHECK: return %0 : $Optional // id: %1 +//CHECK: } + +// There should not be a default value initializer for 'z'. + +// CHECK-NOT: @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1zSivpfi + +// The default value initializer for 'w' should flatten to type (Optional, Optional, Optional, Optional) + +//CHECK: sil [transparent] [ossa] @$[[W_VALUE_INIT:s30implicit_property_initializers21HasDefaultTupleOfNilsV1wSiSg_ytAEt_AE_AEttvpfi]] : $@convention(thin) () -> (Optional, Optional, Optional, Optional) { +//CHECK: bb0: +//CHECK: %0 = enum $Optional, #Optional.none!enumelt // user: %4 +//CHECK: %1 = enum $Optional, #Optional.none!enumelt // user: %4 +//CHECK: %2 = enum $Optional, #Optional.none!enumelt // user: %4 +//CHECK: %3 = enum $Optional, #Optional.none!enumelt // user: %4 +//CHECK: %4 = tuple (%0 : $Optional, %1 : $Optional, %2 : $Optional, %3 : $Optional) // user: %5 +//CHECK: return %4 : $(Optional, Optional, Optional, Optional) // id: %5 +//CHECK: } + +// The default arg generator for 'x' inside the memberwise init should have type (Optional, Optional) + +// CHECK: sil [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA_ : $@convention(thin) () -> (Optional, Optional) { +// CHECK: bb0: +// CHECK: %0 = function_ref @$[[X_VALUE_INIT]] : $@convention(thin) () -> (Optional, Optional) // user: %1 +// CHECK: %1 = apply %0() : $@convention(thin) () -> (Optional, Optional) +// CHECK: (%2, %3) = destructure_tuple %1 : $(Optional, Optional) +// CHECK: %4 = tuple (%2 : $Optional, %3 : $Optional) +// CHECK: return %4 : $(Optional, Optional) +// CHECK: } + +// There should not be a default arg generator for 'y' because it's just a nil literal and clients construct it directly. + +// CHECK-NOT: @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA0_ + +// There should not be a default arg generator for 'z' + +// CHECK-NOT: @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA1_ + +// The default arg generator for 'w' should flatten to type (Optional, Optional, Optional, Optional) + +// CHECK: sil [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA2_ : $@convention(thin) () -> (Optional, Optional, Optional, Optional) { +// CHECK: bb0: +// CHECK: %0 = function_ref @$[[W_VALUE_INIT]] : $@convention(thin) () -> (Optional, Optional, Optional, Optional) +// CHECK: %1 = apply %0() : $@convention(thin) () -> (Optional, Optional, Optional, Optional) +// CHECK: (%2, %3, %4, %5) = destructure_tuple %1 : $(Optional, Optional, Optional, Optional) +// CHECK: %6 = tuple (%2 : $Optional, %3 : $Optional, %4 : $Optional, %5 : $Optional) +// CHECK: return %6 : $(Optional, Optional, Optional, Optional) +// CHECK: }