Miscellaneous cleanups for array bridging in the type checker.

Use protocol conformance checks whenever we want to determine whether
a value type is bridged to an Objective-C class, which is simpler and
more robust. Clean up some of the type checker code around bridging,
using TypeBase::isEqual() to compare types and looking through type
sugar more regularly.

As part of this, move Array's conformance to
_ConditionallyBridgedToObjectiveC into the Foundation overlay. This
lets us use NSArray as the bridged type (which is clearer than using
CocoaArray), and follows what we're doing for dictionary bridging.


As part of this, move Array's bridged-to-


Swift SVN r17868
This commit is contained in:
Doug Gregor
2014-05-11 05:13:24 +00:00
parent 818595fa36
commit 9c03b4f924
6 changed files with 129 additions and 156 deletions

View File

@@ -48,6 +48,7 @@ PROTOCOL(Hashable)
PROTOCOL(Comparable)
PROTOCOL(_BridgedToObjectiveC)
PROTOCOL(_ConditionallyBridgedToObjectiveC)
LITERAL_CONVERTIBLE_PROTOCOL(ArrayLiteralConvertible)
LITERAL_CONVERTIBLE_PROTOCOL(CharacterLiteralConvertible)

View File

@@ -40,7 +40,7 @@ const uint16_t VERSION_MAJOR = 0;
/// Serialized module format minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
const uint16_t VERSION_MINOR = 92;
const uint16_t VERSION_MINOR = 93;
using DeclID = Fixnum<31>;
using DeclIDField = BCFixed<31>;
@@ -1125,6 +1125,7 @@ namespace index_block {
Comparable,
_BridgedToObjectiveC,
_ConditionallyBridgedToObjectiveC,
};
using KnownProtocolLayout = BCGenericRecordLayout<

View File

@@ -2352,36 +2352,27 @@ namespace {
}
// Allow for casts from AnyObject to a briged type.
if (tc.isBridgedDynamicConversion(cs.DC, fromType, toType)) {
auto bridgedType = tc.getBridgedType(cs.DC, toType);
if (auto bridgedType = tc.getDynamicBridgedThroughObjCClass(cs.DC,
fromType,
toType)) {
sub->setType(bridgedType);
if (!bridgedType.isNull()) {
sub->setType(bridgedType);
sub = tc.coerceToRValue(expr->getSubExpr());
if (!sub)
return nullptr;
expr->setSubExpr(sub);
expr->getCastTypeLoc().setType(bridgedType, true);
expr->setType(tc.getOptionalType(expr->getLoc(), bridgedType));
sub = tc.coerceToRValue(expr->getSubExpr());
if (!sub)
return nullptr;
expr->setSubExpr(sub);
auto optionalStringType = tc.getOptionalType(expr->getLoc(), toType);
auto OSTLoc = TypeLoc::withoutLoc(optionalStringType);
expr->getCastTypeLoc().setType(bridgedType,
true);
auto wrappedExpr
= new (tc.Context) ConditionalCheckedCastExpr(expr,
expr->getLoc(),
OSTLoc);
expr->setType(tc.getOptionalType(expr->getLoc(),
bridgedType));
auto optionalStringType = tc.getOptionalType(expr->getLoc(),
toType);
auto OSTLoc = TypeLoc::withoutLoc(optionalStringType);
auto wrappedExpr =
new (tc.Context)
ConditionalCheckedCastExpr(expr,
expr->getLoc(),
OSTLoc);
return visitConditionalCheckedCastExpr(wrappedExpr);
}
return visitConditionalCheckedCastExpr(wrappedExpr);
}
Type finalResultType = simplifyType(expr->getType());
@@ -3152,17 +3143,13 @@ Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType,
auto &tc = solution.getConstraintSystem().getTypeChecker();
Type fromType = expr->getType();
if (tc.isBridgedDynamicConversion(cs.DC, toType, fromType)) {
// Need to coerce to the briged type.
auto bridgedType = tc.getBridgedType(cs.DC, fromType);
if (!bridgedType.isNull() &&
// Protect against "no-op" conversions. If the bridged type points back
// to itself, the constraint solver won't have a conversion handy to
// coerce to a user conversion, so we'll should avoid creating a new
// expression node.
(bridgedType.getPointer() != fromType.getPointer()) &&
(bridgedType.getPointer() != toType.getPointer())) {
if (auto bridgedType = tc.getDynamicBridgedThroughObjCClass(cs.DC, toType,
fromType)) {
// Protect against "no-op" conversions. If the bridged type points back
// to itself, the constraint solver won't have a conversion handy to
// coerce to a user conversion, so we'll should avoid creating a new
// expression node.
if (!bridgedType->isEqual(fromType) && !bridgedType->isEqual(toType)) {
expr = coerceViaUserConversion(expr, bridgedType, locator);
fromType = bridgedType;
}
@@ -3705,10 +3692,9 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
auto desugaredArray = fromType.getPointer()->getDesugaredType();
auto baseType = cs.getBaseTypeForArrayType(desugaredArray);
if (tc.isConditionallyBridgedType(dc, baseType)) {
bridgedArrayConversion->isConditionallyBridged = true;
}
bool isConditionallyBridged = false;
tc.getBridgedToObjC(dc, baseType, &isConditionallyBridged);
bridgedArrayConversion->isConditionallyBridged = isConditionallyBridged;
return bridgedArrayConversion;
}

View File

@@ -1963,19 +1963,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
}
// If possible, redirect the coercion to a bridged type.
if (TC.isBridgedDynamicConversion(DC, protocol->getDeclaredType(), type)) {
auto bridgedType = TC.getBridgedType(DC, type);
if (auto bridgedType = TC.getDynamicBridgedThroughObjCClass(
DC, protocol->getDeclaredType(), type)) {
simplifyRestrictedConstraint(ConversionRestrictionKind::User,
type,
bridgedType,
TypeMatchKind::Conversion,
TMF_GenerateConstraints,
locator);
if (!bridgedType.isNull()) {
simplifyRestrictedConstraint(ConversionRestrictionKind::User,
type,
bridgedType,
TypeMatchKind::Conversion,
TMF_GenerateConstraints,
locator);
return SolutionKind::Solved;
}
return SolutionKind::Solved;
}
// There's nothing more we can do; fail.
@@ -2835,13 +2832,12 @@ static TypeMatchKind getTypeMatchKind(ConstraintKind kind) {
}
Type ConstraintSystem::getBaseTypeForArrayType(TypeBase *type) {
if (auto sliceType = dyn_cast<ArraySliceType>(type)) {
return sliceType->getBaseType();
if (auto bound = type->getAs<BoundGenericStructType>()) {
if (bound->getDecl() == getASTContext().getArrayDecl()) {
return bound->getGenericArgs()[0];
}
}
if (auto arrayType = dyn_cast<BoundGenericType>(type)) {
return arrayType->getGenericArgs()[0];
}
llvm_unreachable("attempted to extract a base type from a non-array type");
}
@@ -2971,8 +2967,7 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
Type baseType2 = this->getBaseTypeForArrayType(t2);
if (baseType2->isAnyObject()) {
if (dyn_cast<ClassType>(baseType1.getPointer()) ||
dyn_cast<BoundGenericClassType>(baseType1.getPointer())) {
if (baseType1->getClassOrBoundGenericClass()) {
return SolutionKind::Solved;
}
}
@@ -2996,28 +2991,22 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
Type baseType2 = this->getBaseTypeForArrayType(t2);
if (!baseType2->isAnyObject()) {
if (auto nominalType1 = dyn_cast<NominalType>(baseType1.getPointer())) {
auto bridgedType1 = TC.getBridgedType(DC, nominalType1);
// Allow assignments from Array<T> to Array<U> if T is bridged to type
// Allow assignments from Array<T> to Array<U> if T is bridged
// to type U.
if (auto bridgedType1 = TC.getBridgedToObjC(DC, baseType1).first) {
// If we're not converting to AnyObject, check if T.ObjectiveCType is
// U.
if (!bridgedType1.isNull()) {
// If we're not converting to AnyObject, check if T.ObjectiveCType is
// U.
// We'll save the further check for conformance with
// _ConditionallyBridgedToObjectiveC until runtime.
if (bridgedType1.getPointer() == baseType2.getPointer()) {
return SolutionKind::Solved;
}
else {
// Check if T's bridged type is a subtype of U.
return matchTypes(bridgedType1,
baseType2,
TypeMatchKind::Subtype,
flags,
locator);
}
// We'll save the further check for conformance with
// _ConditionallyBridgedToObjectiveC until runtime.
if (bridgedType1->isEqual(baseType2)) {
return SolutionKind::Solved;
} else {
// Check if T's bridged type is a subtype of U.
return matchTypes(bridgedType1,
baseType2,
TypeMatchKind::Subtype,
flags,
locator);
}
}
}

View File

@@ -80,52 +80,21 @@ Type TypeChecker::getNSStringType(DeclContext *dc) {
return NSStringType;
}
bool TypeChecker::isBridgedDynamicConversion(DeclContext *dc,
Type protocolType,
Type concreteType) {
if (protocolType->isAnyObject()) {
if (auto nominalType = concreteType->getAs<NominalType>()) {
if (getBridgedType(dc, nominalType)) {
return true;
}
}
}
return false;
}
Type TypeChecker::getBridgedType(DeclContext *dc, Type type) {
auto name = dc->getASTContext().getIdentifier("bridgeToObjectiveC");
auto result = lookupMember(type, name,
dc,
true);
if (!result.empty()) {
auto memberDecl = result.front();
auto memberType = memberDecl->getType();
// Unwrap the inner function from self -> () -> bridgedType.
if (auto functionType =
dyn_cast<FunctionType>(memberType.getPointer())) {
if (auto innerFunctionType =
dyn_cast<FunctionType>(functionType->getResult().getPointer())) {
return innerFunctionType->getResult();
}
}
Type
TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc,
Type dynamicType,
Type valueType,
bool *isConditionallyBridged) {
if (dynamicType->isAnyObject()) {
// We only interested in types that bridge non-verbatim.
auto bridged = getBridgedToObjC(dc, valueType, isConditionallyBridged);
if (bridged.first && !bridged.second)
return bridged.first;
}
return Type();
}
bool TypeChecker::isConditionallyBridgedType(DeclContext *dc, Type type) {
auto name = dc->getASTContext().getIdentifier("isBridgedToObjectiveC");
auto result = lookupMember(type, name,
dc,
true);
return !result.empty();
}
void TypeChecker::forceExternalDeclMembers(NominalTypeDecl *nominalDecl) {
// Force any delayed members added to the nominal type declaration.
if (nominalDecl->hasDelayedProtocolDecls() ||
@@ -2088,35 +2057,42 @@ bool TypeChecker::isTriviallyRepresentableInObjC(const DeclContext *DC,
return false;
}
/// Retrieves the Objective-C type to which the given type is bridged along
/// with a bit indicating whether it was bridged verbatim.
///
/// \returns a pair containing the bridged type (or a null type if the type
/// cannot be bridged) and a bit indicating whether the type was bridged
/// verbatim.
static std::pair<Type, bool> getBridgedToObjC(TypeChecker &tc,
const DeclContext *dc,
Type type) {
std::pair<Type, bool>
TypeChecker::getBridgedToObjC(const DeclContext *dc,
Type type,
bool *isConditionallyBridged) {
if (isConditionallyBridged)
*isConditionallyBridged = false;
// Class types and ObjC existential types are always bridged verbatim.
if (type->getClassOrBoundGenericClass() || type->isObjCExistentialType())
return { type, true };
// Retrieve the _BridgedToObjectiveC protocol.
auto bridgedProto
= tc.Context.getProtocol(KnownProtocolKind::_BridgedToObjectiveC);
= Context.getProtocol(KnownProtocolKind::_BridgedToObjectiveC);
if (!bridgedProto)
return { nullptr, false };
// Check whether the type conforms to _BridgedToObjectiveC.
ProtocolConformance *conformance = nullptr;
if (!tc.conformsToProtocol(type, bridgedProto, const_cast<DeclContext *>(dc),
&conformance))
if (!conformsToProtocol(type, bridgedProto, const_cast<DeclContext *>(dc),
&conformance))
return { nullptr, false };
// FIXME: Check conditional bridging if requested?
// Check conditional bridging if requested.
if (isConditionallyBridged) {
if (auto condBridgedProto
= Context.getProtocol(
KnownProtocolKind::_ConditionallyBridgedToObjectiveC)) {
*isConditionallyBridged
= conformsToProtocol(type, condBridgedProto,
const_cast<DeclContext *>(dc));
}
}
return { tc.getWitnessType(type, bridgedProto, conformance,
tc.Context.getIdentifier("ObjectiveCType"),
return { getWitnessType(type, bridgedProto, conformance,
Context.getIdentifier("ObjectiveCType"),
diag::broken_bridged_to_objc_protocol),
false };
}
@@ -2160,19 +2136,18 @@ bool TypeChecker::isRepresentableInObjC(const DeclContext *DC, Type T) {
return true;
}
// Dictionary<K, V> is representable when K is a class inheriting from
// NSObject and V is something compatible with AnyObject.
// Dictionary<K, V> is representable when K and V are bridged to Objective-C.
if (auto dictDecl = Context.getDictionaryDecl()) {
if (auto boundGeneric = T->getAs<BoundGenericType>()) {
if (boundGeneric->getDecl() == dictDecl) {
// The key type must be bridged to Objective-C.
auto keyType = boundGeneric->getGenericArgs()[0];
if (getBridgedToObjC(*this, DC, keyType).first.isNull())
if (getBridgedToObjC(DC, keyType).first.isNull())
return false;
// The value type must be bridged to Objective-C.
auto valueType = boundGeneric->getGenericArgs()[1];
if (getBridgedToObjC(*this, DC, valueType).first.isNull())
if (getBridgedToObjC(DC, valueType).first.isNull())
return false;
return true;

View File

@@ -679,19 +679,40 @@ public:
SourceRange diagToRange,
std::function<bool(Type)> convertToType);
/// \brief Determine if a given concrete type can be coerced to an existential
/// protocol type.
bool isBridgedDynamicConversion(DeclContext *dc,
Type protocolType,
Type concreteType);
/// \brief Return the bridged Objective-C type if possbile, otherwise return
/// a null Type.
Type getBridgedType(DeclContext *dc, Type type);
/// \brief Return true if the type conforms to the
/// _ConditionallyBridgedToObjectiveC protocol
bool isConditionallyBridgedType(DeclContext *dc, Type type);
/// Retrieves the Objective-C type to which the given value type is
/// bridged along with a bit indicating whether it was bridged
/// verbatim.
///
/// \param dc The declaration context from which we will look for
/// bridging.
///
/// \param type The value type being queried, e.g., String.
///
/// \param isConditionallyBridged If non-null, will be set to
/// indicate whether the bridging is conditional.
///
/// \returns a pair containing the bridged type (or a null type if the type
/// cannot be bridged) and a bit indicating whether the type was bridged
/// verbatim.
std::pair<Type, bool> getBridgedToObjC(const DeclContext *dc,
Type type,
bool *isConditionallyBridged=nullptr);
/// Find the Objective-C class that bridges between a value of the given
/// dynamic type and the given value type.
///
/// \param dynamicType A dynamic type from which we are bridging. At
/// present, only \c AnyObject can be used here for a successful
/// bridge.
///
/// \returns the Objective-C class type that represents the value
/// type as an Objective-C class, e.g., \c NSString represents \c
/// String, or a null type if there is no such type or if the
/// dynamic type isn't something we can start from.
Type getDynamicBridgedThroughObjCClass(DeclContext *dc,
Type dynamicType,
Type valueType,
bool *isConditionallyBridged=nullptr);
/// \brief Type check the given expression as an array bound, which converts
/// it to a builtin integer value.