Skip to content

Commit

Permalink
Handle CF <-> Objective-C toll-free-bridged conversions in the type c…
Browse files Browse the repository at this point in the history
…hecker.

Introduce an attribute that describes when a given CF type is
toll-free-bridged to an Objective-C class, and which class that
is. Use that information in the type checker to provide the CF <->
Objective-C toll-free-bridged conversions directly, rather than using
the user-defined conversion machinery.

Swift SVN r21376
  • Loading branch information
DougGregor committed Aug 21, 2014
1 parent 2886625 commit 1771652
Show file tree
Hide file tree
Showing 20 changed files with 205 additions and 81 deletions.
2 changes: 2 additions & 0 deletions include/swift/AST/Attr.def
Expand Up @@ -192,6 +192,8 @@ DECL_ATTR(effects, Effects, OnFunc, 50)

DECL_ATTR_ALIAS(unowned, Ownership)

DECL_ATTR(__objc_bridged, ObjCBridged, OnClass | NotSerialized,
/* Not serialized */51)

// Reordering these platforms will break serialization.
AVAILABILITY_PLATFORM(iOS, "iOS")
Expand Down
26 changes: 26 additions & 0 deletions include/swift/AST/Attr.h
Expand Up @@ -31,6 +31,7 @@ class ASTPrinter;
class ASTContext;
struct PrintOptions;
class Decl;
class ClassDecl;

/// The associativity of a binary operator.
enum class Associativity {
Expand Down Expand Up @@ -945,6 +946,31 @@ class RawDocCommentAttr : public DeclAttribute {
}
};

/// An attribute applied to a CoreFoundation class that is toll-free bridged to
/// an Objective-C class.
///
/// This attribute is introduced by the Clang importer, and is therefore always
/// implicit.
class ObjCBridgedAttr : public DeclAttribute {
ClassDecl *ObjCClass;

public:
ObjCBridgedAttr(ClassDecl *ObjCClass)
: DeclAttribute(DAK_ObjCBridged, SourceLoc(), SourceRange(),
/*Implicit=*/true),
ObjCClass(ObjCClass)
{
}

/// Retrieve the Objective-C class to which this foreign class is toll-free
/// bridged.
ClassDecl *getObjCClass() const { return ObjCClass; }

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_ObjCBridged;
}
};

/// \brief Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
Expand Down
11 changes: 11 additions & 0 deletions include/swift/AST/Expr.h
Expand Up @@ -2022,7 +2022,18 @@ class PointerToPointerExpr : public ImplicitConversionExpr {
return E->getKind() == ExprKind::PointerToPointer;
}
};

/// Convert between a foreign object and its corresponding Objective-C object.
class ForeignObjectConversionExpr : public ImplicitConversionExpr {
public:
ForeignObjectConversionExpr(Expr *subExpr, Type ty)
: ImplicitConversionExpr(ExprKind::ForeignObjectConversion, subExpr, ty) {}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::ForeignObjectConversion;
}
};

/// TupleShuffleExpr - This represents a permutation of a tuple value to a new
/// tuple type. The expression's type is known to be a tuple type and the
/// subexpression is known to have a tuple type as well.
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/ExprNodes.def
Expand Up @@ -137,7 +137,8 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
EXPR(StringToPointer, ImplicitConversionExpr)
EXPR(PointerToPointer, ImplicitConversionExpr)
EXPR(LValueToPointer, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, LValueToPointer)
EXPR(ForeignObjectConversion, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, ForeignObjectConversion)
ABSTRACT_EXPR(ExplicitCast, Expr)
ABSTRACT_EXPR(CheckedCast, ExplicitCastExpr)
UNCHECKED_EXPR(UnresolvedCheckedCast, CheckedCastExpr)
Expand Down
5 changes: 3 additions & 2 deletions include/swift/Serialization/ModuleFormat.h
Expand Up @@ -1058,10 +1058,11 @@ namespace decls_block {
using AccessibilityDeclAttrLayout = BCRecordLayout<Accessibility_DECL_ATTR>;
using SetterAccessibilityDeclAttrLayout =
BCRecordLayout<SetterAccessibility_DECL_ATTR>;
using ObjCBridgedDeclAttrLayout = BCRecordLayout<ObjCBridged_DECL_ATTR>;

using InlineDeclAttrLayout = BCRecordLayout<
Inline_DECL_ATTR,
BCFixed<2> // inline value
Inline_DECL_ATTR,
BCFixed<2> // inline value
>;

// Encodes a VersionTuple:
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/ASTDumper.cpp
Expand Up @@ -1526,6 +1526,11 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
printRec(E->getSubExpr());
OS << ')';
}
void visitForeignObjectConversionExpr(ForeignObjectConversionExpr *E) {
printCommon(E, "foreign_object_conversion") << '\n';
printRec(E->getSubExpr());
OS << ')';
}

void visitInOutExpr(InOutExpr *E) {
printCommon(E, "inout_expr") << '\n';
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Attr.cpp
Expand Up @@ -198,6 +198,11 @@ void DeclAttribute::print(ASTPrinter &Printer,
case DAK_RawDocComment:
// Not printed.
return;

case DAK_ObjCBridged:
// Not printed.
return;

case DAK_Count:
llvm_unreachable("exceed declaration attribute kinds");
}
Expand Down Expand Up @@ -277,6 +282,8 @@ StringRef DeclAttribute::getAttrName() const {
}
case DAK_RawDocComment:
return "<<raw doc comment>>";
case DAK_ObjCBridged:
return "<<ObjC bridged>>";
}
}

Expand Down
92 changes: 16 additions & 76 deletions lib/ClangImporter/ImportDecl.cpp
Expand Up @@ -781,66 +781,6 @@ static ConstructorDecl *makeOptionSetDefaultConstructor(StructDecl *optionSetDec
return ctorDecl;
}

/// Create a call to a builtin name.
static Expr *emitBuiltinCast(ASTContext &C, Expr *op, StringRef builtinName) {
SmallVector<ValueDecl*, 4> results;
C.TheBuiltinModule->lookupValue({}, C.getIdentifier(builtinName),
NLKind::QualifiedLookup, results);
assert(results.size() == 1 && "lookup for builtin didn't find anything?");
auto builtin = new (C) DeclRefExpr(results[0], SourceLoc(), /*implicit*/true);
return new (C) CallExpr(builtin, op, /*implicit*/ true);
}

// Build the implicit conversion from a CF type to an NS type or vice versa.
//
// extension FromType : ToType {
// var value: RawType
// func __conversion() -> Bool {
// return self.value != 0
// }
// }
static FuncDecl *makeClassToClassImplicitConversion(ClassDecl *fromDecl,
ClassDecl *toDecl) {
ASTContext &C = fromDecl->getASTContext();

auto resultType = toDecl->getDeclaredType();

VarDecl *selfDecl = createSelfDecl(fromDecl, /*isStaticMethod*/false);
Pattern *selfParam = createTypedNamedPattern(selfDecl);
Pattern *methodParam = TuplePattern::create(C, SourceLoc(),{},SourceLoc());
methodParam->setType(TupleType::getEmpty(C));
Pattern *params[] = {selfParam, methodParam};

DeclName name(C, C.Id_Conversion, { });
FuncDecl *conversionDecl = FuncDecl::create(
C, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
name, SourceLoc(), nullptr, Type(),
params, TypeLoc::withoutLoc(resultType), fromDecl);
conversionDecl->setImplicit();
conversionDecl->getAttrs().add(new (C) FinalAttr(/*implicit*/ true));
conversionDecl->setAccessibility(Accessibility::Public);

auto argType = TupleType::getEmpty(C);
Type fnType = FunctionType::get(argType, resultType);
fnType = FunctionType::get(fromDecl->getDeclaredTypeInContext(), fnType);
conversionDecl->setType(fnType);
conversionDecl->setBodyResultType(resultType);

Expr *op = new (C) DeclRefExpr(selfDecl, SourceLoc(), /*implicit*/ true);
op = emitBuiltinCast(C, op, "castToNativeObject");
op = emitBuiltinCast(C, op, "castFromNativeObject");
auto ret = new (C) ReturnStmt(SourceLoc(), op);

auto body = BraceStmt::create(C, SourceLoc(), ASTNode(ret),
SourceLoc(),
/*implicit*/ true);
conversionDecl->setBody(body);

// Add as an external definition.
C.addedExternalDecl(conversionDecl);

return conversionDecl;
}

namespace {
class CFPointeeInfo {
Expand Down Expand Up @@ -1083,20 +1023,6 @@ namespace {
return Type();
}

void addClassToClassConversions(ClassDecl *fromClass,
const clang::IdentifierInfo *toClassName) {
// Just bail out if we can't find a class by that name.
ClassDecl *toClass = dyn_cast_or_null<ClassDecl>(
Impl.importDeclByName(toClassName->getName()));
if (!toClass) return;

auto toConversion = makeClassToClassImplicitConversion(fromClass, toClass);
fromClass->addMember(toConversion);

auto fromConversion = makeClassToClassImplicitConversion(toClass, fromClass);
toClass->addMember(fromConversion);
}

Type importCFClassType(const clang::TypedefNameDecl *decl,
CFPointeeInfo info) {
// Name the class 'CFString', not 'CFStringRef'.
Expand Down Expand Up @@ -1135,11 +1061,25 @@ namespace {
auto record = info.getRecord()->getMostRecentDecl();
if (info.isConst()) {
if (auto attr = record->getAttr<clang::ObjCBridgeAttr>()) {
addClassToClassConversions(theClass, attr->getBridgedType());
// Record the Objective-C class to which this CF type is toll-free
// bridged.
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
Impl.importDeclByName(
attr->getBridgedType()->getName()))) {
theClass->getAttrs().add(
new (Impl.SwiftContext) ObjCBridgedAttr(objcClass));
}
}
} else {
if (auto attr = record->getAttr<clang::ObjCBridgeMutableAttr>()) {
addClassToClassConversions(theClass, attr->getBridgedType());
// Record the Objective-C class to which this CF type is toll-free
// bridged.
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
Impl.importDeclByName(
attr->getBridgedType()->getName()))) {
theClass->getAttrs().add(
new (Impl.SwiftContext) ObjCBridgedAttr(objcClass));
}
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Parse/ParseDecl.cpp
Expand Up @@ -333,6 +333,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
llvm_unreachable("DAK_Count should not appear in parsing switch");

case DAK_RawDocComment:
case DAK_ObjCBridged:
llvm_unreachable("virtual attributes should not be parsed "
"by attribute parsing code");
case DAK_SetterAccessibility:
Expand Down
14 changes: 14 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Expand Up @@ -238,6 +238,8 @@ namespace {
RValue visitArrayToPointerExpr(ArrayToPointerExpr *E, SGFContext C);
RValue visitStringToPointerExpr(StringToPointerExpr *E, SGFContext C);
RValue visitPointerToPointerExpr(PointerToPointerExpr *E, SGFContext C);
RValue visitForeignObjectConversionExpr(ForeignObjectConversionExpr *E,
SGFContext C);
};
}

Expand Down Expand Up @@ -5659,6 +5661,18 @@ RValue RValueEmitter::visitPointerToPointerExpr(PointerToPointerExpr *E,
return RValue(SGF, E, result);
}

RValue RValueEmitter::visitForeignObjectConversionExpr(
ForeignObjectConversionExpr *E,
SGFContext C) {
// Get the original value.
ManagedValue orig = SGF.emitRValueAsSingleValue(E->getSubExpr());
ManagedValue result(SGF.B.createUncheckedRefCast(
E, orig.getValue(),
SGF.getLoweredType(E->getType())),
orig.getCleanup());
return RValue(SGF, E, E->getType()->getCanonicalType(), result);
}

RValue SILGenFunction::emitRValue(Expr *E, SGFContext C) {
return RValueEmitter(*this).visit(E, C);
}
Expand Down
20 changes: 20 additions & 0 deletions lib/Sema/CSApply.cpp
Expand Up @@ -4038,6 +4038,26 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,

case ConversionRestrictionKind::BridgeFromObjC:
return forceBridgeFromObjectiveC(expr, toType);

case ConversionRestrictionKind::CFTollFreeBridgeToObjC: {
auto foreignClass = expr->getType()->getClassOrBoundGenericClass();
auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
->getObjCClass()->getDeclaredInterfaceType();
auto asObjCClass = new (tc.Context) ForeignObjectConversionExpr(expr,
objcType);
return coerceToType(asObjCClass, toType, locator);
}

case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: {
auto foreignClass = toType->getClassOrBoundGenericClass();
auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
->getObjCClass()->getDeclaredInterfaceType();
Expr *result = coerceToType(expr, objcType, locator);
if (!result)
return nullptr;

return new (tc.Context) ForeignObjectConversionExpr(result, toType);
}
}
}

Expand Down
52 changes: 52 additions & 0 deletions lib/Sema/CSSimplify.cpp
Expand Up @@ -1513,6 +1513,27 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
if (nominal1->getDecl() == nominal2->getDecl()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
}

// Check for CF <-> ObjectiveC bridging.
if (desugar1->getKind() == TypeKind::Class) {
auto class1 = cast<ClassDecl>(nominal1->getDecl());
auto class2 = cast<ClassDecl>(nominal2->getDecl());

// CF -> Objective-C via toll-free bridging.
if (class1->isForeign() && !class2->isForeign() &&
class1->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::CFTollFreeBridgeToObjC);
}

// Objective-C -> CF via toll-free bridging.
if (class2->isForeign() && !class1->isForeign() &&
class2->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ObjCTollFreeBridgeToCF);
}
}

break;
}

Expand Down Expand Up @@ -3803,6 +3824,37 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
locator);
}

// T' < U and T a toll-free-bridged to T' ===> T' <c U
case ConversionRestrictionKind::CFTollFreeBridgeToObjC: {
increaseScore(SK_UserConversion); // FIXME: Use separate score kind?
if (worseThanBestSolution()) {
return SolutionKind::Error;
}

auto nativeClass = type1->getClassOrBoundGenericClass();
auto bridgedObjCClass
= nativeClass->getAttrs().getAttribute<ObjCBridgedAttr>()->getObjCClass();

return matchTypes(bridgedObjCClass->getDeclaredInterfaceType(),
type2, TypeMatchKind::Subtype, subFlags, locator);
}

// T < U' and U a toll-free-bridged to U' ===> T <c U
case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: {
increaseScore(SK_UserConversion); // FIXME: Use separate score kind?
if (worseThanBestSolution()) {
return SolutionKind::Error;
}

auto nativeClass = type2->getClassOrBoundGenericClass();
auto bridgedObjCClass
= nativeClass->getAttrs().getAttribute<ObjCBridgedAttr>()->getObjCClass();

return matchTypes(type1,
bridgedObjCClass->getDeclaredInterfaceType(),
TypeMatchKind::Subtype, subFlags, locator);
}

// T' < U, hasMember(T, conversion, T -> T') ===> T <c U
case ConversionRestrictionKind::User:
addContextualScore();
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/Constraint.cpp
Expand Up @@ -335,6 +335,10 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
return "[bridge-to-objc]";
case ConversionRestrictionKind::BridgeFromObjC:
return "[bridge-from-objc]";
case ConversionRestrictionKind::CFTollFreeBridgeToObjC:
return "[cf-toll-free-bridge-to-objc]";
case ConversionRestrictionKind::ObjCTollFreeBridgeToCF:
return "[objc-toll-free-bridge-to-cf]";
case ConversionRestrictionKind::User:
return "[user]";
}
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/Constraint.h
Expand Up @@ -198,6 +198,12 @@ enum class ConversionRestrictionKind {
BridgeToObjC,
/// Implicit bridging from an Objective-C class to a value type.
BridgeFromObjC,
/// Implicit conversion from a CF type to its toll-free-bridged Objective-C
/// class type.
CFTollFreeBridgeToObjC,
/// Implicit conversion from an Objective-C class type to its
/// toll-free-bridged CF type.
ObjCTollFreeBridgeToCF,
/// User-defined conversions.
User
};
Expand Down

0 comments on commit 1771652

Please sign in to comment.