Skip to content

Runtime optional casts. #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/ABI/MetadataKind.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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;
}
};

Expand Down
10 changes: 10 additions & 0 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions lib/IRGen/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions stdlib/public/core/Builtin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,11 @@ public // @testable
func _isPOD<T>(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<T>(type: T.Type) -> Bool {
return Bool(Builtin.isOptional(type))
}
11 changes: 11 additions & 0 deletions stdlib/public/core/ImplicitlyUnwrappedOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions stdlib/public/core/OutputStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ internal func _adHocPrint<T, TargetStream : OutputStreamType>(
internal func _print_unlocked<T, TargetStream : OutputStreamType>(
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
Expand Down
72 changes: 72 additions & 0 deletions stdlib/public/runtime/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ static void _buildNameForMetadata(const Metadata *type,
result);
}
case MetadataKind::Enum:
case MetadataKind::Optional:
case MetadataKind::Struct: {
auto structType = static_cast<const StructMetadata *>(type);
return _buildNominalTypeName(structType->Description,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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<ExistentialTypeMetadata>(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<EnumMetadata>(srcType)->getGenericArgs()[0];
int enumCase =
swift_getEnumCaseSinglePayload(src, payloadType, 1 /*emptyCases=*/);
if (enumCase != -1) {
// Allow Optional<T>.None -> Optional<U>.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<EnumMetadata>(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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions stdlib/public/runtime/ErrorObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading