mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -48,6 +48,7 @@ PROTOCOL(Hashable)
|
||||
PROTOCOL(Comparable)
|
||||
|
||||
PROTOCOL(_BridgedToObjectiveC)
|
||||
PROTOCOL(_ConditionallyBridgedToObjectiveC)
|
||||
|
||||
LITERAL_CONVERTIBLE_PROTOCOL(ArrayLiteralConvertible)
|
||||
LITERAL_CONVERTIBLE_PROTOCOL(CharacterLiteralConvertible)
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user