mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SILGen] Fix the type of closure thunks that are passed const reference structs (#75491)
The thunk's parameter needs the @in_guaranteed convention if it's a const reference parameter. However, that convention wasn't being used because clang importer was removing the const reference from the type and SILGen was computing the type of the parameter based on the type without const reference. This commit fixes the bug by passing the clang function type to SILDeclRef so that it can be used to compute the correct thunk type. This fixes a crash when a closure is passed to a C function taking a pointer to a function that has a const reference struct parameter. rdar://131321096
This commit is contained in:
@@ -1081,7 +1081,7 @@ struct BridgedSuccessorArray {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BridgedDeclRef {
|
struct BridgedDeclRef {
|
||||||
uint64_t storage[3];
|
uint64_t storage[4];
|
||||||
|
|
||||||
#ifdef USED_IN_CPP_SOURCE
|
#ifdef USED_IN_CPP_SOURCE
|
||||||
BridgedDeclRef(swift::SILDeclRef declRef) {
|
BridgedDeclRef(swift::SILDeclRef declRef) {
|
||||||
@@ -1098,7 +1098,7 @@ struct BridgedDeclRef {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BridgedVTableEntry {
|
struct BridgedVTableEntry {
|
||||||
uint64_t storage[5];
|
uint64_t storage[6];
|
||||||
|
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
Normal,
|
Normal,
|
||||||
@@ -1146,7 +1146,7 @@ struct OptionalBridgedVTable {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BridgedWitnessTableEntry {
|
struct BridgedWitnessTableEntry {
|
||||||
uint64_t storage[5];
|
uint64_t storage[6];
|
||||||
|
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
invalid,
|
invalid,
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ namespace llvm {
|
|||||||
class raw_ostream;
|
class raw_ostream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
class Type;
|
||||||
|
}
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
enum class EffectsKind : uint8_t;
|
enum class EffectsKind : uint8_t;
|
||||||
class AbstractFunctionDecl;
|
class AbstractFunctionDecl;
|
||||||
@@ -204,6 +208,9 @@ struct SILDeclRef {
|
|||||||
const GenericSignatureImpl *, CustomAttr *>
|
const GenericSignatureImpl *, CustomAttr *>
|
||||||
pointer;
|
pointer;
|
||||||
|
|
||||||
|
// Type of closure thunk.
|
||||||
|
const clang::Type *thunkType = nullptr;
|
||||||
|
|
||||||
/// Returns the type of AST node location being stored by the SILDeclRef.
|
/// Returns the type of AST node location being stored by the SILDeclRef.
|
||||||
LocKind getLocKind() const {
|
LocKind getLocKind() const {
|
||||||
if (loc.is<ValueDecl *>())
|
if (loc.is<ValueDecl *>())
|
||||||
@@ -257,11 +264,10 @@ struct SILDeclRef {
|
|||||||
/// for the containing ClassDecl.
|
/// for the containing ClassDecl.
|
||||||
/// - If 'loc' is a global VarDecl, this returns its GlobalAccessor
|
/// - If 'loc' is a global VarDecl, this returns its GlobalAccessor
|
||||||
/// SILDeclRef.
|
/// SILDeclRef.
|
||||||
explicit SILDeclRef(
|
explicit SILDeclRef(Loc loc, bool isForeign = false,
|
||||||
Loc loc,
|
bool isDistributed = false,
|
||||||
bool isForeign = false,
|
bool isDistributedLocal = false,
|
||||||
bool isDistributed = false,
|
const clang::Type *thunkType = nullptr);
|
||||||
bool isDistributedLocal = false);
|
|
||||||
|
|
||||||
/// See above put produces a prespecialization according to the signature.
|
/// See above put produces a prespecialization according to the signature.
|
||||||
explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig);
|
explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig);
|
||||||
|
|||||||
@@ -135,11 +135,15 @@ SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign,
|
|||||||
isAsyncLetClosure(0), pointer(derivativeId) {}
|
isAsyncLetClosure(0), pointer(derivativeId) {}
|
||||||
|
|
||||||
SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
|
SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
|
||||||
bool asDistributed, bool asDistributedKnownToBeLocal)
|
bool asDistributed, bool asDistributedKnownToBeLocal,
|
||||||
|
const clang::Type *thunkType)
|
||||||
: isRuntimeAccessible(false),
|
: isRuntimeAccessible(false),
|
||||||
backDeploymentKind(SILDeclRef::BackDeploymentKind::None),
|
backDeploymentKind(SILDeclRef::BackDeploymentKind::None),
|
||||||
defaultArgIndex(0), isAsyncLetClosure(0),
|
defaultArgIndex(0), isAsyncLetClosure(0),
|
||||||
pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) {
|
pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr),
|
||||||
|
thunkType(thunkType) {
|
||||||
|
assert((!thunkType || baseLoc.is<AbstractClosureExpr *>()) &&
|
||||||
|
"thunk type is needed only for closures");
|
||||||
if (auto *vd = baseLoc.dyn_cast<ValueDecl*>()) {
|
if (auto *vd = baseLoc.dyn_cast<ValueDecl*>()) {
|
||||||
if (auto *fd = dyn_cast<FuncDecl>(vd)) {
|
if (auto *fd = dyn_cast<FuncDecl>(vd)) {
|
||||||
// Map FuncDecls directly to Func SILDeclRefs.
|
// Map FuncDecls directly to Func SILDeclRefs.
|
||||||
@@ -169,6 +173,8 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
|
|||||||
llvm_unreachable("invalid loc decl for SILDeclRef!");
|
llvm_unreachable("invalid loc decl for SILDeclRef!");
|
||||||
}
|
}
|
||||||
} else if (auto *ACE = baseLoc.dyn_cast<AbstractClosureExpr *>()) {
|
} else if (auto *ACE = baseLoc.dyn_cast<AbstractClosureExpr *>()) {
|
||||||
|
assert((!asForeign || thunkType) &&
|
||||||
|
"thunk type needed for foreign type for closures");
|
||||||
loc = ACE;
|
loc = ACE;
|
||||||
kind = Kind::Func;
|
kind = Kind::Func;
|
||||||
if (ACE->getASTContext().LangOpts.hasFeature(
|
if (ACE->getASTContext().LangOpts.hasFeature(
|
||||||
|
|||||||
@@ -3965,12 +3965,10 @@ static CanSILFunctionType getUncachedSILFunctionTypeForConstant(
|
|||||||
// The type of the native-to-foreign thunk for a swift closure.
|
// The type of the native-to-foreign thunk for a swift closure.
|
||||||
if (constant.isForeign && constant.hasClosureExpr() &&
|
if (constant.isForeign && constant.hasClosureExpr() &&
|
||||||
shouldStoreClangType(TC.getDeclRefRepresentation(constant))) {
|
shouldStoreClangType(TC.getDeclRefRepresentation(constant))) {
|
||||||
auto clangType = TC.Context.getClangFunctionType(
|
assert(!extInfoBuilder.getClangTypeInfo().empty() &&
|
||||||
origLoweredInterfaceType->getParams(),
|
"clang type not found");
|
||||||
origLoweredInterfaceType->getResult(),
|
AbstractionPattern pattern = AbstractionPattern(
|
||||||
FunctionTypeRepresentation::CFunctionPointer);
|
origLoweredInterfaceType, extInfoBuilder.getClangTypeInfo().getType());
|
||||||
AbstractionPattern pattern =
|
|
||||||
AbstractionPattern(origLoweredInterfaceType, clangType);
|
|
||||||
return getSILFunctionTypeForAbstractCFunction(
|
return getSILFunctionTypeForAbstractCFunction(
|
||||||
TC, pattern, origLoweredInterfaceType, extInfoBuilder, constant);
|
TC, pattern, origLoweredInterfaceType, extInfoBuilder, constant);
|
||||||
}
|
}
|
||||||
@@ -4476,9 +4474,13 @@ getAbstractionPatternForConstant(ASTContext &ctx, SILDeclRef constant,
|
|||||||
if (!constant.isForeign)
|
if (!constant.isForeign)
|
||||||
return AbstractionPattern(fnType);
|
return AbstractionPattern(fnType);
|
||||||
|
|
||||||
|
if (constant.thunkType)
|
||||||
|
return AbstractionPattern(fnType, constant.thunkType);
|
||||||
|
|
||||||
auto bridgedFn = getBridgedFunction(constant);
|
auto bridgedFn = getBridgedFunction(constant);
|
||||||
if (!bridgedFn)
|
if (!bridgedFn)
|
||||||
return AbstractionPattern(fnType);
|
return AbstractionPattern(fnType);
|
||||||
|
|
||||||
const clang::Decl *clangDecl = bridgedFn->getClangDecl();
|
const clang::Decl *clangDecl = bridgedFn->getClangDecl();
|
||||||
if (!clangDecl)
|
if (!clangDecl)
|
||||||
return AbstractionPattern(fnType);
|
return AbstractionPattern(fnType);
|
||||||
|
|||||||
@@ -1315,8 +1315,9 @@ static SILValue emitObjCUnconsumedArgument(SILGenFunction &SGF,
|
|||||||
SILLocation loc,
|
SILLocation loc,
|
||||||
SILValue arg) {
|
SILValue arg) {
|
||||||
auto &lowering = SGF.getTypeLowering(arg->getType());
|
auto &lowering = SGF.getTypeLowering(arg->getType());
|
||||||
// If address-only, make a +1 copy and operate on that.
|
// If arg is non-trivial and has an address type, make a +1 copy and operate
|
||||||
if (lowering.isAddressOnly() && SGF.useLoweredAddresses()) {
|
// on that.
|
||||||
|
if (!lowering.isTrivial() && arg->getType().isAddress()) {
|
||||||
auto tmp = SGF.emitTemporaryAllocation(loc, arg->getType().getObjectType());
|
auto tmp = SGF.emitTemporaryAllocation(loc, arg->getType().getObjectType());
|
||||||
SGF.B.createCopyAddr(loc, arg, tmp, IsNotTake, IsInitialization);
|
SGF.B.createCopyAddr(loc, arg, tmp, IsNotTake, IsInitialization);
|
||||||
return tmp;
|
return tmp;
|
||||||
@@ -1448,6 +1449,11 @@ emitObjCThunkArguments(SILGenFunction &SGF, SILLocation loc, SILDeclRef thunk,
|
|||||||
auto buf = SGF.emitTemporaryAllocation(loc, native.getType());
|
auto buf = SGF.emitTemporaryAllocation(loc, native.getType());
|
||||||
native.forwardInto(SGF, loc, buf);
|
native.forwardInto(SGF, loc, buf);
|
||||||
native = SGF.emitManagedBufferWithCleanup(buf);
|
native = SGF.emitManagedBufferWithCleanup(buf);
|
||||||
|
} else if (!fnConv.isSILIndirect(nativeInputs[i]) &&
|
||||||
|
native.getType().isAddress()) {
|
||||||
|
// Load the value if the argument has an address type and the native
|
||||||
|
// function expects the argument to be passed directly.
|
||||||
|
native = SGF.emitManagedLoadCopy(loc, native.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nativeInputs[i].isConsumedInCaller()) {
|
if (nativeInputs[i].isConsumedInCaller()) {
|
||||||
|
|||||||
@@ -1723,7 +1723,19 @@ static ManagedValue convertCFunctionSignature(SILGenFunction &SGF,
|
|||||||
FunctionConversionExpr *e,
|
FunctionConversionExpr *e,
|
||||||
SILType loweredResultTy,
|
SILType loweredResultTy,
|
||||||
llvm::function_ref<ManagedValue ()> fnEmitter) {
|
llvm::function_ref<ManagedValue ()> fnEmitter) {
|
||||||
SILType loweredDestTy = SGF.getLoweredType(e->getType());
|
SILType loweredDestTy;
|
||||||
|
auto destTy = e->getType();
|
||||||
|
auto clangInfo =
|
||||||
|
destTy->castTo<AnyFunctionType>()->getExtInfo().getClangTypeInfo();
|
||||||
|
if (clangInfo.empty())
|
||||||
|
loweredDestTy = SGF.getLoweredType(destTy);
|
||||||
|
else
|
||||||
|
// This won't be necessary after we stop dropping clang types when
|
||||||
|
// canonicalizing function types.
|
||||||
|
loweredDestTy = SGF.getLoweredType(
|
||||||
|
AbstractionPattern(destTy->getCanonicalType(), clangInfo.getType()),
|
||||||
|
destTy);
|
||||||
|
|
||||||
ManagedValue result;
|
ManagedValue result;
|
||||||
|
|
||||||
// We're converting between C function pointer types. They better be
|
// We're converting between C function pointer types. They better be
|
||||||
@@ -1794,7 +1806,9 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF,
|
|||||||
#endif
|
#endif
|
||||||
semanticExpr = conv->getSubExpr()->getSemanticsProvidingExpr();
|
semanticExpr = conv->getSubExpr()->getSemanticsProvidingExpr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clang::Type *destFnType = nullptr;
|
||||||
|
|
||||||
if (auto declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
|
if (auto declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
|
||||||
setLocFromConcreteDeclRef(declRef->getDeclRef());
|
setLocFromConcreteDeclRef(declRef->getDeclRef());
|
||||||
} else if (auto memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
|
} else if (auto memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
|
||||||
@@ -1808,12 +1822,18 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF,
|
|||||||
loc = closure;
|
loc = closure;
|
||||||
return ManagedValue();
|
return ManagedValue();
|
||||||
});
|
});
|
||||||
|
auto clangInfo = conversionExpr->getType()
|
||||||
|
->castTo<FunctionType>()
|
||||||
|
->getExtInfo()
|
||||||
|
.getClangTypeInfo();
|
||||||
|
if (!clangInfo.empty())
|
||||||
|
destFnType = clangInfo.getType();
|
||||||
} else {
|
} else {
|
||||||
llvm_unreachable("c function pointer converted from a non-concrete decl ref");
|
llvm_unreachable("c function pointer converted from a non-concrete decl ref");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a reference to the C-compatible entry point for the function.
|
// Produce a reference to the C-compatible entry point for the function.
|
||||||
SILDeclRef constant(loc, /*foreign*/ true);
|
SILDeclRef constant(loc, /*foreign*/ true, false, false, destFnType);
|
||||||
SILConstantInfo constantInfo =
|
SILConstantInfo constantInfo =
|
||||||
SGF.getConstantInfo(SGF.getTypeExpansionContext(), constant);
|
SGF.getConstantInfo(SGF.getTypeExpansionContext(), constant);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ struct NonTrivial {
|
|||||||
int *p;
|
int *p;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Trivial {
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
void cfunc(void (^ _Nonnull block)(NonTrivial)) noexcept {
|
void cfunc(void (^ _Nonnull block)(NonTrivial)) noexcept {
|
||||||
block(NonTrivial());
|
block(NonTrivial());
|
||||||
}
|
}
|
||||||
@@ -45,4 +49,13 @@ void cfuncARCWeak(ARCWeak) noexcept;
|
|||||||
void (* _Nonnull getFnPtr() noexcept)(NonTrivial) noexcept;
|
void (* _Nonnull getFnPtr() noexcept)(NonTrivial) noexcept;
|
||||||
void (* _Nonnull getFnPtr2() noexcept)(ARCWeak) noexcept;
|
void (* _Nonnull getFnPtr2() noexcept)(ARCWeak) noexcept;
|
||||||
|
|
||||||
|
void cfuncConstRefNonTrivial(void (*_Nonnull)(const NonTrivial &));
|
||||||
|
void cfuncConstRefTrivial(void (*_Nonnull)(const Trivial &));
|
||||||
|
void blockConstRefNonTrivial(void (^_Nonnull)(const NonTrivial &));
|
||||||
|
void blockConstRefTrivial(void (^_Nonnull)(const Trivial &));
|
||||||
|
#if __OBJC__
|
||||||
|
void cfuncConstRefStrong(void (*_Nonnull)(const ARCStrong &));
|
||||||
|
void blockConstRefStrong(void (^_Nonnull)(const ARCStrong &));
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // __CLOSURE__
|
#endif // __CLOSURE__
|
||||||
|
|||||||
@@ -35,3 +35,88 @@ public func testClosureToFuncPtr() {
|
|||||||
public func testClosureToBlockReturnNonTrivial() {
|
public func testClosureToBlockReturnNonTrivial() {
|
||||||
cfuncReturnNonTrivial({() -> NonTrivial in return NonTrivial() })
|
cfuncReturnNonTrivial({() -> NonTrivial in return NonTrivial() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil private [thunk] [ossa] @$s4main22testConstRefNonTrivialyyFySo0eF0VcfU_To : $@convention(c) (@in_guaranteed NonTrivial) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*NonTrivial):
|
||||||
|
// CHECK: %[[V1:.*]] = alloc_stack $NonTrivial
|
||||||
|
// CHECK: copy_addr %[[V0]] to [init] %[[V1]] : $*NonTrivial
|
||||||
|
// CHECK: %[[V3:.*]] = function_ref @$s4main22testConstRefNonTrivialyyFySo0eF0VcfU_ : $@convention(thin) (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: %[[V4:.*]] = apply %[[V3]](%[[V1]]) : $@convention(thin) (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: destroy_addr %[[V1]] : $*NonTrivial
|
||||||
|
// CHECK: dealloc_stack %[[V1]] : $*NonTrivial
|
||||||
|
// CHECK: return %[[V4]] : $()
|
||||||
|
|
||||||
|
public func testConstRefNonTrivial() {
|
||||||
|
cfuncConstRefNonTrivial({S in });
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil private [thunk] [ossa] @$s4main19testConstRefTrivialyyFySo0E0VcfU_To : $@convention(c) (@in_guaranteed Trivial) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*Trivial):
|
||||||
|
// CHECK: %[[V1:.*]] = load [trivial] %[[V0]] : $*Trivial
|
||||||
|
// CHECK: %[[V2:.*]] = function_ref @$s4main19testConstRefTrivialyyFySo0E0VcfU_ : $@convention(thin) (Trivial) -> ()
|
||||||
|
// CHECK: %[[V3:.*]] = apply %[[V2]](%[[V1]]) : $@convention(thin) (Trivial) -> ()
|
||||||
|
// CHECK: return %[[V3]] : $()
|
||||||
|
|
||||||
|
public func testConstRefTrivial() {
|
||||||
|
cfuncConstRefTrivial({S in });
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil private [thunk] [ossa] @$s4main18testConstRefStrongyyFySo9ARCStrongVcfU_To : $@convention(c) (@in_guaranteed ARCStrong) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*ARCStrong):
|
||||||
|
// CHECK: %[[V1:.*]] = alloc_stack $ARCStrong
|
||||||
|
// CHECK: copy_addr %[[V0]] to [init] %[[V1]] : $*ARCStrong
|
||||||
|
// CHECK: %[[V3:.*]] = load [copy] %[[V1]] : $*ARCStrong
|
||||||
|
// CHECK: %[[V4:.*]] = begin_borrow %[[V3]] : $ARCStrong
|
||||||
|
// CHECK: %[[V5:.*]] = function_ref @$s4main18testConstRefStrongyyFySo9ARCStrongVcfU_ : $@convention(thin) (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: %[[V6:.*]] = apply %[[V5]](%[[V4]]) : $@convention(thin) (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: end_borrow %[[V4]] : $ARCStrong
|
||||||
|
// CHECK: destroy_value %[[V3]] : $ARCStrong
|
||||||
|
// CHECK: destroy_addr %[[V1]] : $*ARCStrong
|
||||||
|
// CHECK: dealloc_stack %[[V1]] : $*ARCStrong
|
||||||
|
// CHECK: return %[[V6]] : $()
|
||||||
|
|
||||||
|
public func testConstRefStrong() {
|
||||||
|
cfuncConstRefStrong({S in });
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSo10NonTrivialVIegn_ABIeyBn_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed (@in_guaranteed NonTrivial) -> (), @in_guaranteed NonTrivial) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*@block_storage @callee_guaranteed (@in_guaranteed NonTrivial) -> (), %[[V1:.*]] : $*NonTrivial):
|
||||||
|
// CHECK: %[[V2:.*]] = project_block_storage %[[V0]] : $*@block_storage @callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: %[[V3:.*]] = load [copy] %[[V2]] : $*@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: %[[V4:.*]] = begin_borrow %[[V3]] : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: apply %[[V4]](%[[V1]]) : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: end_borrow %[[V4]] : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
// CHECK: destroy_value %[[V3]] : $@callee_guaranteed (@in_guaranteed NonTrivial) -> ()
|
||||||
|
|
||||||
|
public func testBlockConstRefNonTrivial() {
|
||||||
|
blockConstRefNonTrivial({S in });
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSo7TrivialVIegy_ABIeyBn_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed (Trivial) -> (), @in_guaranteed Trivial) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*@block_storage @callee_guaranteed (Trivial) -> (), %[[V1:.*]] : $*Trivial):
|
||||||
|
// CHECK: %[[V2:.*]] = project_block_storage %[[V0]] : $*@block_storage @callee_guaranteed (Trivial) -> ()
|
||||||
|
// CHECK: %[[V3:.*]] = load [copy] %[[V2]] : $*@callee_guaranteed (Trivial) -> ()
|
||||||
|
// CHECK: %[[V4:.*]] = load [trivial] %[[V1]] : $*Trivial
|
||||||
|
// CHECK: %[[V5:.*]] = begin_borrow %[[V3]] : $@callee_guaranteed (Trivial) -> ()
|
||||||
|
// CHECK: apply %[[V5]](%[[V4]]) : $@callee_guaranteed (Trivial) -> ()
|
||||||
|
// CHECK: end_borrow %[[V5]] : $@callee_guaranteed (Trivial) -> ()
|
||||||
|
// CHECK: destroy_value %[[V3]] : $@callee_guaranteed (Trivial) -> ()
|
||||||
|
|
||||||
|
public func testBlockConstRefTrivial() {
|
||||||
|
blockConstRefTrivial({S in });
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$sSo9ARCStrongVIegg_ABIeyBn_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed (@guaranteed ARCStrong) -> (), @in_guaranteed ARCStrong) -> () {
|
||||||
|
// CHECK: bb0(%[[V0:.*]] : $*@block_storage @callee_guaranteed (@guaranteed ARCStrong) -> (), %[[V1:.*]] : $*ARCStrong):
|
||||||
|
// CHECK: %[[V2:.*]] = project_block_storage %[[V0]] : $*@block_storage @callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: %[[V3:.*]] = load [copy] %[[V2]] : $*@callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: %[[V4:.*]] = load_borrow %[[V1]] : $*ARCStrong
|
||||||
|
// CHECK: %[[V5:.*]] = begin_borrow %[[V3]] : $@callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: apply %[[V5]](%[[V4]]) : $@callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: end_borrow %[[V5]] : $@callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
// CHECK: end_borrow %[[V4]] : $ARCStrong
|
||||||
|
// CHECK: destroy_value %[[V3]] : $@callee_guaranteed (@guaranteed ARCStrong) -> ()
|
||||||
|
|
||||||
|
public func testBlockConstRefStrong() {
|
||||||
|
blockConstRefStrong({S in });
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,12 +64,11 @@ StdFunctionTestSuite.test("FunctionStringToString init from closure and pass as
|
|||||||
expectEqual(std.string("prefixabcabc"), res)
|
expectEqual(std.string("prefixabcabc"), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: assertion for address-only closure params (rdar://124501345)
|
StdFunctionTestSuite.test("FunctionStringToStringConstRef init from closure and pass as parameter") {
|
||||||
//StdFunctionTestSuite.test("FunctionStringToStringConstRef init from closure and pass as parameter") {
|
let res = invokeFunctionTwiceConstRef(.init({ $0 + std.string("abc") }),
|
||||||
// let res = invokeFunctionTwiceConstRef(.init({ $0 + std.string("abc") }),
|
std.string("prefix"))
|
||||||
// std.string("prefix"))
|
expectEqual(std.string("prefixabcabc"), res)
|
||||||
// expectEqual(std.string("prefixabcabc"), res)
|
}
|
||||||
//}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
runAllTests()
|
runAllTests()
|
||||||
|
|||||||
Reference in New Issue
Block a user