Skip to content

Commit cf9e0d2

Browse files
committed
Sema: Introduce metatype-to-object conversions.
Allow class metatypes (including class-constrained existential metatypes) to be treated as subtypes of AnyObject, and single-@objc protocol metatypes to be treated as subtypes of the Protocol class from objc. No codegen support yet, so this is hidden behind a frontend flag for now. Swift SVN r18810
1 parent a4ea927 commit cf9e0d2

15 files changed

+212
-1
lines changed

include/swift/AST/Expr.h

+34
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,40 @@ class ImplicitConversionExpr : public Expr {
18451845
}
18461846
};
18471847

1848+
/// The implicit conversion from a class metatype to AnyObject.
1849+
class ClassMetatypeToObjectExpr : public ImplicitConversionExpr {
1850+
public:
1851+
ClassMetatypeToObjectExpr(Expr *subExpr, Type ty)
1852+
: ImplicitConversionExpr(ExprKind::ClassMetatypeToObject, subExpr, ty) {}
1853+
1854+
static bool classof(const Expr *E) {
1855+
return E->getKind() == ExprKind::ClassMetatypeToObject;
1856+
}
1857+
};
1858+
1859+
/// The implicit conversion from a class existential metatype to AnyObject.
1860+
class ExistentialMetatypeToObjectExpr : public ImplicitConversionExpr {
1861+
public:
1862+
ExistentialMetatypeToObjectExpr(Expr *subExpr, Type ty)
1863+
: ImplicitConversionExpr(ExprKind::ClassMetatypeToObject, subExpr, ty) {}
1864+
1865+
static bool classof(const Expr *E) {
1866+
return E->getKind() == ExprKind::ExistentialMetatypeToObject;
1867+
}
1868+
};
1869+
1870+
/// The implicit conversion from a protocol value metatype to ObjC's Protocol
1871+
/// class type.
1872+
class ProtocolMetatypeToObjectExpr : public ImplicitConversionExpr {
1873+
public:
1874+
ProtocolMetatypeToObjectExpr(Expr *subExpr, Type ty)
1875+
: ImplicitConversionExpr(ExprKind::ProtocolMetatypeToObject, subExpr, ty) {}
1876+
1877+
static bool classof(const Expr *E) {
1878+
return E->getKind() == ExprKind::ProtocolMetatypeToObject;
1879+
}
1880+
};
1881+
18481882
/// InjectIntoOptionalExpr - The implicit conversion from T to T?.
18491883
class InjectIntoOptionalExpr : public ImplicitConversionExpr {
18501884
public:

include/swift/AST/ExprNodes.def

+3
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
129129
EXPR(ArchetypeToSuper, ImplicitConversionExpr)
130130
EXPR(ScalarToTuple, ImplicitConversionExpr)
131131
EXPR(InjectIntoOptional, ImplicitConversionExpr)
132+
EXPR(ClassMetatypeToObject, ImplicitConversionExpr)
133+
EXPR(ExistentialMetatypeToObject, ImplicitConversionExpr)
134+
EXPR(ProtocolMetatypeToObject, ImplicitConversionExpr)
132135
EXPR(LValueConversion, ImplicitConversionExpr)
133136
EXPR(LValueToPointer, ImplicitConversionExpr)
134137
EXPR_RANGE(ImplicitConversion, Load, LValueToPointer)

include/swift/AST/KnownIdentifiers.def

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ IDENTIFIER(hashValue)
3434
IDENTIFIER(init)
3535
IDENTIFIER(objectAtIndexedSubscript)
3636
IDENTIFIER(objectForKeyedSubscript)
37+
IDENTIFIER(ObjectiveC)
3738
IDENTIFIER(Protocol)
3839
IDENTIFIER(raw)
3940
IDENTIFIER(RawType)

include/swift/Basic/LangOptions.h

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ namespace swift {
5050
/// \brief Enable experimental "switch" pattern-matching features.
5151
bool EnableExperimentalPatterns = false;
5252

53+
/// \brief Enable ObjC compatibility metatype-to-class conversions.
54+
bool EnableMetatypeToObjectConversions = false;
55+
5356
/// \brief Enable features useful for running in the debugger.
5457
bool DebuggerSupport = false;
5558

include/swift/Driver/FrontendOptions.td

+3
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ def enable_dynamic_value_type_layout :
123123
def enable_experimental_patterns : Flag<["-"], "enable-experimental-patterns">,
124124
HelpText<"Enable experimental 'switch' pattern matching features">;
125125

126+
def enable_metatype_object_conversions : Flag<["-"], "enable-metatype-object-conversions">,
127+
HelpText<"Enable metatype-to-object conversions">;
128+
126129
def enable_character_literals : Flag<["-"], "enable-character-literals">,
127130
HelpText<"Enable legacy character literals">;
128131

lib/AST/ASTDumper.cpp

+16-1
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
10761076
void visitErrorExpr(ErrorExpr *E) {
10771077
printCommon(E, "error_expr") << ')';
10781078
}
1079-
1079+
10801080
void visitIntegerLiteralExpr(IntegerLiteralExpr *E) {
10811081
printCommon(E, "integer_literal_expr");
10821082
if (E->isNegative())
@@ -1439,6 +1439,21 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
14391439
printRec(E->getSubExpr());
14401440
OS << ')';
14411441
}
1442+
void visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *E) {
1443+
printCommon(E, "class_metatype_to_object") << '\n';
1444+
printRec(E->getSubExpr());
1445+
OS << ')';
1446+
}
1447+
void visitExistentialMetatypeToObjectExpr(ExistentialMetatypeToObjectExpr *E) {
1448+
printCommon(E, "existential_metatype_to_object") << '\n';
1449+
printRec(E->getSubExpr());
1450+
OS << ')';
1451+
}
1452+
void visitProtocolMetatypeToObjectExpr(ProtocolMetatypeToObjectExpr *E) {
1453+
printCommon(E, "protocol_metatype_to_object") << '\n';
1454+
printRec(E->getSubExpr());
1455+
OS << ')';
1456+
}
14421457

14431458
void visitInOutExpr(InOutExpr *E) {
14441459
printCommon(E, "inout_expr") << '\n';

lib/Frontend/CompilerInvocation.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
536536

537537
Opts.EnableExperimentalPatterns |= Args.hasArg(OPT_enable_experimental_patterns);
538538

539+
Opts.EnableMetatypeToObjectConversions
540+
|= Args.hasArg(OPT_enable_metatype_object_conversions);
541+
539542
Opts.EnableCharacterLiterals |= Args.hasArg(OPT_enable_character_literals);
540543

541544
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);

lib/SILGen/SILGenExpr.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,12 @@ namespace {
221221
RValue visitInjectIntoOptionalExpr(InjectIntoOptionalExpr *E, SGFContext C);
222222
RValue visitLValueConversionExpr(LValueConversionExpr *E, SGFContext C);
223223
RValue visitLValueToPointerExpr(LValueToPointerExpr *E, SGFContext C);
224+
RValue visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *E,
225+
SGFContext C);
226+
RValue visitExistentialMetatypeToObjectExpr(ExistentialMetatypeToObjectExpr *E,
227+
SGFContext C);
228+
RValue visitProtocolMetatypeToObjectExpr(ProtocolMetatypeToObjectExpr *E,
229+
SGFContext C);
224230
RValue visitIfExpr(IfExpr *E, SGFContext C);
225231

226232
RValue visitDefaultValueExpr(DefaultValueExpr *E, SGFContext C);
@@ -4202,6 +4208,23 @@ RValue RValueEmitter::visitLValueToPointerExpr(LValueToPointerExpr *E,
42024208
return RValue(SGF, E, ManagedValue::forUnmanaged(ptr));
42034209
}
42044210

4211+
RValue RValueEmitter::visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *E,
4212+
SGFContext C) {
4213+
llvm_unreachable("not implemented");
4214+
}
4215+
4216+
RValue RValueEmitter::visitExistentialMetatypeToObjectExpr(
4217+
ExistentialMetatypeToObjectExpr *E,
4218+
SGFContext C) {
4219+
llvm_unreachable("not implemented");
4220+
}
4221+
4222+
RValue RValueEmitter::visitProtocolMetatypeToObjectExpr(
4223+
ProtocolMetatypeToObjectExpr *E,
4224+
SGFContext C) {
4225+
llvm_unreachable("not implemented");
4226+
}
4227+
42054228
RValue RValueEmitter::visitInOutConversionExpr(InOutConversionExpr *E,
42064229
SGFContext C) {
42074230
// Disable nested writeback scopes for any calls evaluated in the

lib/Sema/CSApply.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -3960,6 +3960,16 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
39603960
case ConversionRestrictionKind::Existential:
39613961
return coerceExistential(expr, toType, locator);
39623962

3963+
case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
3964+
return new (tc.Context) ClassMetatypeToObjectExpr(expr, toType);
3965+
}
3966+
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: {
3967+
return new (tc.Context) ExistentialMetatypeToObjectExpr(expr, toType);
3968+
}
3969+
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: {
3970+
return new (tc.Context) ProtocolMetatypeToObjectExpr(expr, toType);
3971+
}
3972+
39633973
case ConversionRestrictionKind::ValueToOptional: {
39643974
auto toGenericType = toType->castTo<BoundGenericType>();
39653975
assert(toGenericType->getDecl()->classifyAsOptionalType());

lib/Sema/CSSimplify.cpp

+60
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,58 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
16311631
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
16321632
}
16331633

1634+
// Metatype to object conversion.
1635+
1636+
if (getASTContext().LangOpts.EnableMetatypeToObjectConversions) {
1637+
// These conversions are between concrete types that don't need further
1638+
// resolution, so we can consider them immediately solved.
1639+
auto addSolvedRestrictedConstraint
1640+
= [&](ConversionRestrictionKind restriction) -> SolutionKind {
1641+
auto constraint = Constraint::createRestricted(*this,
1642+
ConstraintKind::Subtype,
1643+
restriction,
1644+
type1, type2,
1645+
getConstraintLocator(locator));
1646+
this->addConstraint(constraint);
1647+
return SolutionKind::Solved;
1648+
};
1649+
1650+
if (auto meta1 = type1->getAs<MetatypeType>()) {
1651+
// Class metatypes can be converted to AnyObject.
1652+
if (meta1->getInstanceType()->mayHaveSuperclass()
1653+
&& type2->isAnyObject()) {
1654+
return addSolvedRestrictedConstraint(
1655+
ConversionRestrictionKind::ClassMetatypeToAnyObject);
1656+
}
1657+
// Single @objc protocol value metatypes can be converted to the ObjC
1658+
// Protocol class type.
1659+
auto isProtocolClassType = [&](Type t) -> bool {
1660+
if (auto classDecl = t->getClassOrBoundGenericClass())
1661+
if (classDecl->getName() == getASTContext().Id_Protocol
1662+
&& classDecl->getModuleContext()->Name
1663+
== getASTContext().Id_ObjectiveC)
1664+
return true;
1665+
return false;
1666+
};
1667+
1668+
if (auto protoTy = meta1->getInstanceType()->getAs<ProtocolType>()) {
1669+
if (protoTy->getDecl()->isObjC()
1670+
&& isProtocolClassType(type2)) {
1671+
return addSolvedRestrictedConstraint(
1672+
ConversionRestrictionKind::ProtocolMetatypeToProtocolClass);
1673+
}
1674+
}
1675+
}
1676+
if (auto meta1 = type1->getAs<ExistentialMetatypeType>()) {
1677+
// Class-constrained existential metatypes can be converted to AnyObject.
1678+
if (meta1->getInstanceType()->isClassExistentialType()
1679+
&& type2->isAnyObject()) {
1680+
return addSolvedRestrictedConstraint(
1681+
ConversionRestrictionKind::ExistentialMetatypeToAnyObject);
1682+
}
1683+
}
1684+
}
1685+
16341686
// Implicit array conversions.
16351687
if (kind >= TypeMatchKind::Conversion) {
16361688
if (isArrayType(desugar1) && isArrayType(desugar2)) {
@@ -3035,6 +3087,14 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
30353087
matchKind, subFlags, locator);
30363088
}
30373089

3090+
case ConversionRestrictionKind::ClassMetatypeToAnyObject:
3091+
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
3092+
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: {
3093+
// Nothing more to solve.
3094+
addContextualScore();
3095+
return SolutionKind::Solved;
3096+
}
3097+
30383098
// T < U ===> Array<T> <c Array<U>
30393099
case ConversionRestrictionKind::ArrayUpcast: {
30403100
auto t1 = type1->getDesugaredType();

lib/Sema/Constraint.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
294294
return "[unchecked-optional-to-optional]";
295295
case ConversionRestrictionKind::OptionalToImplicitlyUnwrappedOptional:
296296
return "[optional-to-unchecked-optional]";
297+
case ConversionRestrictionKind::ClassMetatypeToAnyObject:
298+
return "[class-metatype-to-object]";
299+
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
300+
return "[existential-metatype-to-object]";
301+
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass:
302+
return "[protocol-metatype-to-object]";
297303
case ConversionRestrictionKind::ForceUnchecked:
298304
return "[force-unchecked]";
299305
case ConversionRestrictionKind::ArrayUpcast:

lib/Sema/Constraint.h

+7
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ enum class ConversionRestrictionKind {
149149
DeepEquality,
150150
/// Subclass-to-superclass conversion.
151151
Superclass,
152+
/// Class metatype to AnyObject conversion.
153+
ClassMetatypeToAnyObject,
154+
/// Existential metatype to AnyObject conversion.
155+
ExistentialMetatypeToAnyObject,
156+
/// Protocol value metatype to Protocol class conversion.
157+
ProtocolMetatypeToProtocolClass,
158+
/// Existential metatype to AnyObject conversion.
152159
/// Lvalue-to-rvalue conversion.
153160
LValueToRValue,
154161
/// Value to existential value conversion.

lib/Sema/MiscDiagnostics.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ static void diagModuleOrMetatypeValue(TypeChecker &TC, const Expr *E) {
204204
case ExprKind::OpaqueValue:
205205
case ExprKind::BindOptional:
206206
case ExprKind::OptionalEvaluation:
207+
case ExprKind::ClassMetatypeToObject:
208+
case ExprKind::ProtocolMetatypeToObject:
209+
case ExprKind::ExistentialMetatypeToObject:
207210
case ExprKind::ForceValue:
208211
case ExprKind::OpenExistential:
209212
case ExprKind::PrefixUnary:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: rm -rf %t/clang-module-cache
2+
// RUN: %swift %clang-importer-sdk -enable-metatype-object-conversions -parse -verify -module-cache-path %t/clang-module-cache -target x86_64-apple-darwin13 %s
3+
4+
import ObjectiveC
5+
6+
@objc protocol ObjCProto {}
7+
@objc protocol ObjCProto2 {}
8+
protocol NonObjCProto {}
9+
typealias TwoObjCProtos = protocol<ObjCProto, ObjCProto2>
10+
11+
func takesProtocol(x: Protocol) {} // expected-note{{}} expected-note{{}}
12+
13+
takesProtocol(ObjCProto.self)
14+
takesProtocol(ObjCProto2.self)
15+
takesProtocol(NonObjCProto.self) // expected-error{{'NonObjCProto.Protocol' is not convertible to 'Protocol'}}
16+
takesProtocol(TwoObjCProtos.self) // expected-error{{'TwoObjCProtos.Protocol' is not convertible to 'Protocol'}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %swift -enable-metatype-object-conversions -parse -verify %s
2+
3+
class C {}
4+
struct S {}
5+
6+
protocol NonClassProto {}
7+
@class_protocol protocol ClassConstrainedProto {}
8+
9+
func takesAnyObject(x: AnyObject) {} // expected-note{{}} // expected-note{{}} // expected-note{{}}
10+
11+
func concreteTypes() {
12+
takesAnyObject(C.self)
13+
// TODO: Better error messages
14+
takesAnyObject(S.self) // expected-error{{'S.Type' does not conform to protocol 'AnyObject'}}
15+
takesAnyObject(ClassConstrainedProto.self) // expected-error{{'ClassConstrainedProto.Protocol' does not conform to protocol 'AnyObject'}}
16+
}
17+
18+
func existentialMetatypes(nonClass: NonClassProto.Type,
19+
classConstrained: ClassConstrainedProto.Type,
20+
compo: protocol<NonClassProto, ClassConstrainedProto>.Type) {
21+
takesAnyObject(nonClass) // expected-error{{'NonClassProto.Type' does not conform to protocol 'AnyObject'}}
22+
takesAnyObject(classConstrained)
23+
takesAnyObject(compo)
24+
}

0 commit comments

Comments
 (0)