From a98de1ba1ce58b6a338d723c9347ab6bb8900338 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 1 Dec 2015 12:28:41 -0800 Subject: [PATCH 1/3] Add an Optional metadata kind for runtime casts. Reuses the enum metadata layout and builder because most of the logic is also required for Optional (generic arg and payload). We may want to optimize this at some point (Optional doesn't have a Parent), but I don't see much opportunity. Note that with this approach there will be no change in metadata layout. Changing the kind still breaks the ABI of course. Also leaves the MirrorData summary string as "(Enum Value)". We should consider changing it. --- include/swift/ABI/MetadataKind.def | 3 +++ include/swift/Runtime/Metadata.h | 5 ++++- lib/IRGen/GenMeta.cpp | 3 ++- stdlib/public/runtime/Casting.cpp | 23 +++++++++++++++++++++++ stdlib/public/runtime/ErrorObject.mm | 1 + stdlib/public/runtime/Metadata.cpp | 2 ++ stdlib/public/runtime/Reflection.mm | 3 +++ stdlib/public/runtime/SwiftObject.mm | 5 ++++- 8 files changed, 42 insertions(+), 3 deletions(-) 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/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/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/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index ec5404f1fe835..7091ec3b3ae23 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: @@ -2020,6 +2039,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. @@ -2069,6 +2089,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, case MetadataKind::Struct: case MetadataKind::Enum: + case MetadataKind::Optional: switch (srcType->getKind()) { case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: @@ -2092,6 +2113,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, } case MetadataKind::Enum: + case MetadataKind::Optional: case MetadataKind::Existential: case MetadataKind::ExistentialMetatype: case MetadataKind::Function: @@ -2970,6 +2992,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: 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); From a8a49afd9ea0f0743040df921c60887b415cd2f8 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 8 Dec 2015 10:19:31 -0800 Subject: [PATCH 2/3] Add Builtin.isOptional. There was previously no way to detect a type that is nominally Optional at runtime. The standard library, namely OutputStream, needs to handle Optionals specially in order to cirumvent conversion to the Optional's wrapped type. This should be done with conditional conformance, but until that feature is available, Builtin.isOptional will serve as a useful crutch. --- include/swift/AST/Builtins.def | 5 +++++ lib/AST/Builtins.cpp | 10 ++++++++++ lib/IRGen/RuntimeFunctions.def | 7 +++++++ stdlib/public/core/Builtin.swift | 8 ++++++++ stdlib/public/runtime/Casting.cpp | 4 ++++ test/1_stdlib/Builtins.swift | 10 ++++++++++ 6 files changed, 44 insertions(+) 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/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/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/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 7091ec3b3ae23..d42b65abc8faa 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -3172,3 +3172,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/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() From 35cb1afab8b5009604cd4405375a3931d9f53f01 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 9 Dec 2015 16:07:31 -0800 Subject: [PATCH 3/3] Fix dynamic runtime casts of Optionals. Fixes Runtime dynamic casts... This makes runtime dynamic casts consistent with language rules, and consequently makes specialization of generic code consistent with an equivalent nongeneric implementation. The runtime now supports casts from Optional to U. Naturally the cast fails on nil source, but otherwise succeeds if T is convertible to U. When casting T to Optional the runtime succeeds whenever T is convertible to U and simply wraps the result in an Optional. To greatly simplify the runtime, I am assuming that target-type-specific runtime cast entry points (e.g. swift_dynamicCastClass) are never invoked with an optional source. This assumption is valid for the following reasons. At the language level optionals must be unwrapped before downcasting (via as[?!]), so we only need to worry about SIL and IR lowering. This implementation assumes (with asserts) that: - SIL promotion from an address cast to a value casts should only happen when the source is nonoptional. Handling optional unwrapping in SIL would be too complicated because we need to check for Optional's own conformances. (I added a test case to ensure this promotion does not happen). This is not an issue for unchecked_ref_cast, which implicitly unwraps optionals, so we can promote those! - IRGen lowers unchecked_ref_cast (Builtin.castReference) directly to a bitcast (will be caught by asserts). - IRGen continues to emit the generic dynamicCast entry point for address-casts (will be caught by asserts). --- .../core/ImplicitlyUnwrappedOptional.swift | 11 ++ stdlib/public/core/OutputStream.swift | 10 ++ stdlib/public/runtime/Casting.cpp | 47 ++++++- test/1_stdlib/Optional.swift | 126 ++++++++++++++++++ ...pecialize_unconditional_checked_cast.swift | 15 +++ 5 files changed, 208 insertions(+), 1 deletion(-) 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 d42b65abc8faa..d7584a83e32b5 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -1990,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: @@ -2089,7 +2135,6 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, case MetadataKind::Struct: case MetadataKind::Enum: - case MetadataKind::Optional: switch (srcType->getKind()) { case MetadataKind::Class: case MetadataKind::ObjCClassWrapper: 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