Skip to content
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
24 changes: 10 additions & 14 deletions lib/SILGen/SILGenForeignError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,16 @@ void SILGenFunction::emitForeignErrorBlock(SILLocation loc,
static SILValue emitUnwrapIntegerResult(SILGenFunction &gen,
SILLocation loc,
SILValue value) {
CanType boolType = gen.SGM.Types.getBoolType();

value = gen.emitBridgedToNativeValue(
loc, ManagedValue::forUnmanaged(value),
SILFunctionTypeRepresentation::CFunctionPointer,
boolType).forward(gen);

auto structDecl = value->getType().getStructOrBoundGenericStruct();
assert(structDecl && "value for error result wasn't of struct type!");
assert(std::next(structDecl->getStoredProperties().begin())
== structDecl->getStoredProperties().end());
auto property = *structDecl->getStoredProperties().begin();
value = gen.B.createStructExtract(loc, value, property);
assert(value->getType().is<BuiltinIntegerType>());
// This is a loop because we want to handle types that wrap integer types,
// like ObjCBool (which may be Bool or Int8).
while (!value->getType().is<BuiltinIntegerType>()) {
auto structDecl = value->getType().getStructOrBoundGenericStruct();
assert(structDecl && "value for error result wasn't of struct type!");
assert(std::next(structDecl->getStoredProperties().begin())
== structDecl->getStoredProperties().end());
auto property = *structDecl->getStoredProperties().begin();
value = gen.B.createStructExtract(loc, value, property);
}

return value;
}
Expand Down
22 changes: 22 additions & 0 deletions test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,28 @@ extension Int : _ObjectiveCBridgeable {
}
}

extension Bool: _ObjectiveCBridgeable {
public func _bridgeToObjectiveC() -> NSNumber {
return NSNumber()
}
public static func _forceBridgeFromObjectiveC(
_ x: NSNumber,
result: inout Bool?
) {
}
public static func _conditionallyBridgeFromObjectiveC(
_ x: NSNumber,
result: inout Bool?
) -> Bool {
return true
}
public static func _unconditionallyBridgeFromObjectiveC(
_ x: NSNumber?
) -> Bool {
return false
}
}

extension Array : _ObjectiveCBridgeable {
public func _bridgeToObjectiveC() -> NSArray {
return NSArray()
Expand Down
1 change: 1 addition & 0 deletions test/Inputs/clang-importer-sdk/usr/include/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
+ (float) bounce: (NSError**) err __attribute__((swift_error(nonnull_error)));
+ (void) flounce: (NSError**) err __attribute__((swift_error(nonnull_error)));
+ (int) ounce: (NSError**) err __attribute__((swift_error(zero_result)));
+ (NSInteger) ounceWord: (NSError**) err __attribute__((swift_error(zero_result)));
+ (int) once: (NSError**) err __attribute__((swift_error(nonzero_result)));
+ (BOOL) sconce: (NSError**) err __attribute__((swift_error(zero_result)));
+ (BOOL) scotch: (NSError**) err __attribute__((swift_error(nonzero_result)));
Expand Down
50 changes: 45 additions & 5 deletions test/SILGen/foreign_errors.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -parse-as-library %s | %FileCheck %s
// RUN: rm -rf %t && mkdir -p %t
// RUN: %build-clang-importer-objc-overlays
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-silgen -parse-as-library %s | %FileCheck %s

// REQUIRES: objc_interop

Expand Down Expand Up @@ -31,10 +33,12 @@ func test0() throws {
// CHECK: assign [[T1]] to [[ERR_TEMP0]]

// Pull out the boolean value and compare it to zero.
// CHECK: [[FN:%.*]] = function_ref @_TF10ObjectiveC22_convertObjCBoolToBoolFVS_8ObjCBoolSb
// CHECK: [[BOOL:%.*]] = apply [[FN]]([[RESULT]])
// CHECK: [[BIT:%.*]] = struct_extract [[BOOL]]
// CHECK: cond_br [[BIT]], [[NORMAL_BB:bb[0-9]+]], [[ERROR_BB:bb[0-9]+]]
// CHECK: [[BOOL_OR_INT:%.*]] = struct_extract [[RESULT]]
// CHECK: [[RAW_VALUE:%.*]] = struct_extract [[BOOL_OR_INT]]
// On some platforms RAW_VALUE will be compared against 0; on others it's
// already an i1 (bool) and those instructions will be skipped. Just do a
// looser check.
// CHECK: cond_br {{%.+}}, [[NORMAL_BB:bb[0-9]+]], [[ERROR_BB:bb[0-9]+]]
try ErrorProne.fail()

// Normal path: fall out and return.
Expand Down Expand Up @@ -229,3 +233,39 @@ func testPreservedResult() throws -> CInt {
// CHECK-NOT: release
// CHECK: return [[RESULT]]
// CHECK: [[ERROR_BB]]

func testPreservedResultBridged() throws -> Int {
return try ErrorProne.ounceWord()
}

// CHECK-LABEL: sil hidden @_TF14foreign_errors26testPreservedResultBridgedFzT_Si
// CHECK: [[T0:%.*]] = metatype $@thick ErrorProne.Type
// CHECK: [[T1:%.*]] = class_method [volatile] [[T0]] : $@thick ErrorProne.Type, #ErrorProne.ounceWord!1.foreign : (ErrorProne.Type) -> () throws -> Int , $@convention(objc_method) (ImplicitlyUnwrappedOptional<AutoreleasingUnsafeMutablePointer<Optional<NSError>>>, @objc_metatype ErrorProne.Type) -> Int
// CHECK: [[OPTERR:%.*]] = alloc_stack $Optional<NSError>
// CHECK: [[RESULT:%.*]] = apply [[T1]](
// CHECK: [[T0:%.*]] = struct_extract [[RESULT]]
// CHECK: [[T1:%.*]] = integer_literal $[[PRIM:Builtin.Int[0-9]+]], 0
// CHECK: [[T2:%.*]] = builtin "cmp_ne_Int{{.*}}"([[T0]] : $[[PRIM]], [[T1]] : $[[PRIM]])
// CHECK: cond_br [[T2]], [[NORMAL_BB:bb[0-9]+]], [[ERROR_BB:bb[0-9]+]]
// CHECK: [[NORMAL_BB]]:
// CHECK-NOT: release
// CHECK: return [[RESULT]]
// CHECK: [[ERROR_BB]]

func testPreservedResultInverted() throws {
try ErrorProne.once()
}

// CHECK-LABEL: sil hidden @_TF14foreign_errors27testPreservedResultInvertedFzT_T_
// CHECK: [[T0:%.*]] = metatype $@thick ErrorProne.Type
// CHECK: [[T1:%.*]] = class_method [volatile] [[T0]] : $@thick ErrorProne.Type, #ErrorProne.once!1.foreign : (ErrorProne.Type) -> () throws -> () , $@convention(objc_method) (ImplicitlyUnwrappedOptional<AutoreleasingUnsafeMutablePointer<Optional<NSError>>>, @objc_metatype ErrorProne.Type) -> Int32
// CHECK: [[OPTERR:%.*]] = alloc_stack $Optional<NSError>
// CHECK: [[RESULT:%.*]] = apply [[T1]](
// CHECK: [[T0:%.*]] = struct_extract [[RESULT]]
// CHECK: [[T1:%.*]] = integer_literal $[[PRIM:Builtin.Int[0-9]+]], 0
// CHECK: [[T2:%.*]] = builtin "cmp_ne_Int32"([[T0]] : $[[PRIM]], [[T1]] : $[[PRIM]])
// CHECK: cond_br [[T2]], [[ERROR_BB:bb[0-9]+]], [[NORMAL_BB:bb[0-9]+]]
// CHECK: [[NORMAL_BB]]:
// CHECK-NOT: release
// CHECK: return {{%.+}} : $()
// CHECK: [[ERROR_BB]]