Skip to content

Commit

Permalink
Merge pull request #26506 from harlanhaskins/extremely-default-and-in…
Browse files Browse the repository at this point in the history
…credibly-nil

[AST] Handle printing default values for tuples of nils
  • Loading branch information
harlanhaskins committed Aug 7, 2019
2 parents 206994c + 7fd8649 commit 4277f11
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 1 deletion.
58 changes: 57 additions & 1 deletion lib/AST/Decl.cpp
Expand Up @@ -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<TupleType>(), 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<TupleType>();
if (!tuple) return false;
for (auto elt : tuple->getElementTypes())
if (!elt->getOptionalObjectType() && !isTupleOfOptionals(elt))
return false;
return true;
}

StringRef
ParamDecl::getDefaultValueStringRepresentation(
SmallVectorImpl<char> &scratch) const {
Expand Down Expand Up @@ -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<TupleType>(), os);
return os.str();
}
return "<<empty>>";
}

return extractInlinableText(getASTContext().SourceMgr,
var->getParentInitializer(),
init,
scratch);
}
case DefaultArgumentKind::Inherited: return "super";
Expand Down
14 changes: 14 additions & 0 deletions test/IDE/print_ast_tc_decls.swift
Expand Up @@ -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 {
Expand Down
82 changes: 82 additions & 0 deletions 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<Int>, Optional<Int>)

// CHECK: sil [transparent] [ossa] @$[[X_VALUE_INIT:s30implicit_property_initializers21HasDefaultTupleOfNilsV1xSiSg_AEtvpfi]] : $@convention(thin) () -> (Optional<Int>, Optional<Int>) {
// CHECK: bb0:
// CHECK: %0 = enum $Optional<Int>, #Optional.none!enumelt
// CHECK: %1 = enum $Optional<Int>, #Optional.none!enumelt
// CHECK: %2 = tuple (%0 : $Optional<Int>, %1 : $Optional<Int>)
// CHECK: return %2 : $(Optional<Int>, Optional<Int>)
// CHECK: }

// The default value initializer for 'y' should have type Optional<Int>

//CHECK: sil [transparent] [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1ySiSgvpfi : $@convention(thin) () -> Optional<Int> {
//CHECK: bb0:
//CHECK: %0 = enum $Optional<Int>, #Optional.none!enumelt // user: %1
//CHECK: return %0 : $Optional<Int> // 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<Int>, Optional<Int>, Optional<Int>, Optional<Int>)

//CHECK: sil [transparent] [ossa] @$[[W_VALUE_INIT:s30implicit_property_initializers21HasDefaultTupleOfNilsV1wSiSg_ytAEt_AE_AEttvpfi]] : $@convention(thin) () -> (Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>) {
//CHECK: bb0:
//CHECK: %0 = enum $Optional<Int>, #Optional.none!enumelt // user: %4
//CHECK: %1 = enum $Optional<Int>, #Optional.none!enumelt // user: %4
//CHECK: %2 = enum $Optional<Int>, #Optional.none!enumelt // user: %4
//CHECK: %3 = enum $Optional<Int>, #Optional.none!enumelt // user: %4
//CHECK: %4 = tuple (%0 : $Optional<Int>, %1 : $Optional<Int>, %2 : $Optional<Int>, %3 : $Optional<Int>) // user: %5
//CHECK: return %4 : $(Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>) // id: %5
//CHECK: }

// The default arg generator for 'x' inside the memberwise init should have type (Optional<Int>, Optional<Int>)

// CHECK: sil [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA_ : $@convention(thin) () -> (Optional<Int>, Optional<Int>) {
// CHECK: bb0:
// CHECK: %0 = function_ref @$[[X_VALUE_INIT]] : $@convention(thin) () -> (Optional<Int>, Optional<Int>) // user: %1
// CHECK: %1 = apply %0() : $@convention(thin) () -> (Optional<Int>, Optional<Int>)
// CHECK: (%2, %3) = destructure_tuple %1 : $(Optional<Int>, Optional<Int>)
// CHECK: %4 = tuple (%2 : $Optional<Int>, %3 : $Optional<Int>)
// CHECK: return %4 : $(Optional<Int>, Optional<Int>)
// 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<Int>, Optional<Int>, Optional<Int>, Optional<Int>)

// CHECK: sil [ossa] @$s30implicit_property_initializers21HasDefaultTupleOfNilsV1x1y1z1wACSiSg_AHt_AHSiAH_ytAHt_AH_AHtttcfcfA2_ : $@convention(thin) () -> (Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>) {
// CHECK: bb0:
// CHECK: %0 = function_ref @$[[W_VALUE_INIT]] : $@convention(thin) () -> (Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>)
// CHECK: %1 = apply %0() : $@convention(thin) () -> (Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>)
// CHECK: (%2, %3, %4, %5) = destructure_tuple %1 : $(Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>)
// CHECK: %6 = tuple (%2 : $Optional<Int>, %3 : $Optional<Int>, %4 : $Optional<Int>, %5 : $Optional<Int>)
// CHECK: return %6 : $(Optional<Int>, Optional<Int>, Optional<Int>, Optional<Int>)
// CHECK: }

0 comments on commit 4277f11

Please sign in to comment.