Skip to content

Commit 8c2631a

Browse files
authored
Merge pull request #3294 from jckarter/id-as-any-import
Initial 'id as Any' Clang importer implementation
2 parents 7b1a8dc + 3e56942 commit 8c2631a

File tree

10 files changed

+94
-5
lines changed

10 files changed

+94
-5
lines changed

include/swift/AST/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
402402
/// hasReferenceSemantics() - Do objects of this type have reference
403403
/// semantics?
404404
bool hasReferenceSemantics();
405+
406+
/// Is this the 'Any' type?
407+
bool isAny();
405408

406409
/// Are values of this type essentially just class references,
407410
/// possibly with some sort of additional information?

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ namespace swift {
157157
/// Whether we are stripping the "NS" prefix from Foundation et al.
158158
bool StripNSPrefix = true;
159159

160+
/// Should 'id' in Objective-C be imported as 'Any' in Swift?
161+
bool EnableIdAsAny = false;
162+
160163
/// Enable the Swift 3 migration via Fix-Its.
161164
bool Swift3Migration = false;
162165

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,5 +347,8 @@ def group_info_path : Separate<["-"], "group-info-path">,
347347

348348
def enable_protocol_typealiases: Flag<["-"], "enable-protocol-typealiases">,
349349
HelpText<"Enable typealiases inside protocol declarations">;
350+
351+
def enable_id_as_any: Flag<["-"], "enable-id-as-any">,
352+
HelpText<"Enable importing ObjC 'id' as Swift 'Any' type">;
350353

351354
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]

lib/AST/Type.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ bool TypeBase::hasReferenceSemantics() {
8989
return getCanonicalType().hasReferenceSemantics();
9090
}
9191

92+
bool TypeBase::isAny() {
93+
if (auto comp = getAs<ProtocolCompositionType>())
94+
if (comp->getProtocols().empty())
95+
return true;
96+
97+
return false;
98+
}
99+
92100
bool TypeBase::isAnyClassReferenceType() {
93101
return getCanonicalType().isAnyClassReferenceType();
94102
}
@@ -1998,6 +2006,13 @@ getObjCObjectRepresentable(Type type, const DeclContext *dc) {
19982006
if (type->isObjCExistentialType())
19992007
return ForeignRepresentableKind::Object;
20002008

2009+
// Any can be bridged to id.
2010+
if (type->getASTContext().LangOpts.EnableIdAsAny) {
2011+
if (type->isAny()) {
2012+
return ForeignRepresentableKind::Bridged;
2013+
}
2014+
}
2015+
20012016
// Class-constrained generic parameters, from ObjC generic classes.
20022017
if (auto tyContext = dc->getInnermostTypeContext())
20032018
if (auto clas = tyContext->getAsClassOrClassExtensionContext())

lib/ClangImporter/ImportDecl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,16 @@ namespace {
15231523

15241524
Type SwiftType;
15251525
if (Decl->getDeclContext()->getRedeclContext()->isTranslationUnit()) {
1526+
// Ignore the 'id' typedef. We want to bridge the underlying
1527+
// ObjCId type.
1528+
//
1529+
// When we remove the EnableIdAsAny staging flag, the 'id' entry
1530+
// should be removed from MappedTypes.def, and this conditional should
1531+
// become unnecessary.
1532+
if (Name.str() == "id" && Impl.SwiftContext.LangOpts.EnableIdAsAny) {
1533+
return nullptr;
1534+
}
1535+
15261536
bool IsError;
15271537
StringRef StdlibTypeName;
15281538
MappedTypeNameKind NameMapping;

lib/ClangImporter/ImportType.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -926,9 +926,15 @@ namespace {
926926
if (!proto)
927927
return Type();
928928

929-
// id maps to AnyObject.
929+
// id maps to Any in bridgeable contexts, AnyObject otherwise.
930930
if (type->isObjCIdType()) {
931-
return { proto->getDeclaredType(), ImportHint::ObjCPointer };
931+
if (Impl.SwiftContext.LangOpts.EnableIdAsAny) {
932+
return { proto->getDeclaredType(),
933+
ImportHint(ImportHint::ObjCBridged,
934+
ProtocolCompositionType::get(Impl.SwiftContext, {})) };
935+
} else {
936+
return { proto->getDeclaredType(), ImportHint::ObjCPointer };
937+
}
932938
}
933939

934940
// Class maps to AnyObject.Type.
@@ -1207,9 +1213,13 @@ static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
12071213

12081214
// If we have a bridged Objective-C type and we are allowed to
12091215
// bridge, do so.
1210-
if (hint == ImportHint::ObjCBridged && canBridgeTypes(importKind) &&
1211-
(impl.tryLoadFoundationModule() || impl.ImportForwardDeclarations))
1212-
importedType = hint.BridgedType;
1216+
if (hint == ImportHint::ObjCBridged && canBridgeTypes(importKind))
1217+
// id and Any can be bridged without Foundation. There would be
1218+
// bootstrapping issues with the ObjectiveC module otherwise.
1219+
if (hint.BridgedType->isAny()
1220+
|| impl.tryLoadFoundationModule()
1221+
|| impl.ImportForwardDeclarations)
1222+
importedType = hint.BridgedType;
12131223

12141224
if (!importedType)
12151225
return importedType;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
776776

777777
Opts.EnableThrowWithoutTry |= Args.hasArg(OPT_enable_throw_without_try);
778778
Opts.EnableProtocolTypealiases |= Args.hasArg(OPT_enable_protocol_typealiases);
779+
Opts.EnableIdAsAny |= Args.hasArg(OPT_enable_id_as_any);
779780

780781
if (auto A = Args.getLastArg(OPT_enable_objc_attr_requires_foundation_module,
781782
OPT_disable_objc_attr_requires_foundation_module)) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse -verify -enable-id-as-any %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
func assertTypeIsAny(_: Any.Protocol) {}
7+
func staticType<T>(_: T) -> T.Type { return T.self }
8+
9+
let idLover = IdLover()
10+
11+
let t1 = staticType(idLover.makesId())
12+
assertTypeIsAny(t1)
13+
14+
struct ArbitraryThing {}
15+
idLover.takesId(ArbitraryThing())
16+
17+
var x: AnyObject = NSObject()
18+
idLover.takesArray(ofId: &x)
19+
var y: Any = NSObject()
20+
idLover.takesArray(ofId: &y) // expected-error{{}}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,14 @@ extern NSString *NSHTTPRequestKey;
10341034

10351035
@end
10361036

1037+
@interface NSIdLover: NSObject
1038+
1039+
- (id _Nonnull)makesId;
1040+
- (void)takesId:(id _Nonnull)x;
1041+
- (void)takesArrayOfId:(const id _Nonnull * _Nonnull)x;
1042+
1043+
@end
1044+
10371045
#define NSTimeIntervalSince1970 978307200.0
10381046
#define NS_DO_SOMETHING 17
10391047

test/attr/attr_objc_any.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -parse -verify %s -enable-id-as-any
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
@objc var foo: Any // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}}
7+
8+
class Foo: NSObject {
9+
override init() {}
10+
11+
@objc var property: Any
12+
13+
@objc func method(x: Any) -> Any { return x }
14+
15+
@objc func indirectAny(x: UnsafePointer<Any>) {} // expected-error{{type of the parameter cannot be represented in Objective-C}}
16+
}

0 commit comments

Comments
 (0)