diff --git a/include/swift/ABI/MetadataKind.def b/include/swift/ABI/MetadataKind.def index 1e8d454288756..9bef04dfd9c92 100644 --- a/include/swift/ABI/MetadataKind.def +++ b/include/swift/ABI/MetadataKind.def @@ -47,6 +47,9 @@ NOMINALTYPEMETADATAKIND(Struct, 1) /// If we add reference enums, that needs to go here. NOMINALTYPEMETADATAKIND(Enum, 2) +/// An optional type. +NOMINALTYPEMETADATAKIND(Optional, 3) + /// A type whose value is not exposed in the metadata system. METADATAKIND(Opaque, 8) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index a6592e5eb4f5f..9a01ac1f3b14c 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -353,6 +353,11 @@ BUILTIN_RUNTIME_CALL(UnexpectedError, "unexpectedError", "") /// errorInMain: ErrorType -> () BUILTIN_RUNTIME_CALL(ErrorInMain, "errorInMain", "") +/// IsOptionalType : T.Type -> Bool +/// This builtin takes a metatype and returns true if the metatype's +/// nominal type is Optional. +BUILTIN_RUNTIME_CALL(IsOptionalType, "isOptional", "") + #undef BUILTIN_RUNTIME_CALL // BUILTIN_MISC_OPERATION - Miscellaneous operations without a unifying class. diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 11173047573b0..7adc3931e4f3b 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -1003,6 +1003,7 @@ struct Metadata { case MetadataKind::Function: case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Existential: @@ -1029,6 +1030,7 @@ struct Metadata { case MetadataKind::ForeignClass: case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -1746,7 +1748,8 @@ struct EnumMetadata : public Metadata { } static bool classof(const Metadata *metadata) { - return metadata->getKind() == MetadataKind::Enum; + return metadata->getKind() == MetadataKind::Enum + || metadata->getKind() == MetadataKind::Optional; } }; diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index b85f398a1f4e5..d77f7c4a89923 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -683,6 +683,13 @@ static ValueDecl *getIsPODOperation(ASTContext &Context, Identifier Id) { return builder.build(Id); } +static ValueDecl *getIsOptionalOperation(ASTContext &Context, Identifier Id) { + GenericSignatureBuilder builder(Context); + builder.addParameter(makeMetatype(makeGenericParam())); + builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context))); + return builder.build(Id); +} + static ValueDecl *getAllocOperation(ASTContext &Context, Identifier Id) { Type PtrSizeTy = BuiltinIntegerType::getWordType(Context); TupleTypeElt ArgElts[] = { PtrSizeTy, PtrSizeTy }; @@ -1582,6 +1589,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::IsPOD: return getIsPODOperation(Context, Id); + case BuiltinValueKind::IsOptionalType: + return getIsOptionalOperation(Context, Id); + case BuiltinValueKind::AllocRaw: return getAllocOperation(Context, Id); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 4e41759064a8f..8682c9cecac9d 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -4525,7 +4525,8 @@ class EnumMetadataBuilderBase : super(IGM, theEnum) {} void addMetadataFlags() { - addWord(getMetadataKind(IGM, MetadataKind::Enum)); + addWord(getMetadataKind(IGM, Target->classifyAsOptionalType() + ? MetadataKind::Optional : MetadataKind::Enum)); } void addNominalTypeDescriptor() { diff --git a/lib/IRGen/RuntimeFunctions.def b/lib/IRGen/RuntimeFunctions.def index 39b8b9a0c7ea4..ab5371f6499fb 100644 --- a/lib/IRGen/RuntimeFunctions.def +++ b/lib/IRGen/RuntimeFunctions.def @@ -853,6 +853,13 @@ FUNCTION(IsClassType, ARGS(TypeMetadataPtrTy), ATTRS(NoUnwind, ReadNone)) +// bool swift_isOptionalType(type*); +FUNCTION(IsOptionalType, + swift_isOptionalType, RuntimeCC, + RETURNS(Int1Ty), + ARGS(TypeMetadataPtrTy), + ATTRS(NoUnwind, ReadNone)) + // void swift_once(swift_once_t *predicate, // void (*function_code)(RefCounted*)); FUNCTION(Once, swift_once, RuntimeCC, diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index 10ee4bbbf3618..86d156d3c1d14 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -568,3 +568,11 @@ public // @testable func _isPOD(type: T.Type) -> Bool { return Bool(Builtin.ispod(type)) } + +/// Return true if type is nominally an Optional type. +@_transparent +@warn_unused_result +public // @testable +func _isOptional(type: T.Type) -> Bool { + return Bool(Builtin.isOptional(type)) +} diff --git a/stdlib/public/core/ImplicitlyUnwrappedOptional.swift b/stdlib/public/core/ImplicitlyUnwrappedOptional.swift index f1ab6ea1af68f..80ca7acf89382 100644 --- a/stdlib/public/core/ImplicitlyUnwrappedOptional.swift +++ b/stdlib/public/core/ImplicitlyUnwrappedOptional.swift @@ -95,6 +95,17 @@ extension ImplicitlyUnwrappedOptional : CustomStringConvertible { } } +/// Directly conform to CustomDebugStringConvertible to support +/// optional printing. Implementation of that feature relies on +/// _isOptional thus cannot distinguish ImplicitlyUnwrappedOptional +/// from Optional. When conditional conformance is available, this +/// outright conformance can be removed. +extension ImplicitlyUnwrappedOptional : CustomDebugStringConvertible { + public var debugDescription: String { + return description + } +} + @_transparent @warn_unused_result public // COMPILER_INTRINSIC diff --git a/stdlib/public/core/OutputStream.swift b/stdlib/public/core/OutputStream.swift index cf68151642b25..4043eeb105c2a 100644 --- a/stdlib/public/core/OutputStream.swift +++ b/stdlib/public/core/OutputStream.swift @@ -165,6 +165,16 @@ internal func _adHocPrint( internal func _print_unlocked( value: T, inout _ target: TargetStream ) { + // Optional has no representation suitable for display; therefore, + // values of optional type should be printed as a debug + // string. Check for Optional first, before checking protocol + // conformance below, because an Optional value is convertible to a + // protocol if its wrapped type conforms to that protocol. + if _isOptional(value.dynamicType) { + let debugPrintable = value as! CustomDebugStringConvertible + debugPrintable.debugDescription.writeTo(&target) + return + } if case let streamableObject as Streamable = value { streamableObject.writeTo(&target) return diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index ec5404f1fe835..d7584a83e32b5 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -247,6 +247,7 @@ static void _buildNameForMetadata(const Metadata *type, result); } case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Struct: { auto structType = static_cast(type); return _buildNominalTypeName(structType->Description, @@ -574,6 +575,7 @@ static bool _conformsToProtocol(const OpaqueValue *value, case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -634,6 +636,7 @@ static bool _conformsToProtocol(const OpaqueValue *value, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -736,6 +739,7 @@ static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type, case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -797,6 +801,7 @@ static void deallocateDynamicValue(OpaqueValue *value, const Metadata *type) { case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -822,6 +827,7 @@ swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype) { // Other kinds of metadata don't cast to AnyObject. case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -852,6 +858,7 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype) { // Other kinds of metadata don't cast to AnyObject. case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -939,6 +946,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: #if SWIFT_OBJC_INTEROP // If the source type is bridged to Objective-C, try to bridge. if (auto srcBridgeWitness = findBridgeWitness(srcDynamicType)) { @@ -1111,6 +1119,7 @@ swift::swift_dynamicCastUnknownClass(const void *object, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1166,6 +1175,7 @@ swift::swift_dynamicCastUnknownClassUnconditional(const void *object, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1225,6 +1235,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1255,6 +1266,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1270,6 +1282,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1332,6 +1345,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1363,6 +1377,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1377,6 +1392,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType, case MetadataKind::ErrorObject: case MetadataKind::Metatype: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1715,6 +1731,7 @@ static bool _dynamicCastToMetatype(OpaqueValue *dest, case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1885,6 +1902,7 @@ static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest, case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Struct: case MetadataKind::Tuple: @@ -1947,6 +1965,7 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, case MetadataKind::Class: case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::ObjCClassWrapper: case MetadataKind::ForeignClass: case MetadataKind::ExistentialMetatype: @@ -1971,13 +1990,59 @@ static id dynamicCastValueToNSError(OpaqueValue *src, } #endif +static bool canCastToExistential(OpaqueValue *dest, OpaqueValue *src, + const Metadata *srcType, + const Metadata *targetType) { + if (targetType->getKind() != MetadataKind::Existential) + return false; + + return _dynamicCastToExistential(dest, src, srcType, + cast(targetType), + DynamicCastFlags::Default); +} + /// Perform a dynamic cast to an arbitrary type. bool swift::swift_dynamicCast(OpaqueValue *dest, OpaqueValue *src, const Metadata *srcType, const Metadata *targetType, DynamicCastFlags flags) { + // Check if the cast source is Optional and the target is not an existential + // that Optional conforms to. Unwrap one level of Optional and continue. + if (srcType->getKind() == MetadataKind::Optional + && !canCastToExistential(dest, src, srcType, targetType)) { + const Metadata *payloadType = + cast(srcType)->getGenericArgs()[0]; + int enumCase = + swift_getEnumCaseSinglePayload(src, payloadType, 1 /*emptyCases=*/); + if (enumCase != -1) { + // Allow Optional.None -> Optional.None + if (targetType->getKind() != MetadataKind::Optional) + return _fail(src, srcType, targetType, flags); + // Inject the .None tag + swift_storeEnumTagSinglePayload(dest, payloadType, enumCase, + 1 /*emptyCases=*/); + return _succeed(dest, src, srcType, flags); + } + // .Some + // Single payload enums are guaranteed layout compatible with their + // payload. Only the source's payload needs to be taken or destroyed. + srcType = payloadType; + } + switch (targetType->getKind()) { + // Handle wrapping an Optional target. + case MetadataKind::Optional: { + // Recursively cast into the layout compatible payload area. + const Metadata *payloadType = + cast(targetType)->getGenericArgs()[0]; + if (swift_dynamicCast(dest, src, srcType, payloadType, flags)) { + swift_storeEnumTagSinglePayload(dest, payloadType, -1 /*case*/, + 1 /*emptyCases*/); + return true; + } + return false; + } // Casts to class type. case MetadataKind::Class: @@ -2020,6 +2085,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, } case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Struct: { #if SWIFT_OBJC_INTEROP // If the source type is bridged to Objective-C, try to bridge. @@ -2092,6 +2158,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, } case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Existential: case MetadataKind::ExistentialMetatype: case MetadataKind::Function: @@ -2970,6 +3037,7 @@ findBridgeWitness(const Metadata *T) { case MetadataKind::Class: case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -3149,3 +3217,7 @@ extern "C" const Metadata *_swift_getSuperclass_nonNull( extern "C" bool swift_isClassType(const Metadata *type) { return Metadata::isAnyKindOfClass(type->getKind()); } + +extern "C" bool swift_isOptionalType(const Metadata *type) { + return type->getKind() == MetadataKind::Optional; +} diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index 8a302f4f282c9..2902d0d96540b 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -380,6 +380,7 @@ static id _swift_bridgeErrorTypeToNSError_(SwiftError *errorObject) { } // Not a class. case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Existential: case MetadataKind::ExistentialMetatype: case MetadataKind::Function: diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 5c905fb792a17..e4c358cdd1c4f 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2323,6 +2323,7 @@ Metadata::getNominalTypeDescriptor() const { } case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: return static_cast(this)->Description; case MetadataKind::ForeignClass: case MetadataKind::Opaque: @@ -2362,6 +2363,7 @@ Metadata::getClassObject() const { // Other kinds of types don't have class objects. case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::ForeignClass: case MetadataKind::Opaque: case MetadataKind::Tuple: diff --git a/stdlib/public/runtime/Reflection.mm b/stdlib/public/runtime/Reflection.mm index dcc722a6f4bb6..c0019d586d76b 100644 --- a/stdlib/public/runtime/Reflection.mm +++ b/stdlib/public/runtime/Reflection.mm @@ -309,6 +309,7 @@ void swift_MagicMirrorData_summary(const Metadata *T, String *result) { new (result) String("(Struct)"); break; case MetadataKind::Enum: + case MetadataKind::Optional: new (result) String("(Enum Value)"); break; case MetadataKind::Opaque: @@ -381,6 +382,7 @@ intptr_t swift_TupleMirror_count(HeapObject *owner, case MetadataKind::Class: case MetadataKind::Opaque: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Function: case MetadataKind::Metatype: break; @@ -1149,6 +1151,7 @@ static Mirror ObjC_getMirrorForSuperclass(Class sup, T, &StructMirrorMetadata, &StructMirrorWitnessTable); case MetadataKind::Enum: + case MetadataKind::Optional: return std::make_tuple( T, &EnumMirrorMetadata, &EnumMirrorWitnessTable); diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 89627ef84f441..6f6a4e6dce203 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -1059,6 +1059,7 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) { // Other kinds of type can never conform to ObjC protocols. case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -1106,6 +1107,7 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) { // Other kinds of type can never conform to ObjC protocols. case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Opaque: case MetadataKind::Tuple: case MetadataKind::Function: @@ -1237,7 +1239,8 @@ static void doWeakDestroy(WeakReference *addr, bool valueIsNative) { return _buildDemanglingForNominalType(Node::Kind::BoundGenericClass, type, classType->getDescription()); } - case MetadataKind::Enum: { + case MetadataKind::Enum: + case MetadataKind::Optional: { auto structType = static_cast(type); return _buildDemanglingForNominalType(Node::Kind::BoundGenericEnum, type, structType->Description); diff --git a/test/1_stdlib/Builtins.swift b/test/1_stdlib/Builtins.swift index d074460077f77..8446814421732 100644 --- a/test/1_stdlib/Builtins.swift +++ b/test/1_stdlib/Builtins.swift @@ -277,4 +277,14 @@ tests.test("_isPOD") { expectFalse(_isPOD(P.self)) } +tests.test("_isOptional") { + expectTrue(_isOptional(Optional.self)) + expectTrue(_isOptional(Optional.self)) + expectTrue(_isOptional(Optional

.self)) + expectTrue(_isOptional(ImplicitlyUnwrappedOptional

.self)) + expectFalse(_isOptional(Int.self)) + expectFalse(_isOptional(X.self)) + expectFalse(_isOptional(P.self)) +} + runAllTests() diff --git a/test/1_stdlib/Optional.swift b/test/1_stdlib/Optional.swift index 8b042047d284d..c1a922568de6b 100644 --- a/test/1_stdlib/Optional.swift +++ b/test/1_stdlib/Optional.swift @@ -237,4 +237,130 @@ OptionalTests.test("flatMap") { expectEmpty((3 as Int32?).flatMap(half)) } +@inline(never) +func anyToAny(a: T, _ : U.Type) -> U { + return a as! U +} +@inline(never) +func anyToAnyOrNil(a: T, _ : U.Type) -> U? { + return a as? U +} +func canGenericCast(a: T, _ ty : U.Type) -> Bool { + return anyToAnyOrNil(a, ty) != nil +} + +OptionalTests.test("Casting Optional") { + let x = C() + let sx : C? = x + let nx : C? = nil + expectTrue(anyToAny(x, Optional.self)! === x) + expectTrue(anyToAny(sx, C.self) === x) + expectTrue(anyToAny(sx, Optional.self)! === x) + + expectTrue(anyToAny(nx, Optional.self) == nil) + expectTrue(anyToAnyOrNil(nx, C.self) == nil) + + let i = Int.max + let si : Int? = Int.max + let ni : Int? = nil + expectEqual(anyToAny(i, Optional.self)!, Int.max) + expectEqual(anyToAny(si, Int.self), Int.max) + expectEqual(anyToAny(si, Optional.self)!, Int.max) + + expectTrue(anyToAny(ni, Optional.self) == nil) + expectTrue(anyToAnyOrNil(ni, Int.self) == nil) + + let ssx : C?? = sx + expectTrue(anyToAny(ssx, Optional.self)! === x) + expectTrue(anyToAny(x, Optional>.self)!! === x) + expectTrue(anyToAnyOrNil(ni, Int.self) == nil) +} + +OptionalTests.test("Casting Optional Traps") { + let nx : C? = nil + expectCrashLater() + anyToAny(nx, Int.self) +} + +class TestNoString {} +class TestString : CustomStringConvertible, CustomDebugStringConvertible { + var description: String { + return "AString" + } + var debugDescription: String { + return "XString" + } +} +class TestStream : Streamable { + func writeTo(inout target: Target) { + target.write("AStream") + } +} + +func debugPrintStr(a: T) -> String { + var s = "" + debugPrint(a, terminator: "", toStream: &s) + return s +} +// Optional should not conform to output stream protocols itself, but is +// convertible to them if its wrapped type is. +// Furthermore, printing an Optional should always print the debug +// description regardless of whether the wrapper type conforms to an +// output stream protocol. +OptionalTests.test("Optional OutputStream") { + let optNoString : TestNoString? = TestNoString() + expectFalse(optNoString is CustomStringConvertible) + expectFalse(canGenericCast(optNoString, CustomStringConvertible.self)) + expectFalse(optNoString is Streamable) + expectFalse(canGenericCast(optNoString, Streamable.self)) + expectTrue(optNoString is CustomDebugStringConvertible) + expectTrue(canGenericCast(optNoString, CustomDebugStringConvertible.self)) + expectEqual(String(optNoString), "Optional(main.TestNoString)") + expectEqual(debugPrintStr(optNoString), "Optional(main.TestNoString)") + + let iouNoString : TestNoString! = TestNoString() + // IOU directly conforms to CustomStringConvertible. + // Disabled pending SR-164 + // expectTrue(iouNoString is CustomStringConvertible) + expectTrue(canGenericCast(iouNoString, CustomStringConvertible.self)) + expectFalse(iouNoString is Streamable) + expectFalse(canGenericCast(iouNoString, Streamable.self)) + // CustomDebugStringConvertible conformance is a temporary hack. + // Disabled pending SR-164 + // expectTrue(iouNoString is CustomDebugStringConvertible) + expectTrue(canGenericCast(iouNoString, CustomDebugStringConvertible.self)) + expectEqual(String(iouNoString), "main.TestNoString") + expectEqual(debugPrintStr(iouNoString), "main.TestNoString") + + let optString : TestString? = TestString() + expectTrue(optString is CustomStringConvertible) + expectTrue(canGenericCast(optString, CustomStringConvertible.self)) + expectTrue(optString is CustomDebugStringConvertible) + expectTrue(canGenericCast(optString, CustomDebugStringConvertible.self)) + expectEqual(String(TestString()), "AString") + expectEqual(String(optString), "Optional(XString)") + expectEqual(debugPrintStr(optString), "Optional(XString)") + + let iouString : TestString! = TestString() + expectTrue(iouString is CustomStringConvertible) + expectTrue(canGenericCast(iouString, CustomStringConvertible.self)) + // CustomDebugStringConvertible conformance is a temporary hack. + expectTrue(iouString is CustomDebugStringConvertible) + expectTrue(canGenericCast(iouString, CustomDebugStringConvertible.self)) + expectEqual(String(iouString), "AString") + // FIXME: Ideally the debug output would be "XString", but a reasonable + // implemention of that behavior requires conditional conformance. + // (directly invoking debugPrint(Any) already works correctly). + expectEqual(debugPrintStr(iouString), "AString") + + let optStream : TestStream? = TestStream() + expectTrue(optStream is Streamable) + expectTrue(canGenericCast(optStream, Streamable.self)) + expectTrue(optStream is CustomDebugStringConvertible) + expectTrue(canGenericCast(optStream, CustomDebugStringConvertible.self)) + expectEqual(String(TestStream()), "AStream") + expectEqual(String(optStream), "Optional(AStream)") + expectEqual(debugPrintStr(optStream), "Optional(AStream)") +} + runAllTests() diff --git a/test/SILPasses/specialize_unconditional_checked_cast.swift b/test/SILPasses/specialize_unconditional_checked_cast.swift index 7a09a6acea2c3..3aa206c4ef3e3 100644 --- a/test/SILPasses/specialize_unconditional_checked_cast.swift +++ b/test/SILPasses/specialize_unconditional_checked_cast.swift @@ -357,6 +357,21 @@ ExistentialToArchetype(o: o, t: c) ExistentialToArchetype(o: o, t: b) ExistentialToArchetype(o: o, t: o) +// Ensure that a downcast from an Optional source is not promoted to a +// value cast. We could do the promotion, but the optimizer would need +// to insert the Optional unwrapping logic before the cast. +// +// CHECK-LABEL: sil shared [noinline] @_TTSg5GSqC37specialize_unconditional_checked_cast1C__CS_1D___TF37specialize_unconditional_checked_cast15genericDownCastu0_rFTxMq__q_ : $@convention(thin) (@out D, @in Optional, @thick D.Type) -> () { +// CHECK: unconditional_checked_cast_addr take_always Optional in %1 : $*Optional to D in %0 : $*D +@inline(never) +public func genericDownCast(a: T, _ : U.Type) -> U { + return a as! U +} + +public func callGenericDownCast(c: C?) -> D { + return genericDownCast(c, D.self) +} + //order: -5 // x -> y where y is a class but x is not. // CHECK-LABEL: sil shared [noinline] @_TTSf4d___TTSg5C37specialize_unconditional_checked_cast1C___TF37specialize_unconditional_checked_cast31ArchetypeToConcreteConvertUInt8