mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Eliminate forced downcasting's dependency on conditional downcasting.
Previously, a forced downcast was implemented as a conditional downcast following by an implicit unwrap, and relied on peephole optimizations (in both constraint application and SILGen) to turn them into a forced downcast. However, these didn't kick in for AnyObject -> T[] downcasts, bypassing the more-efficient deferred checking our native collections can do. Finishes <rdar://problem/17319154>. Swift SVN r19064
This commit is contained in:
@@ -1114,6 +1114,38 @@ namespace {
|
|||||||
{ }, diag::broken_bridged_to_objc_protocol);
|
{ }, diag::broken_bridged_to_objc_protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bridge the given object from Objective-C to its value type.
|
||||||
|
///
|
||||||
|
/// This routine should only be used for bridging value types.
|
||||||
|
///
|
||||||
|
/// \param object The object, whose type should already be of the type
|
||||||
|
/// that the value type bridges through.
|
||||||
|
///
|
||||||
|
/// \param valueType The value type to which we are bridging.
|
||||||
|
///
|
||||||
|
/// \returns a value of type \c valueType that stores the bridged result.
|
||||||
|
Expr *bridgeFromObjectiveC(Expr *object, Type valueType) {
|
||||||
|
auto &tc = cs.getTypeChecker();
|
||||||
|
|
||||||
|
// Find the _BridgedToObjectiveC protocol.
|
||||||
|
auto bridgedProto
|
||||||
|
= tc.Context.getProtocol(KnownProtocolKind::_BridgedToObjectiveC);
|
||||||
|
|
||||||
|
// Find the conformance of the value type to _BridgedToObjectiveC.
|
||||||
|
ProtocolConformance *conformance = nullptr;
|
||||||
|
bool conforms = tc.conformsToProtocol(valueType, bridgedProto, cs.DC,
|
||||||
|
&conformance);
|
||||||
|
assert(conforms && "Should already have checked the conformance");
|
||||||
|
(void)conforms;
|
||||||
|
|
||||||
|
// Form the call.
|
||||||
|
return tc.callWitness(TypeExpr::createImplicit(valueType, tc.Context),
|
||||||
|
cs.DC, bridgedProto, conformance,
|
||||||
|
tc.Context.Id_bridgeFromObjectiveC,
|
||||||
|
{ object },
|
||||||
|
diag::broken_bridged_to_objc_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
/// Conditionally bridge the given object from Objective-C to its
|
/// Conditionally bridge the given object from Objective-C to its
|
||||||
/// value type.
|
/// value type.
|
||||||
///
|
///
|
||||||
@@ -1147,25 +1179,7 @@ namespace {
|
|||||||
args, diag::broken_bridged_to_objc_protocol);
|
args, diag::broken_bridged_to_objc_protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the _BridgedToObjectiveC protocol.
|
Expr *result = bridgeFromObjectiveC(object, valueType);
|
||||||
auto bridgedProto
|
|
||||||
= tc.Context.getProtocol(KnownProtocolKind::_BridgedToObjectiveC);
|
|
||||||
|
|
||||||
// Find the conformance of the value type to _BridgedToObjectiveC.
|
|
||||||
ProtocolConformance *conformance = nullptr;
|
|
||||||
bool conforms = tc.conformsToProtocol(valueType, bridgedProto, cs.DC,
|
|
||||||
&conformance);
|
|
||||||
assert(conforms && "Should already have checked the conformance");
|
|
||||||
(void)conforms;
|
|
||||||
|
|
||||||
// Form the call.
|
|
||||||
Expr *valueMetatype = TypeExpr::createImplicit(valueType, tc.Context);
|
|
||||||
Expr *args[1] = { object };
|
|
||||||
Expr *result = tc.callWitness(valueMetatype, cs.DC, bridgedProto,
|
|
||||||
conformance,
|
|
||||||
tc.Context.Id_bridgeFromObjectiveC,
|
|
||||||
args,
|
|
||||||
diag::broken_bridged_to_objc_protocol);
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@@ -2518,9 +2532,12 @@ namespace {
|
|||||||
|
|
||||||
/// Handle optional operands and results in an explicit cast.
|
/// Handle optional operands and results in an explicit cast.
|
||||||
Expr *handleOptionalBindings(ExplicitCastExpr *cast,
|
Expr *handleOptionalBindings(ExplicitCastExpr *cast,
|
||||||
Type finalResultType) {
|
Type finalResultType,
|
||||||
|
bool conditionalCast) {
|
||||||
auto &tc = cs.getTypeChecker();
|
auto &tc = cs.getTypeChecker();
|
||||||
|
|
||||||
|
unsigned destExtraOptionals = conditionalCast ? 1 : 0;
|
||||||
|
|
||||||
// FIXME: some of this work needs to be delayed until runtime to
|
// FIXME: some of this work needs to be delayed until runtime to
|
||||||
// properly account for archetypes dynamically being optional
|
// properly account for archetypes dynamically being optional
|
||||||
// types. For example, if we're casting T to NSView?, that
|
// types. For example, if we're casting T to NSView?, that
|
||||||
@@ -2539,41 +2556,52 @@ namespace {
|
|||||||
SmallVector<Type, 4> destOptionals;
|
SmallVector<Type, 4> destOptionals;
|
||||||
auto destValueType = plumbOptionals(finalResultType, destOptionals);
|
auto destValueType = plumbOptionals(finalResultType, destOptionals);
|
||||||
|
|
||||||
// This is a checked cast, so the result type will always have
|
// If this is a conditional cast, the result type will always
|
||||||
// at least one level of optional, which should become the type
|
// have at least one level of optional, which should become the
|
||||||
// of the checked-cast expression.
|
// type of the checked-cast expression.
|
||||||
assert(!destOptionals.empty() &&
|
if (conditionalCast) {
|
||||||
"result of checked cast is not an optional type");
|
assert(!destOptionals.empty() &&
|
||||||
cast->setType(destOptionals.back());
|
"result of checked cast is not an optional type");
|
||||||
|
cast->setType(destOptionals.back());
|
||||||
|
} else {
|
||||||
|
cast->setType(destValueType);
|
||||||
|
}
|
||||||
|
|
||||||
// The result type (without the final optional) is a subtype of
|
// The result type (without the final optional) is a subtype of
|
||||||
// the operand type, so it will never have a higher depth.
|
// the operand type, so it will never have a higher depth.
|
||||||
assert(destOptionals.size() - 1 <= srcOptionals.size());
|
assert(destOptionals.size() - destExtraOptionals <= srcOptionals.size());
|
||||||
|
|
||||||
// The outermost N levels of optionals on the operand must all
|
// The outermost N levels of optionals on the operand must all
|
||||||
// be present or the cast fails. The innermost M levels of
|
// be present or the cast fails. The innermost M levels of
|
||||||
// optionals on the operand are reflected in the requested
|
// optionals on the operand are reflected in the requested
|
||||||
// destination type, so we should map these nils into the result.
|
// destination type, so we should map these nils into the result.
|
||||||
unsigned numRequiredOptionals =
|
unsigned numRequiredOptionals =
|
||||||
srcOptionals.size() - (destOptionals.size() - 1);
|
srcOptionals.size() - (destOptionals.size() - destExtraOptionals);
|
||||||
|
|
||||||
// The number of OptionalEvaluationExprs between the point of the
|
// The number of OptionalEvaluationExprs between the point of the
|
||||||
// inner cast and the enclosing OptionalEvaluationExpr (exclusive)
|
// inner cast and the enclosing OptionalEvaluationExpr (exclusive)
|
||||||
// which represents failure for the entire operation.
|
// which represents failure for the entire operation.
|
||||||
unsigned failureDepth = destOptionals.size() - 1;
|
unsigned failureDepth = destOptionals.size() - destExtraOptionals;
|
||||||
|
|
||||||
// Drill down on the operand until it's non-optional.
|
// Drill down on the operand until it's non-optional.
|
||||||
SourceLoc fakeQuestionLoc = subExpr->getEndLoc();
|
SourceLoc fakeQuestionLoc = subExpr->getEndLoc();
|
||||||
for (unsigned i : indices(srcOptionals)) {
|
for (unsigned i : indices(srcOptionals)) {
|
||||||
|
Type valueType =
|
||||||
|
(i + 1 == srcOptionals.size() ? srcType : srcOptionals[i+1]);
|
||||||
|
|
||||||
// As we move into the range of mapped optionals, start
|
// As we move into the range of mapped optionals, start
|
||||||
// lowering the depth.
|
// lowering the depth.
|
||||||
unsigned depth = failureDepth;
|
unsigned depth = failureDepth;
|
||||||
if (i >= numRequiredOptionals) {
|
if (i >= numRequiredOptionals) {
|
||||||
depth -= (i - numRequiredOptionals) + 1;
|
depth -= (i - numRequiredOptionals) + 1;
|
||||||
|
} else if (!conditionalCast) {
|
||||||
|
// For a forced cast, force the required optionals.
|
||||||
|
subExpr = new (tc.Context) ForceValueExpr(subExpr, fakeQuestionLoc);
|
||||||
|
subExpr->setType(valueType);
|
||||||
|
subExpr->setImplicit(true);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type valueType =
|
|
||||||
(i + 1 == srcOptionals.size() ? srcType : srcOptionals[i+1]);
|
|
||||||
subExpr = new (tc.Context) BindOptionalExpr(subExpr, fakeQuestionLoc,
|
subExpr = new (tc.Context) BindOptionalExpr(subExpr, fakeQuestionLoc,
|
||||||
depth, valueType);
|
depth, valueType);
|
||||||
subExpr->setImplicit(true);
|
subExpr->setImplicit(true);
|
||||||
@@ -2583,7 +2611,7 @@ namespace {
|
|||||||
// If we're casting to an optional type, we need to capture the
|
// If we're casting to an optional type, we need to capture the
|
||||||
// final M bindings.
|
// final M bindings.
|
||||||
Expr *result = cast;
|
Expr *result = cast;
|
||||||
if (destOptionals.size() > 1) {
|
if (destOptionals.size() > destExtraOptionals) {
|
||||||
// If the innermost cast fails, the entire expression fails. To
|
// If the innermost cast fails, the entire expression fails. To
|
||||||
// get this behavior, we have to bind and then re-inject the result.
|
// get this behavior, we have to bind and then re-inject the result.
|
||||||
// (SILGen should know how to peephole this.)
|
// (SILGen should know how to peephole this.)
|
||||||
@@ -2598,7 +2626,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we just need to capture the failure-depth binding.
|
// Otherwise, we just need to capture the failure-depth binding.
|
||||||
} else {
|
} else if (conditionalCast) {
|
||||||
result = new (tc.Context) OptionalEvaluationExpr(result,
|
result = new (tc.Context) OptionalEvaluationExpr(result,
|
||||||
finalResultType);
|
finalResultType);
|
||||||
}
|
}
|
||||||
@@ -2617,11 +2645,12 @@ namespace {
|
|||||||
unsigned choice = solution.getDisjunctionChoice(locator);
|
unsigned choice = solution.getDisjunctionChoice(locator);
|
||||||
assert(choice <= 1 && "checked cast choices not synced with disjunction");
|
assert(choice <= 1 && "checked cast choices not synced with disjunction");
|
||||||
|
|
||||||
|
auto &tc = cs.getTypeChecker();
|
||||||
|
auto sub = tc.coerceToRValue(expr->getSubExpr());
|
||||||
|
|
||||||
// Choice 0 is coercion.
|
// Choice 0 is coercion.
|
||||||
if (choice == 0) {
|
if (choice == 0) {
|
||||||
// The subexpression is always an rvalue.
|
// The subexpression is always an rvalue.
|
||||||
auto &tc = cs.getTypeChecker();
|
|
||||||
auto sub = tc.coerceToRValue(expr->getSubExpr());
|
|
||||||
if (!sub)
|
if (!sub)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@@ -2641,46 +2670,98 @@ namespace {
|
|||||||
|
|
||||||
// Choice 1 is downcast.
|
// Choice 1 is downcast.
|
||||||
assert(choice == 1);
|
assert(choice == 1);
|
||||||
|
auto fromType = sub->getType();
|
||||||
|
auto castKind = tc.typeCheckCheckedCast(
|
||||||
|
fromType, toType, cs.DC,
|
||||||
|
expr->getLoc(),
|
||||||
|
sub->getSourceRange(),
|
||||||
|
expr->getCastTypeLoc().getSourceRange(),
|
||||||
|
[&](Type commonTy) -> bool {
|
||||||
|
return tc.convertToType(sub, commonTy,
|
||||||
|
cs.DC);
|
||||||
|
});
|
||||||
|
switch (castKind) {
|
||||||
|
/// Invalid cast.
|
||||||
|
case CheckedCastKind::Unresolved:
|
||||||
|
return nullptr;
|
||||||
|
case CheckedCastKind::Coercion:
|
||||||
|
llvm_unreachable("Coercions handled above");
|
||||||
|
|
||||||
// Form a conditional checked cast and type-check that.
|
// Valid casts.
|
||||||
auto &tc = cs.getTypeChecker();
|
case CheckedCastKind::ArrayDowncast:
|
||||||
Expr *casted;
|
case CheckedCastKind::ArrayDowncastBridged:
|
||||||
{
|
case CheckedCastKind::DictionaryDowncast:
|
||||||
auto *conditionalCast = new (tc.Context) ConditionalCheckedCastExpr(
|
case CheckedCastKind::DictionaryDowncastBridged:
|
||||||
expr->getSubExpr(),
|
case CheckedCastKind::Downcast:
|
||||||
expr->getLoc(),
|
case CheckedCastKind::SuperToArchetype:
|
||||||
SourceLoc(),
|
case CheckedCastKind::ArchetypeToArchetype:
|
||||||
expr->getCastTypeLoc());
|
case CheckedCastKind::ArchetypeToConcrete:
|
||||||
conditionalCast->setType(OptionalType::get(toType));
|
case CheckedCastKind::ExistentialToArchetype:
|
||||||
if (expr->isImplicit())
|
case CheckedCastKind::ExistentialToConcrete:
|
||||||
conditionalCast->setImplicit();
|
case CheckedCastKind::ConcreteToArchetype:
|
||||||
casted = visitConditionalCheckedCastExpr(conditionalCast);
|
case CheckedCastKind::ConcreteToUnrelatedExistential:
|
||||||
if (!casted)
|
break;
|
||||||
return nullptr;
|
}
|
||||||
|
|
||||||
|
auto *cast = new (tc.Context) ForcedCheckedCastExpr(
|
||||||
|
sub,
|
||||||
|
expr->getLoc(),
|
||||||
|
expr->getCastTypeLoc());
|
||||||
|
cast->setType(toType);
|
||||||
|
cast->setCastKind(castKind);
|
||||||
|
if (expr->isImplicit())
|
||||||
|
cast->setImplicit();
|
||||||
|
|
||||||
|
// Check whether we're casting to a bridged value type.
|
||||||
|
// Allow for casts from AnyObject to a bridged type.
|
||||||
|
auto fromValueType = fromType->lookThroughAllAnyOptionalTypes();
|
||||||
|
auto toValueType = toType->lookThroughAllAnyOptionalTypes();
|
||||||
|
Type finalResultType = simplifyType(expr->getType());
|
||||||
|
Type bridgedThroughClass;
|
||||||
|
if (castKind == CheckedCastKind::ArrayDowncast ||
|
||||||
|
castKind == CheckedCastKind::DictionaryDowncast ||
|
||||||
|
!(bridgedThroughClass = tc.getDynamicBridgedThroughObjCClass(
|
||||||
|
cs.DC, fromValueType, toValueType))) {
|
||||||
|
// We're not casting through a bridged type, so finish up the
|
||||||
|
// optional bindings and we're done.
|
||||||
|
return handleOptionalBindings(cast, finalResultType,
|
||||||
|
/*conditionalCast=*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we ended up with a "bare" conditional checked cast, replace it
|
// Rebuild the class type we're bridging through with the
|
||||||
// with a forced checked cast.
|
// optionals from the final result type.
|
||||||
//
|
Type intermediateResultType = bridgedThroughClass;
|
||||||
// This is the common case.
|
SmallVector<Type, 2> finalOptionals;
|
||||||
if (auto conditional = dyn_cast<ConditionalCheckedCastExpr>(casted)) {
|
plumbOptionals(finalResultType, finalOptionals);
|
||||||
auto result = new (tc.Context) ForcedCheckedCastExpr(
|
for (unsigned i = finalOptionals.size(); i > 0; --i) {
|
||||||
conditional->getSubExpr(),
|
// Figure out the kind of this optional.
|
||||||
conditional->getLoc(),
|
OptionalTypeKind kind;
|
||||||
conditional->getCastTypeLoc());
|
finalOptionals[i-1]->getAnyOptionalObjectType(kind);
|
||||||
result->setType(toType);
|
|
||||||
result->setCastKind(conditional->getCastKind());
|
intermediateResultType = OptionalType::get(kind,intermediateResultType);
|
||||||
if (conditional->isImplicit())
|
|
||||||
result->setImplicit();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, unwrap the result.
|
// Form a cast to the intermediate result type, which is the
|
||||||
//
|
// class through which we are bridging.
|
||||||
// This happens when the conditional cast propagates optionals.
|
cast->getCastTypeLoc().setType(intermediateResultType,
|
||||||
Expr *result = new (tc.Context) ForceValueExpr(casted, SourceLoc());
|
/*validated=*/true);
|
||||||
result->setType(toType);
|
toType = intermediateResultType;
|
||||||
result->setImplicit();
|
Expr *result = handleOptionalBindings(cast, toType,
|
||||||
|
/*conditionalCast=*/false);
|
||||||
|
if (!result)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If the cast's subexpression has the type same as we're trying
|
||||||
|
// to cast to, drop the cast itself and perform a bridge.
|
||||||
|
// FIXME: The AST no longer reflects the source.
|
||||||
|
if (cast->getSubExpr()->getType()->isEqual(intermediateResultType)) {
|
||||||
|
result = cast->getSubExpr();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form a call to the bridgeFromObjectiveC witness, then patch
|
||||||
|
// up the result type.
|
||||||
|
result = bridgeFromObjectiveC(result, toValueType);
|
||||||
|
result->setType(finalResultType);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2767,7 +2848,8 @@ namespace {
|
|||||||
cs.DC, fromValueType, toValueType))) {
|
cs.DC, fromValueType, toValueType))) {
|
||||||
// We're not casting through a bridged type, so finish up the
|
// We're not casting through a bridged type, so finish up the
|
||||||
// optional bindings and we're done.
|
// optional bindings and we're done.
|
||||||
return handleOptionalBindings(expr, finalResultType);
|
return handleOptionalBindings(expr, finalResultType,
|
||||||
|
/*conditionalCast=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild the class type we're bridging through with the
|
// Rebuild the class type we're bridging through with the
|
||||||
@@ -2787,7 +2869,8 @@ namespace {
|
|||||||
// class through which we are bridging.
|
// class through which we are bridging.
|
||||||
expr->getCastTypeLoc().setType(intermediateResultType,/*validated=*/true);
|
expr->getCastTypeLoc().setType(intermediateResultType,/*validated=*/true);
|
||||||
toType = OptionalType::get(intermediateResultType);
|
toType = OptionalType::get(intermediateResultType);
|
||||||
auto result = handleOptionalBindings(expr, toType);
|
auto result = handleOptionalBindings(expr, toType,
|
||||||
|
/*conditionalCast=*/true);
|
||||||
if (!result)
|
if (!result)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -1448,13 +1448,22 @@ namespace {
|
|||||||
memberLoc, expr->isImplicit());
|
memberLoc, expr->isImplicit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto forced = dyn_cast<ForcedCheckedCastExpr>(expr)) {
|
||||||
|
expr = new (TC.Context) UnresolvedCheckedCastExpr(
|
||||||
|
forced->getSubExpr(),
|
||||||
|
forced->getLoc(),
|
||||||
|
forced->getCastTypeLoc());
|
||||||
|
if (forced->isImplicit())
|
||||||
|
expr->setImplicit();
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Ignore declarations.
|
/// \brief Ignore declarations.
|
||||||
bool walkToDeclPre(Decl *decl) override { return false; }
|
bool walkToDeclPre(Decl *decl) override { return false; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConstraintWalker : public ASTWalker {
|
class ConstraintWalker : public ASTWalker {
|
||||||
|
|||||||
@@ -1988,8 +1988,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
|
|||||||
return CheckedCastKind::Unresolved;
|
return CheckedCastKind::Unresolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the expression is a an implicit call to bridgeFromObjectiveC,
|
/// If the expression is a an implicit call to bridgeFromObjectiveC or
|
||||||
/// returns the argument of that call.
|
/// bridgeFromObjectiveCConditional, returns the argument of that call.
|
||||||
static Expr *lookThroughBridgeFromObjCCall(ASTContext &ctx, Expr *expr) {
|
static Expr *lookThroughBridgeFromObjCCall(ASTContext &ctx, Expr *expr) {
|
||||||
auto call = dyn_cast<CallExpr>(expr);
|
auto call = dyn_cast<CallExpr>(expr);
|
||||||
if (!call || !call->isImplicit())
|
if (!call || !call->isImplicit())
|
||||||
@@ -2022,45 +2022,47 @@ ExplicitCastExpr *swift::findForcedDowncast(ASTContext &ctx, Expr *expr) {
|
|||||||
// If we have an implicit force, look through it.
|
// If we have an implicit force, look through it.
|
||||||
if (auto forced = dyn_cast<ForceValueExpr>(expr)) {
|
if (auto forced = dyn_cast<ForceValueExpr>(expr)) {
|
||||||
if (forced->isImplicit()) {
|
if (forced->isImplicit()) {
|
||||||
// Skip through optional evaluations and binds.
|
expr = forced->getSubExpr();
|
||||||
auto skipOptionalEvalAndBinds = [](Expr *expr) -> Expr* {
|
|
||||||
do {
|
|
||||||
if (!expr->isImplicit())
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (auto optionalEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
|
|
||||||
expr = optionalEval->getSubExpr();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto bindOptional = dyn_cast<BindOptionalExpr>(expr)) {
|
|
||||||
expr = bindOptional->getSubExpr();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
return expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto sub = skipOptionalEvalAndBinds(forced->getSubExpr());
|
|
||||||
|
|
||||||
// If we have an explicit cast, we're done.
|
|
||||||
if (isa<ForcedCheckedCastExpr>(sub) ||
|
|
||||||
isa<ConditionalCheckedCastExpr>(sub))
|
|
||||||
return cast<ExplicitCastExpr>(sub);
|
|
||||||
|
|
||||||
// Otherwise, try to look through an implicit
|
|
||||||
// bridgeFromObjectiveC() call.
|
|
||||||
if (auto arg = lookThroughBridgeFromObjCCall(ctx, sub)) {
|
|
||||||
sub = skipOptionalEvalAndBinds(arg);
|
|
||||||
if (isa<ForcedCheckedCastExpr>(sub) ||
|
|
||||||
isa<ConditionalCheckedCastExpr>(sub))
|
|
||||||
return cast<ExplicitCastExpr>(sub);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip through optional evaluations and binds.
|
||||||
|
auto skipOptionalEvalAndBinds = [](Expr *expr) -> Expr* {
|
||||||
|
do {
|
||||||
|
if (!expr->isImplicit())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (auto optionalEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
|
||||||
|
expr = optionalEval->getSubExpr();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto bindOptional = dyn_cast<BindOptionalExpr>(expr)) {
|
||||||
|
expr = bindOptional->getSubExpr();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto sub = skipOptionalEvalAndBinds(expr);
|
||||||
|
|
||||||
|
// If we have an explicit cast, we're done.
|
||||||
|
if (isa<ForcedCheckedCastExpr>(sub) ||
|
||||||
|
isa<ConditionalCheckedCastExpr>(sub))
|
||||||
|
return cast<ExplicitCastExpr>(sub);
|
||||||
|
|
||||||
|
// Otherwise, try to look through an implicit
|
||||||
|
// bridgeFromObjectiveC() call.
|
||||||
|
if (auto arg = lookThroughBridgeFromObjCCall(ctx, sub)) {
|
||||||
|
sub = skipOptionalEvalAndBinds(arg);
|
||||||
|
if (isa<ForcedCheckedCastExpr>(sub) ||
|
||||||
|
isa<ConditionalCheckedCastExpr>(sub))
|
||||||
|
return cast<ExplicitCastExpr>(sub);
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,42 @@ extension Int : _BridgedToObjectiveC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Array : _ConditionallyBridgedToObjectiveC {
|
||||||
|
static func getObjectiveCType() -> Any.Type {
|
||||||
|
return NSArray.self
|
||||||
|
}
|
||||||
|
func bridgeToObjectiveC() -> NSArray {
|
||||||
|
return NSArray()
|
||||||
|
}
|
||||||
|
static func bridgeFromObjectiveC(x: NSArray) -> Array {
|
||||||
|
fatal("implement")
|
||||||
|
}
|
||||||
|
static func bridgeFromObjectiveCConditional(x: NSArray) -> Array? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
static func isBridgedToObjectiveC() -> Bool {
|
||||||
|
return Swift.isBridgedToObjectiveC(T.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Dictionary : _ConditionallyBridgedToObjectiveC {
|
||||||
|
static func getObjectiveCType() -> Any.Type {
|
||||||
|
return NSDictionary.self
|
||||||
|
}
|
||||||
|
func bridgeToObjectiveC() -> NSDictionary {
|
||||||
|
return NSDictionary()
|
||||||
|
}
|
||||||
|
static func bridgeFromObjectiveC(x: NSDictionary) -> Dictionary {
|
||||||
|
fatal("implement")
|
||||||
|
}
|
||||||
|
static func bridgeFromObjectiveCConditional(x: NSDictionary) -> Dictionary? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
static func isBridgedToObjectiveC() -> Bool {
|
||||||
|
return Swift.isBridgedToObjectiveC(T.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension NSObject : Hashable {
|
extension NSObject : Hashable {
|
||||||
var hashValue: Int { return 0 }
|
var hashValue: Int { return 0 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,25 @@ func testArrayDowncast(array: AnyObject[]) -> BridgedObjC[] {
|
|||||||
return array as BridgedObjC[]
|
return array as BridgedObjC[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil @_TF19collection_downcast27testArrayDowncastFromObject
|
||||||
|
// CHECK: bb0([[OBJ:%[0-9]+]] : $AnyObject):
|
||||||
|
func testArrayDowncastFromObject(obj: AnyObject) -> BridgedObjC[] {
|
||||||
|
// CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSa20bridgeFromObjectiveCU__fMGSaQ__FCSo7NSArrayGSaQ__ : $@thin <τ_0_0> (@owned NSArray, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0>
|
||||||
|
// CHECK: [[ARRAY_META:%[0-9]+]] = metatype $@thin Array<BridgedObjC>.Type
|
||||||
|
// CHECK: [[NSARRAY_OBJ:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to $NSArray
|
||||||
|
// CHECK: apply [[BRIDGE_FN]]<BridgedObjC>([[NSARRAY_OBJ]], [[ARRAY_META]]) : $@thin <τ_0_0> (@owned NSArray, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0>
|
||||||
|
return obj as BridgedObjC[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil @_TF19collection_downcast28testArrayDowncastFromNSArray
|
||||||
|
// CHECK: bb0([[NSARRAY_OBJ:%[0-9]+]] : $NSArray):
|
||||||
|
func testArrayDowncastFromNSArray(obj: NSArray) -> BridgedObjC[] {
|
||||||
|
// CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSa20bridgeFromObjectiveCU__fMGSaQ__FCSo7NSArrayGSaQ__ : $@thin <τ_0_0> (@owned NSArray, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0>
|
||||||
|
// CHECK: [[ARRAY_META:%[0-9]+]] = metatype $@thin Array<BridgedObjC>.Type
|
||||||
|
// CHECK: apply [[BRIDGE_FN]]<BridgedObjC>([[NSARRAY_OBJ]], [[ARRAY_META]]) : $@thin <τ_0_0> (@owned NSArray, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0>
|
||||||
|
return obj as BridgedObjC[]
|
||||||
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: sil @_TF19collection_downcast28testArrayDowncastConditional
|
// CHECK-LABEL: sil @_TF19collection_downcast28testArrayDowncastConditional
|
||||||
// CHECK: bb0([[ARRAY:%[0-9]+]] : $Array<AnyObject>):
|
// CHECK: bb0([[ARRAY:%[0-9]+]] : $Array<AnyObject>):
|
||||||
func testArrayDowncastConditional(array: AnyObject[]) -> BridgedObjC[]? {
|
func testArrayDowncastConditional(array: AnyObject[]) -> BridgedObjC[]? {
|
||||||
@@ -73,6 +92,17 @@ func testArrayIsaBridged(array: AnyObject[]) -> Bool {
|
|||||||
return array is BridgedSwift[] ? true : false
|
return array is BridgedSwift[] ? true : false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil @_TF19collection_downcast32testDictionaryDowncastFromObject
|
||||||
|
// CHECK: bb0([[OBJ:%[0-9]+]] : $AnyObject):
|
||||||
|
func testDictionaryDowncastFromObject(obj: AnyObject)
|
||||||
|
-> Dictionary<BridgedObjC, BridgedObjC> {
|
||||||
|
// CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFVSs10Dictionary20bridgeFromObjectiveCUSs8Hashable___fMGS_Q_Q0__FCSo12NSDictionaryGS_Q_Q0__ : $@thin <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@owned NSDictionary, @thin Dictionary<τ_0_0, τ_0_1>.Type) -> @owned Dictionary<τ_0_0, τ_0_1>
|
||||||
|
// CHECK: [[DICT_META:%[0-9]+]] = metatype $@thin Dictionary<BridgedObjC, BridgedObjC>.Type
|
||||||
|
// CHECK: [[NSDICT_OBJ:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to $NSDictionary
|
||||||
|
// CHECK: apply [[BRIDGE_FN]]<BridgedObjC, BridgedObjC>([[NSDICT_OBJ]], [[DICT_META]]) : $@thin <τ_0_0, τ_0_1 where τ_0_0 : Hashable> (@owned NSDictionary, @thin Dictionary<τ_0_0, τ_0_1>.Type) -> @owned Dictionary<τ_0_0, τ_0_1>
|
||||||
|
return obj as Dictionary<BridgedObjC, BridgedObjC>
|
||||||
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: sil @_TF19collection_downcast22testDictionaryDowncast
|
// CHECK-LABEL: sil @_TF19collection_downcast22testDictionaryDowncast
|
||||||
// CHECK: bb0([[DICT:%[0-9]+]] : $Dictionary<NSObject, AnyObject>)
|
// CHECK: bb0([[DICT:%[0-9]+]] : $Dictionary<NSObject, AnyObject>)
|
||||||
func testDictionaryDowncast(dict: Dictionary<NSObject, AnyObject>)
|
func testDictionaryDowncast(dict: Dictionary<NSObject, AnyObject>)
|
||||||
|
|||||||
Reference in New Issue
Block a user