Skip to content

Commit 549f9c9

Browse files
authored
Merge pull request #3376 from jckarter/id-as-any-silgen-peephole
SILGen: Look through ErasureExprs when bridging to AnyObject.
2 parents 789930b + e6ba22f commit 549f9c9

File tree

4 files changed

+166
-6
lines changed

4 files changed

+166
-6
lines changed

lib/SIL/Bridging.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ Type TypeConverter::getLoweredCBridgedType(AbstractionPattern pattern,
170170
}
171171
}
172172

173+
// `Any` can bridge to `AnyObject` (`id` in ObjC).
174+
if (Context.LangOpts.EnableIdAsAny && t->isAny()) {
175+
return Context.getProtocol(KnownProtocolKind::AnyObject)->getDeclaredType();
176+
}
177+
173178
if (auto funTy = t->getAs<FunctionType>()) {
174179
switch (funTy->getExtInfo().getSILRepresentation()) {
175180
// Functions that are already represented as blocks or C function pointers

lib/SILGen/SILGenApply.cpp

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3053,16 +3053,30 @@ namespace {
30533053
emitted.contextForReabstraction);
30543054
}
30553055

3056+
CanType getAnyObjectType() {
3057+
return SGF.getASTContext()
3058+
.getProtocol(KnownProtocolKind::AnyObject)
3059+
->getDeclaredType()
3060+
->getCanonicalType();
3061+
}
3062+
bool isAnyObjectType(CanType t) {
3063+
return t == getAnyObjectType();
3064+
}
3065+
30563066
ManagedValue emitNativeToBridgedArgument(ArgumentSource &&arg,
30573067
SILType loweredSubstArgType,
30583068
AbstractionPattern origParamType,
30593069
SILParameterInfo param) {
3060-
// TODO: We should take the opportunity to peephole certain sequences
3061-
// here. For instance, when going from concrete type -> Any -> id, we
3062-
// can skip the intermediate 'Any' boxing and directly bridge the concrete
3063-
// type to its object representation. Similarly, when bridging from
3064-
// NSFoo -> Foo -> NSFoo, we should elide the bridge altogether and pass
3065-
// the original object.
3070+
// If we're bridging a concrete type to `id` via Any, skip the Any
3071+
// boxing.
3072+
// TODO: Generalize. Similarly, when bridging from NSFoo -> Foo -> NSFoo,
3073+
// we should elide the bridge altogether and pass the original object.
3074+
if (isAnyObjectType(param.getType()) && !arg.isRValue()) {
3075+
return emitNativeToBridgedObjectArgument(std::move(arg).asKnownExpr(),
3076+
loweredSubstArgType,
3077+
origParamType, param);
3078+
}
3079+
30663080
auto emitted = emitArgumentFromSource(std::move(arg), loweredSubstArgType,
30673081
origParamType, param);
30683082

@@ -3072,6 +3086,67 @@ namespace {
30723086

30733087
}
30743088

3089+
/// Emit an argument expression that we know will be bridged to an
3090+
/// Objective-C object.
3091+
ManagedValue emitNativeToBridgedObjectArgument(Expr *argExpr,
3092+
SILType loweredSubstArgType,
3093+
AbstractionPattern origParamType,
3094+
SILParameterInfo param) {
3095+
argExpr = argExpr->getSemanticsProvidingExpr();
3096+
3097+
// Look through ErasureExprs and try to bridge the underlying
3098+
// concrete value instead.
3099+
while (auto erasure = dyn_cast<ErasureExpr>(argExpr))
3100+
argExpr = erasure->getSubExpr();
3101+
3102+
// Emit the argument.
3103+
auto contexts = getRValueEmissionContexts(loweredSubstArgType, param);
3104+
ManagedValue emittedArg = SGF.emitRValue(argExpr, contexts.ForEmission)
3105+
.getScalarValue();
3106+
3107+
// If the argument is not already a class instance, bridge it.
3108+
if (!argExpr->getType()->mayHaveSuperclass()
3109+
&& !argExpr->getType()->isClassExistentialType()) {
3110+
emittedArg = SGF.emitNativeToBridgedValue(argExpr, emittedArg,
3111+
Rep, param.getType());
3112+
}
3113+
auto emittedArgTy = emittedArg.getType().getSwiftRValueType();
3114+
assert(emittedArgTy->mayHaveSuperclass()
3115+
|| emittedArgTy->isClassExistentialType());
3116+
3117+
// Upcast reference types to AnyObject.
3118+
if (!isAnyObjectType(emittedArgTy)) {
3119+
// Open class existentials first to upcast the reference inside.
3120+
if (emittedArgTy->isClassExistentialType()) {
3121+
emittedArgTy = ArchetypeType::getOpened(emittedArgTy);
3122+
auto opened = SGF.B.createOpenExistentialRef(argExpr,
3123+
emittedArg.getValue(),
3124+
SILType::getPrimitiveObjectType(emittedArgTy));
3125+
emittedArg = ManagedValue(opened, emittedArg.getCleanup());
3126+
}
3127+
3128+
// Erase to AnyObject.
3129+
auto conformance = SGF.SGM.SwiftModule->lookupConformance(
3130+
emittedArgTy,
3131+
SGF.getASTContext().getProtocol(KnownProtocolKind::AnyObject),
3132+
nullptr);
3133+
assert(conformance &&
3134+
"no AnyObject conformance for class?!");
3135+
3136+
ArrayRef<ProtocolConformanceRef> conformances(*conformance);
3137+
auto ctxConformances = SGF.getASTContext().AllocateCopy(conformances);
3138+
3139+
auto erased = SGF.B.createInitExistentialRef(argExpr,
3140+
SILType::getPrimitiveObjectType(getAnyObjectType()),
3141+
emittedArgTy, emittedArg.getValue(),
3142+
ctxConformances);
3143+
emittedArg = ManagedValue(erased, emittedArg.getCleanup());
3144+
}
3145+
3146+
assert(isAnyObjectType(emittedArg.getSwiftType()));
3147+
return emittedArg;
3148+
}
3149+
30753150
struct EmittedArgument {
30763151
SILLocation loc;
30773152
RValue value;

test/Inputs/clang-importer-sdk/usr/include/Foundation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,9 @@ extern NSString *NSHTTPRequestKey;
10391039
- (id _Nonnull)makesId;
10401040
- (void)takesId:(id _Nonnull)x;
10411041
- (void)takesArrayOfId:(const id _Nonnull * _Nonnull)x;
1042+
- (void)takesNullableId:(id _Nullable)x;
1043+
1044+
@property (strong) id propertyOfId;
10421045

10431046
@end
10441047

test/SILGen/objc_bridging_any.swift

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-id-as-any -emit-silgen %s | FileCheck %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
protocol P {}
7+
protocol CP: class {}
8+
9+
// CHECK-LABEL: sil hidden @_TF17objc_bridging_any11passingToId
10+
func passingToId<T: CP, U>(receiver: IdLover,
11+
string: String,
12+
nsString: NSString,
13+
object: AnyObject,
14+
classGeneric: T,
15+
classExistential: CP,
16+
generic: U,
17+
existential: P,
18+
any: Any) {
19+
// CHECK: [[METHOD:%.*]] = class_method [volatile] [[SELF:%.*]] : $IdLover,
20+
// CHECK: [[BRIDGE_STRING:%.*]] = function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
21+
// CHECK: [[NSSTRING:%.*]] = apply [[BRIDGE_STRING]]
22+
// CHECK: [[ANYOBJECT:%.*]] = init_existential_ref [[NSSTRING]] : $NSString : $NSString, $AnyObject
23+
// CHECK: apply [[METHOD]]([[ANYOBJECT]], [[SELF]])
24+
receiver.takesId(string)
25+
26+
// CHECK: [[METHOD:%.*]] = class_method [volatile] [[SELF:%.*]] : $IdLover,
27+
// CHECK: [[ANYOBJECT:%.*]] = init_existential_ref {{%.*}} : $NSString : $NSString, $AnyObject
28+
// CHECK: apply [[METHOD]]([[ANYOBJECT]], [[SELF]])
29+
receiver.takesId(nsString)
30+
31+
// CHECK: [[METHOD:%.*]] = class_method [volatile] [[SELF:%.*]] : $IdLover,
32+
// CHECK: [[ANYOBJECT:%.*]] = init_existential_ref {{%.*}} : $T : $T, $AnyObject
33+
// CHECK: apply [[METHOD]]([[ANYOBJECT]], [[SELF]])
34+
receiver.takesId(classGeneric)
35+
36+
// TODO: Need to look through an (open_existential (erasure)) combo to upcast
37+
// an existential to Any.
38+
/*
39+
receiver.takesId(object)
40+
receiver.takesId(classExistential)
41+
*/
42+
43+
// TODO: These cases need to perform a (to-be-implemented) universal
44+
// bridging conversion.
45+
/*
46+
receiver.takesId(generic)
47+
receiver.takesId(existential)
48+
receiver.takesId(any)
49+
*/
50+
51+
// TODO: Property and subscript setters
52+
}
53+
54+
// TODO: Look through value-to-optional and optional-to-optional conversions.
55+
/*
56+
func passingToNullableId(receiver: IdLover,
57+
string: String,
58+
nsString: NSString,
59+
object: AnyObject,
60+
any: Any,
61+
optString: String?,
62+
optNSString: NSString?,
63+
optObject: AnyObject?,
64+
optAny: Any?)
65+
{
66+
receiver.takesNullableId(string)
67+
receiver.takesNullableId(nsString)
68+
receiver.takesNullableId(object)
69+
receiver.takesNullableId(any)
70+
receiver.takesNullableId(optString)
71+
receiver.takesNullableId(optNSString)
72+
receiver.takesNullableId(optObject)
73+
receiver.takesNullableId(optAny)
74+
}
75+
*/
76+
77+
// TODO: casting from id, nullable or not

0 commit comments

Comments
 (0)