Skip to content

Commit a8a49af

Browse files
committed
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.
1 parent a98de1b commit a8a49af

File tree

6 files changed

+44
-0
lines changed

6 files changed

+44
-0
lines changed

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.

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/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/runtime/Casting.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,3 +3172,7 @@ extern "C" const Metadata *_swift_getSuperclass_nonNull(
31723172
extern "C" bool swift_isClassType(const Metadata *type) {
31733173
return Metadata::isAnyKindOfClass(type->getKind());
31743174
}
3175+
3176+
extern "C" bool swift_isOptionalType(const Metadata *type) {
3177+
return type->getKind() == MetadataKind::Optional;
3178+
}

test/1_stdlib/Builtins.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,14 @@ tests.test("_isPOD") {
277277
expectFalse(_isPOD(P.self))
278278
}
279279

280+
tests.test("_isOptional") {
281+
expectTrue(_isOptional(Optional<Int>.self))
282+
expectTrue(_isOptional(Optional<X>.self))
283+
expectTrue(_isOptional(Optional<P>.self))
284+
expectTrue(_isOptional(ImplicitlyUnwrappedOptional<P>.self))
285+
expectFalse(_isOptional(Int.self))
286+
expectFalse(_isOptional(X.self))
287+
expectFalse(_isOptional(P.self))
288+
}
289+
280290
runAllTests()

0 commit comments

Comments
 (0)