Skip to content

Commit 405f9f5

Browse files
committed
Merge pull request #204 from atrick/optionalcast
Runtime optional casts.
2 parents 8dc7ae5 + 35cb1af commit 405f9f5

17 files changed

+293
-3
lines changed

include/swift/ABI/MetadataKind.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ NOMINALTYPEMETADATAKIND(Struct, 1)
4747
/// If we add reference enums, that needs to go here.
4848
NOMINALTYPEMETADATAKIND(Enum, 2)
4949

50+
/// An optional type.
51+
NOMINALTYPEMETADATAKIND(Optional, 3)
52+
5053
/// A type whose value is not exposed in the metadata system.
5154
METADATAKIND(Opaque, 8)
5255

include/swift/AST/Builtins.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,11 @@ BUILTIN_RUNTIME_CALL(UnexpectedError, "unexpectedError", "")
353353
/// errorInMain: ErrorType -> ()
354354
BUILTIN_RUNTIME_CALL(ErrorInMain, "errorInMain", "")
355355

356+
/// IsOptionalType : T.Type -> Bool
357+
/// This builtin takes a metatype and returns true if the metatype's
358+
/// nominal type is Optional.
359+
BUILTIN_RUNTIME_CALL(IsOptionalType, "isOptional", "")
360+
356361
#undef BUILTIN_RUNTIME_CALL
357362

358363
// BUILTIN_MISC_OPERATION - Miscellaneous operations without a unifying class.

include/swift/Runtime/Metadata.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ struct Metadata {
10031003
case MetadataKind::Function:
10041004
case MetadataKind::Struct:
10051005
case MetadataKind::Enum:
1006+
case MetadataKind::Optional:
10061007
case MetadataKind::Opaque:
10071008
case MetadataKind::Tuple:
10081009
case MetadataKind::Existential:
@@ -1029,6 +1030,7 @@ struct Metadata {
10291030
case MetadataKind::ForeignClass:
10301031
case MetadataKind::Struct:
10311032
case MetadataKind::Enum:
1033+
case MetadataKind::Optional:
10321034
case MetadataKind::Opaque:
10331035
case MetadataKind::Tuple:
10341036
case MetadataKind::Function:
@@ -1746,7 +1748,8 @@ struct EnumMetadata : public Metadata {
17461748
}
17471749

17481750
static bool classof(const Metadata *metadata) {
1749-
return metadata->getKind() == MetadataKind::Enum;
1751+
return metadata->getKind() == MetadataKind::Enum
1752+
|| metadata->getKind() == MetadataKind::Optional;
17501753
}
17511754
};
17521755

lib/AST/Builtins.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,13 @@ static ValueDecl *getIsPODOperation(ASTContext &Context, Identifier Id) {
683683
return builder.build(Id);
684684
}
685685

686+
static ValueDecl *getIsOptionalOperation(ASTContext &Context, Identifier Id) {
687+
GenericSignatureBuilder builder(Context);
688+
builder.addParameter(makeMetatype(makeGenericParam()));
689+
builder.setResult(makeConcrete(BuiltinIntegerType::get(1,Context)));
690+
return builder.build(Id);
691+
}
692+
686693
static ValueDecl *getAllocOperation(ASTContext &Context, Identifier Id) {
687694
Type PtrSizeTy = BuiltinIntegerType::getWordType(Context);
688695
TupleTypeElt ArgElts[] = { PtrSizeTy, PtrSizeTy };
@@ -1582,6 +1589,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
15821589
case BuiltinValueKind::IsPOD:
15831590
return getIsPODOperation(Context, Id);
15841591

1592+
case BuiltinValueKind::IsOptionalType:
1593+
return getIsOptionalOperation(Context, Id);
1594+
15851595
case BuiltinValueKind::AllocRaw:
15861596
return getAllocOperation(Context, Id);
15871597

lib/IRGen/GenMeta.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4526,7 +4526,8 @@ class EnumMetadataBuilderBase
45264526
: super(IGM, theEnum) {}
45274527

45284528
void addMetadataFlags() {
4529-
addWord(getMetadataKind(IGM, MetadataKind::Enum));
4529+
addWord(getMetadataKind(IGM, Target->classifyAsOptionalType()
4530+
? MetadataKind::Optional : MetadataKind::Enum));
45304531
}
45314532

45324533
void addNominalTypeDescriptor() {

lib/IRGen/RuntimeFunctions.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,13 @@ FUNCTION(IsClassType,
853853
ARGS(TypeMetadataPtrTy),
854854
ATTRS(NoUnwind, ReadNone))
855855

856+
// bool swift_isOptionalType(type*);
857+
FUNCTION(IsOptionalType,
858+
swift_isOptionalType, RuntimeCC,
859+
RETURNS(Int1Ty),
860+
ARGS(TypeMetadataPtrTy),
861+
ATTRS(NoUnwind, ReadNone))
862+
856863
// void swift_once(swift_once_t *predicate,
857864
// void (*function_code)(RefCounted*));
858865
FUNCTION(Once, swift_once, RuntimeCC,

stdlib/public/core/Builtin.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,11 @@ public // @testable
568568
func _isPOD<T>(type: T.Type) -> Bool {
569569
return Bool(Builtin.ispod(type))
570570
}
571+
572+
/// Return true if type is nominally an Optional type.
573+
@_transparent
574+
@warn_unused_result
575+
public // @testable
576+
func _isOptional<T>(type: T.Type) -> Bool {
577+
return Bool(Builtin.isOptional(type))
578+
}

stdlib/public/core/ImplicitlyUnwrappedOptional.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ extension ImplicitlyUnwrappedOptional : CustomStringConvertible {
9595
}
9696
}
9797

98+
/// Directly conform to CustomDebugStringConvertible to support
99+
/// optional printing. Implementation of that feature relies on
100+
/// _isOptional thus cannot distinguish ImplicitlyUnwrappedOptional
101+
/// from Optional. When conditional conformance is available, this
102+
/// outright conformance can be removed.
103+
extension ImplicitlyUnwrappedOptional : CustomDebugStringConvertible {
104+
public var debugDescription: String {
105+
return description
106+
}
107+
}
108+
98109
@_transparent
99110
@warn_unused_result
100111
public // COMPILER_INTRINSIC

stdlib/public/core/OutputStream.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ internal func _adHocPrint<T, TargetStream : OutputStreamType>(
165165
internal func _print_unlocked<T, TargetStream : OutputStreamType>(
166166
value: T, inout _ target: TargetStream
167167
) {
168+
// Optional has no representation suitable for display; therefore,
169+
// values of optional type should be printed as a debug
170+
// string. Check for Optional first, before checking protocol
171+
// conformance below, because an Optional value is convertible to a
172+
// protocol if its wrapped type conforms to that protocol.
173+
if _isOptional(value.dynamicType) {
174+
let debugPrintable = value as! CustomDebugStringConvertible
175+
debugPrintable.debugDescription.writeTo(&target)
176+
return
177+
}
168178
if case let streamableObject as Streamable = value {
169179
streamableObject.writeTo(&target)
170180
return

stdlib/public/runtime/Casting.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ static void _buildNameForMetadata(const Metadata *type,
247247
result);
248248
}
249249
case MetadataKind::Enum:
250+
case MetadataKind::Optional:
250251
case MetadataKind::Struct: {
251252
auto structType = static_cast<const StructMetadata *>(type);
252253
return _buildNominalTypeName(structType->Description,
@@ -574,6 +575,7 @@ static bool _conformsToProtocol(const OpaqueValue *value,
574575
case MetadataKind::HeapGenericLocalVariable:
575576
case MetadataKind::ErrorObject:
576577
case MetadataKind::Enum:
578+
case MetadataKind::Optional:
577579
case MetadataKind::Opaque:
578580
case MetadataKind::Struct:
579581
case MetadataKind::Tuple:
@@ -634,6 +636,7 @@ static bool _conformsToProtocol(const OpaqueValue *value,
634636
case MetadataKind::ErrorObject:
635637
case MetadataKind::Metatype:
636638
case MetadataKind::Enum:
639+
case MetadataKind::Optional:
637640
case MetadataKind::Opaque:
638641
case MetadataKind::Struct:
639642
case MetadataKind::Tuple:
@@ -736,6 +739,7 @@ static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
736739
case MetadataKind::HeapGenericLocalVariable:
737740
case MetadataKind::ErrorObject:
738741
case MetadataKind::Enum:
742+
case MetadataKind::Optional:
739743
case MetadataKind::Opaque:
740744
case MetadataKind::Struct:
741745
case MetadataKind::Tuple:
@@ -797,6 +801,7 @@ static void deallocateDynamicValue(OpaqueValue *value, const Metadata *type) {
797801
case MetadataKind::HeapGenericLocalVariable:
798802
case MetadataKind::ErrorObject:
799803
case MetadataKind::Enum:
804+
case MetadataKind::Optional:
800805
case MetadataKind::Opaque:
801806
case MetadataKind::Struct:
802807
case MetadataKind::Tuple:
@@ -822,6 +827,7 @@ swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype) {
822827
// Other kinds of metadata don't cast to AnyObject.
823828
case MetadataKind::Struct:
824829
case MetadataKind::Enum:
830+
case MetadataKind::Optional:
825831
case MetadataKind::Opaque:
826832
case MetadataKind::Tuple:
827833
case MetadataKind::Function:
@@ -852,6 +858,7 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype) {
852858
// Other kinds of metadata don't cast to AnyObject.
853859
case MetadataKind::Struct:
854860
case MetadataKind::Enum:
861+
case MetadataKind::Optional:
855862
case MetadataKind::Opaque:
856863
case MetadataKind::Tuple:
857864
case MetadataKind::Function:
@@ -939,6 +946,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
939946

940947
case MetadataKind::Struct:
941948
case MetadataKind::Enum:
949+
case MetadataKind::Optional:
942950
#if SWIFT_OBJC_INTEROP
943951
// If the source type is bridged to Objective-C, try to bridge.
944952
if (auto srcBridgeWitness = findBridgeWitness(srcDynamicType)) {
@@ -1111,6 +1119,7 @@ swift::swift_dynamicCastUnknownClass(const void *object,
11111119
case MetadataKind::ErrorObject:
11121120
case MetadataKind::Metatype:
11131121
case MetadataKind::Enum:
1122+
case MetadataKind::Optional:
11141123
case MetadataKind::Opaque:
11151124
case MetadataKind::Struct:
11161125
case MetadataKind::Tuple:
@@ -1166,6 +1175,7 @@ swift::swift_dynamicCastUnknownClassUnconditional(const void *object,
11661175
case MetadataKind::ErrorObject:
11671176
case MetadataKind::Metatype:
11681177
case MetadataKind::Enum:
1178+
case MetadataKind::Optional:
11691179
case MetadataKind::Opaque:
11701180
case MetadataKind::Struct:
11711181
case MetadataKind::Tuple:
@@ -1225,6 +1235,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType,
12251235
case MetadataKind::ErrorObject:
12261236
case MetadataKind::Metatype:
12271237
case MetadataKind::Enum:
1238+
case MetadataKind::Optional:
12281239
case MetadataKind::Opaque:
12291240
case MetadataKind::Struct:
12301241
case MetadataKind::Tuple:
@@ -1255,6 +1266,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType,
12551266
case MetadataKind::ErrorObject:
12561267
case MetadataKind::Metatype:
12571268
case MetadataKind::Enum:
1269+
case MetadataKind::Optional:
12581270
case MetadataKind::Opaque:
12591271
case MetadataKind::Struct:
12601272
case MetadataKind::Tuple:
@@ -1270,6 +1282,7 @@ swift::swift_dynamicCastMetatype(const Metadata *sourceType,
12701282
case MetadataKind::ErrorObject:
12711283
case MetadataKind::Metatype:
12721284
case MetadataKind::Enum:
1285+
case MetadataKind::Optional:
12731286
case MetadataKind::Opaque:
12741287
case MetadataKind::Struct:
12751288
case MetadataKind::Tuple:
@@ -1332,6 +1345,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
13321345
case MetadataKind::ErrorObject:
13331346
case MetadataKind::Metatype:
13341347
case MetadataKind::Enum:
1348+
case MetadataKind::Optional:
13351349
case MetadataKind::Opaque:
13361350
case MetadataKind::Struct:
13371351
case MetadataKind::Tuple:
@@ -1363,6 +1377,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
13631377
case MetadataKind::ErrorObject:
13641378
case MetadataKind::Metatype:
13651379
case MetadataKind::Enum:
1380+
case MetadataKind::Optional:
13661381
case MetadataKind::Opaque:
13671382
case MetadataKind::Struct:
13681383
case MetadataKind::Tuple:
@@ -1377,6 +1392,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
13771392
case MetadataKind::ErrorObject:
13781393
case MetadataKind::Metatype:
13791394
case MetadataKind::Enum:
1395+
case MetadataKind::Optional:
13801396
case MetadataKind::Opaque:
13811397
case MetadataKind::Struct:
13821398
case MetadataKind::Tuple:
@@ -1715,6 +1731,7 @@ static bool _dynamicCastToMetatype(OpaqueValue *dest,
17151731
case MetadataKind::HeapGenericLocalVariable:
17161732
case MetadataKind::ErrorObject:
17171733
case MetadataKind::Enum:
1734+
case MetadataKind::Optional:
17181735
case MetadataKind::Opaque:
17191736
case MetadataKind::Struct:
17201737
case MetadataKind::Tuple:
@@ -1885,6 +1902,7 @@ static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest,
18851902
case MetadataKind::HeapGenericLocalVariable:
18861903
case MetadataKind::ErrorObject:
18871904
case MetadataKind::Enum:
1905+
case MetadataKind::Optional:
18881906
case MetadataKind::Opaque:
18891907
case MetadataKind::Struct:
18901908
case MetadataKind::Tuple:
@@ -1947,6 +1965,7 @@ static bool _dynamicCastToFunction(OpaqueValue *dest,
19471965
case MetadataKind::Class:
19481966
case MetadataKind::Struct:
19491967
case MetadataKind::Enum:
1968+
case MetadataKind::Optional:
19501969
case MetadataKind::ObjCClassWrapper:
19511970
case MetadataKind::ForeignClass:
19521971
case MetadataKind::ExistentialMetatype:
@@ -1971,13 +1990,59 @@ static id dynamicCastValueToNSError(OpaqueValue *src,
19711990
}
19721991
#endif
19731992

1993+
static bool canCastToExistential(OpaqueValue *dest, OpaqueValue *src,
1994+
const Metadata *srcType,
1995+
const Metadata *targetType) {
1996+
if (targetType->getKind() != MetadataKind::Existential)
1997+
return false;
1998+
1999+
return _dynamicCastToExistential(dest, src, srcType,
2000+
cast<ExistentialTypeMetadata>(targetType),
2001+
DynamicCastFlags::Default);
2002+
}
2003+
19742004
/// Perform a dynamic cast to an arbitrary type.
19752005
bool swift::swift_dynamicCast(OpaqueValue *dest,
19762006
OpaqueValue *src,
19772007
const Metadata *srcType,
19782008
const Metadata *targetType,
19792009
DynamicCastFlags flags) {
2010+
// Check if the cast source is Optional and the target is not an existential
2011+
// that Optional conforms to. Unwrap one level of Optional and continue.
2012+
if (srcType->getKind() == MetadataKind::Optional
2013+
&& !canCastToExistential(dest, src, srcType, targetType)) {
2014+
const Metadata *payloadType =
2015+
cast<EnumMetadata>(srcType)->getGenericArgs()[0];
2016+
int enumCase =
2017+
swift_getEnumCaseSinglePayload(src, payloadType, 1 /*emptyCases=*/);
2018+
if (enumCase != -1) {
2019+
// Allow Optional<T>.None -> Optional<U>.None
2020+
if (targetType->getKind() != MetadataKind::Optional)
2021+
return _fail(src, srcType, targetType, flags);
2022+
// Inject the .None tag
2023+
swift_storeEnumTagSinglePayload(dest, payloadType, enumCase,
2024+
1 /*emptyCases=*/);
2025+
return _succeed(dest, src, srcType, flags);
2026+
}
2027+
// .Some
2028+
// Single payload enums are guaranteed layout compatible with their
2029+
// payload. Only the source's payload needs to be taken or destroyed.
2030+
srcType = payloadType;
2031+
}
2032+
19802033
switch (targetType->getKind()) {
2034+
// Handle wrapping an Optional target.
2035+
case MetadataKind::Optional: {
2036+
// Recursively cast into the layout compatible payload area.
2037+
const Metadata *payloadType =
2038+
cast<EnumMetadata>(targetType)->getGenericArgs()[0];
2039+
if (swift_dynamicCast(dest, src, srcType, payloadType, flags)) {
2040+
swift_storeEnumTagSinglePayload(dest, payloadType, -1 /*case*/,
2041+
1 /*emptyCases*/);
2042+
return true;
2043+
}
2044+
return false;
2045+
}
19812046

19822047
// Casts to class type.
19832048
case MetadataKind::Class:
@@ -2020,6 +2085,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
20202085
}
20212086

20222087
case MetadataKind::Enum:
2088+
case MetadataKind::Optional:
20232089
case MetadataKind::Struct: {
20242090
#if SWIFT_OBJC_INTEROP
20252091
// If the source type is bridged to Objective-C, try to bridge.
@@ -2092,6 +2158,7 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
20922158
}
20932159

20942160
case MetadataKind::Enum:
2161+
case MetadataKind::Optional:
20952162
case MetadataKind::Existential:
20962163
case MetadataKind::ExistentialMetatype:
20972164
case MetadataKind::Function:
@@ -2970,6 +3037,7 @@ findBridgeWitness(const Metadata *T) {
29703037
case MetadataKind::Class:
29713038
case MetadataKind::Struct:
29723039
case MetadataKind::Enum:
3040+
case MetadataKind::Optional:
29733041
case MetadataKind::Opaque:
29743042
case MetadataKind::Tuple:
29753043
case MetadataKind::Function:
@@ -3149,3 +3217,7 @@ extern "C" const Metadata *_swift_getSuperclass_nonNull(
31493217
extern "C" bool swift_isClassType(const Metadata *type) {
31503218
return Metadata::isAnyKindOfClass(type->getKind());
31513219
}
3220+
3221+
extern "C" bool swift_isOptionalType(const Metadata *type) {
3222+
return type->getKind() == MetadataKind::Optional;
3223+
}

stdlib/public/runtime/ErrorObject.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ static id _swift_bridgeErrorTypeToNSError_(SwiftError *errorObject) {
380380
}
381381
// Not a class.
382382
case MetadataKind::Enum:
383+
case MetadataKind::Optional:
383384
case MetadataKind::Existential:
384385
case MetadataKind::ExistentialMetatype:
385386
case MetadataKind::Function:

0 commit comments

Comments
 (0)