Begin adding plumbing for the type checker to accept "forward" bridged array conversions. (rdar://problem/16540403)

Swift SVN r17640
This commit is contained in:
Joe Pamer
2014-05-07 19:45:37 +00:00
parent 5ceecb4337
commit 2eedc06d66
17 changed files with 270 additions and 24 deletions

View File

@@ -372,6 +372,9 @@ public:
/// Retrieve the simple upcast conversion function for Array<T>. /// Retrieve the simple upcast conversion function for Array<T>.
FuncDecl *getArrayUpCast(LazyResolver *resolver) const; FuncDecl *getArrayUpCast(LazyResolver *resolver) const;
/// Retrieve the simple bridge conversion function for Array<T>.
FuncDecl *getArrayBridgeToObjectiveC(LazyResolver *resolver) const;
/// Retrieve the declaration of /// Retrieve the declaration of
/// Swift._does{,ImplicitlyUnwrapped}OptionalHaveValue. /// Swift._does{,ImplicitlyUnwrapped}OptionalHaveValue.
@@ -592,6 +595,10 @@ public:
/// Returns the protocol requirement decls for a conforming decl. /// Returns the protocol requirement decls for a conforming decl.
ArrayRef<ValueDecl *> getConformances(const ValueDecl *D); ArrayRef<ValueDecl *> getConformances(const ValueDecl *D);
/// \brief Retrieve the substitutions for a bound generic type, if known.
Optional<ArrayRef<Substitution>>
getSubstitutions(BoundGenericType *Bound) const;
private: private:
friend class Decl; friend class Decl;
@@ -603,10 +610,6 @@ private:
friend class BoundGenericType; friend class BoundGenericType;
/// \brief Retrieve the substitutions for a bound generic type, if known.
Optional<ArrayRef<Substitution>>
getSubstitutions(BoundGenericType *Bound) const;
/// \brief Set the substitutions for the given bound generic type. /// \brief Set the substitutions for the given bound generic type.
void setSubstitutions(BoundGenericType *Bound, void setSubstitutions(BoundGenericType *Bound,
ArrayRef<Substitution> Subs) const; ArrayRef<Substitution> Subs) const;

View File

@@ -2041,6 +2041,23 @@ public:
return E->getKind() == ExprKind::ArrayUpcastConversion; return E->getKind() == ExprKind::ArrayUpcastConversion;
} }
}; };
// ArrayBridgedConversionExpr - Convert an Array<U> to an Array<T>, where
// T is bridged to U.
class ArrayBridgedConversionExpr : public ImplicitConversionExpr {
public:
/// Keep track of whether or not the type being bridged to conforms to the
/// _ConditionallyBridgedToObjectiveC protocol.
bool isConditionallyBridged = false;
ArrayBridgedConversionExpr(Expr *subExpr, Type type)
: ImplicitConversionExpr(ExprKind::ArrayBridgedConversion, subExpr, type) {}
static bool classof(const Expr *E) {
return E->getKind() == ExprKind::ArrayBridgedConversion;
}
};
/// AnyErasureExpr - An abstract class for implicit conversions that /// AnyErasureExpr - An abstract class for implicit conversions that
/// erase a type into an existential in some way. /// erase a type into an existential in some way.

View File

@@ -120,6 +120,7 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
EXPR(CovariantReturnConversion, ImplicitConversionExpr) EXPR(CovariantReturnConversion, ImplicitConversionExpr)
EXPR(MetatypeConversion, ImplicitConversionExpr) EXPR(MetatypeConversion, ImplicitConversionExpr)
EXPR(ArrayUpcastConversion, ImplicitConversionExpr) EXPR(ArrayUpcastConversion, ImplicitConversionExpr)
EXPR(ArrayBridgedConversion, ImplicitConversionExpr)
ABSTRACT_EXPR(AnyErasure, ImplicitConversionExpr) ABSTRACT_EXPR(AnyErasure, ImplicitConversionExpr)
EXPR(Erasure, AnyErasureExpr) EXPR(Erasure, AnyErasureExpr)
EXPR(MetatypeErasure, AnyErasureExpr) EXPR(MetatypeErasure, AnyErasureExpr)

View File

@@ -75,6 +75,9 @@ struct ASTContext::Implementation {
/// Array<T> simple upcast. /// Array<T> simple upcast.
FuncDecl *GetArrayUpCast = nullptr; FuncDecl *GetArrayUpCast = nullptr;
/// Array<T> bridge conversion.
FuncDecl *GetArrayBridgeToObjectiveC = nullptr;
/// func _doesOptionalHaveValue<T>(v : [inout] Optional<T>) -> T /// func _doesOptionalHaveValue<T>(v : [inout] Optional<T>) -> T
FuncDecl *DoesOptionalHaveValueDecls[NumOptionalTypeKinds] = {}; FuncDecl *DoesOptionalHaveValueDecls[NumOptionalTypeKinds] = {};
@@ -629,6 +632,20 @@ FuncDecl *ASTContext::getArrayUpCast(LazyResolver *resolver) const {
return decl; return decl;
} }
FuncDecl *ASTContext::getArrayBridgeToObjectiveC(LazyResolver *resolver) const {
auto &cache = Impl.GetArrayBridgeToObjectiveC;
if (cache) return cache;
// Look for a generic function.
CanType input, output, param;
auto decl = findLibraryIntrinsic(*this, "_arrayBridgeToObjectiveC", resolver);
if (!decl)
return nullptr;
cache = decl;
return decl;
}
/// Check whether the given type is Optional applied to the given /// Check whether the given type is Optional applied to the given
/// type argument. /// type argument.
static bool isOptionalType(const ASTContext &ctx, static bool isOptionalType(const ASTContext &ctx,

View File

@@ -1361,6 +1361,11 @@ public:
printRec(E->getSubExpr()); printRec(E->getSubExpr());
OS << ')'; OS << ')';
} }
void visitArrayBridgedConversionExpr(ArrayBridgedConversionExpr *E) {
printCommon(E, "array_bridged_conversion_expr") << '\n';
printRec(E->getSubExpr());
OS << ')';
}
void visitDerivedToBaseExpr(DerivedToBaseExpr *E) { void visitDerivedToBaseExpr(DerivedToBaseExpr *E) {
printCommon(E, "derived_to_base_expr") << '\n'; printCommon(E, "derived_to_base_expr") << '\n';
printRec(E->getSubExpr()); printRec(E->getSubExpr());

View File

@@ -745,6 +745,11 @@ struct ASTNodeBase {};
verifyChecked(E->getSubExpr()); verifyChecked(E->getSubExpr());
verifyCheckedBase(E); verifyCheckedBase(E);
} }
void verifyChecked(ArrayBridgedConversionExpr *E) {
verifyChecked(E->getSubExpr());
verifyCheckedBase(E);
}
void verifyChecked(DerivedToBaseExpr *E) { void verifyChecked(DerivedToBaseExpr *E) {
PrettyStackTraceExpr debugStack(Ctx, "verifying DerivedToBaseExpr", E); PrettyStackTraceExpr debugStack(Ctx, "verifying DerivedToBaseExpr", E);

View File

@@ -177,6 +177,8 @@ namespace {
SGFContext C); SGFContext C);
RValue visitArrayUpcastConversionExpr(ArrayUpcastConversionExpr *E, RValue visitArrayUpcastConversionExpr(ArrayUpcastConversionExpr *E,
SGFContext C); SGFContext C);
RValue visitArrayBridgedConversionExpr(ArrayBridgedConversionExpr *E,
SGFContext C);
RValue visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C); RValue visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, SGFContext C);
RValue visitFunctionConversionExpr(FunctionConversionExpr *E, RValue visitFunctionConversionExpr(FunctionConversionExpr *E,
SGFContext C); SGFContext C);
@@ -947,6 +949,84 @@ visitArrayUpcastConversionExpr(ArrayUpcastConversionExpr *E,
return RValue(SGF, E, emitApply); return RValue(SGF, E, emitApply);
} }
RValue RValueEmitter::
visitArrayBridgedConversionExpr(ArrayBridgedConversionExpr *E,
SGFContext C) {
SILLocation loc = RegularLocation(E);
// Get the sub expression argument as a managed value
auto mv = SGF.emitRValueAsSingleValue(E->getSubExpr());
// Compute substitutions for the intrinsic call.
auto canTypeT = E->getSubExpr()->getType().getPointer()->getCanonicalType();
auto canTypeU = E->getType().getPointer()->getCanonicalType();
auto arrayT = cast<BoundGenericStructType>(canTypeT)->getGenericArgs()[0];
auto arrayU = cast<BoundGenericStructType>(canTypeU)->getGenericArgs()[0];
auto typeName = cast<BoundGenericStructType>(canTypeT)->getDecl()->getName();
// Get the intrinsic function.
FuncDecl *fn = nullptr;
if (typeName.str() == "Array") {
fn = SGF.getASTContext().getArrayBridgeToObjectiveC(nullptr);
} else {
llvm_unreachable("unsupported array bridge kind");
}
// Compute type parameter substitutions.
SmallVector<Substitution, 2> subs;
// Fish out the conformances to _BridgedToObjectiveC and ObjectiveCType from
// the the canonical types of T and U's type arguments.
auto polyFnType = cast<PolymorphicFunctionType>
(fn->getType()->getCanonicalType());
auto genericParams = polyFnType->getGenericParameters();
auto gp1 = genericParams[0].getAsTypeParam();
auto archetype = gp1->getArchetype();
auto protocolDecls = archetype->getConformsTo();
SmallVector<ProtocolConformance *, 1> conformances;
auto &ctx = SGF.getASTContext();
auto dc = protocolDecls[0]->getDeclContext()->getModuleScopeContext();
auto conformance = ctx.getConformsTo(arrayT->getCanonicalType(),
protocolDecls[0])->getPointer();
conformances.push_back(conformance);
subs.push_back(Substitution{archetype,
arrayT,
ctx.AllocateCopy(conformances)});
if (auto nestedAssocType =
archetype->getNestedTypeValue(ctx.getIdentifier("ObjectiveCType"))) {
if (auto nestedArchetype =
dyn_cast<ArchetypeType>(nestedAssocType.getPointer())) {
protocolDecls = nestedArchetype->getConformsTo();
SmallVector<ProtocolConformance *, 1> nestedConformances;
dc = protocolDecls[0]->getDeclContext()->getModuleScopeContext();
auto conformance = ctx.getConformsTo(arrayU->getCanonicalType(),
protocolDecls[0])->getPointer();
nestedConformances.push_back(conformance);
subs.push_back(Substitution{nestedArchetype,
arrayU,
ctx.AllocateCopy(nestedConformances)});
}
}
auto args = {mv};
auto emitApply = SGF.emitApplyOfLibraryIntrinsic(loc,
fn,
subs,
args,
C);
return RValue(SGF, E, emitApply);
}
RValue RValueEmitter::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E, RValue RValueEmitter::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E,
SGFContext C) { SGFContext C) {
ManagedValue archetype = SGF.emitRValueAsSingleValue(E->getSubExpr()); ManagedValue archetype = SGF.emitRValueAsSingleValue(E->getSubExpr());

View File

@@ -3384,7 +3384,16 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
if (!expr) return nullptr; if (!expr) return nullptr;
return coerceToType(expr, toType, locator); return coerceToType(expr, toType, locator);
} }
case ConversionRestrictionKind::ArrayBridged: {
auto bridgedArrayConversion = new (tc.Context)
ArrayBridgedConversionExpr(expr, toType);
bridgedArrayConversion->isConditionallyBridged =
tc.isConditionallyBridgedType(dc, toType);
bridgedArrayConversion->setType(toType);
return bridgedArrayConversion;
}
case ConversionRestrictionKind::ArrayUpcast: { case ConversionRestrictionKind::ArrayUpcast: {
auto arrayConversion = new (tc.Context) auto arrayConversion = new (tc.Context)
ArrayUpcastConversionExpr(expr, toType); ArrayUpcastConversionExpr(expr, toType);

View File

@@ -57,8 +57,11 @@ void ConstraintSystem::increaseScore(ScoreKind kind) {
log << "non-default literal"; log << "non-default literal";
break; break;
case SK_ArrayConversion: case SK_ArrayUpcastConversion:
log << "array conversion"; log << "array upcast conversion";
break;
case SK_ArrayBridgedConversion:
log << "array bridged conversion";
break; break;
} }
log << ")\n"; log << ")\n";

View File

@@ -1545,11 +1545,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass); conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
} }
// Array supertype conversions. // Implicit array conversions.
if (kind >= TypeMatchKind::Conversion) { if (kind >= TypeMatchKind::Conversion) {
if (isArrayType(desugar1) && isArrayType(desugar2)) { if (isArrayType(desugar1) && isArrayType(desugar2)) {
conversionsOrFixes.push_back(ConversionRestrictionKind::
ArrayUpcast); // Push both the upcast and bridged conversion kinds. We'll construct
// a disjunctive constraint out of them, favoring the upcast conversion
// should there be a tie.
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayBridged);
} }
} }
} }
@@ -2867,7 +2871,7 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
// T < U ===> Array<T> <c Array<U> // T < U ===> Array<T> <c Array<U>
case ConversionRestrictionKind::ArrayUpcast: { case ConversionRestrictionKind::ArrayUpcast: {
increaseScore(SK_ArrayConversion); increaseScore(SK_ArrayUpcastConversion);
addContextualScore(); addContextualScore();
assert(matchKind >= TypeMatchKind::Conversion); assert(matchKind >= TypeMatchKind::Conversion);
@@ -2892,22 +2896,70 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
baseType2 = arrayType2->getGenericArgs()[0]; baseType2 = arrayType2->getGenericArgs()[0];
} }
// Allow assignments from Array<T> to Array<AnyObject> if T is a bridged
// type.
if (auto protoTy = baseType2->getAs<ProtocolType>()) {
if(protoTy->getDecl()->isSpecificProtocol(KnownProtocolKind::AnyObject)) {
if (TC.isTriviallyRepresentableInObjC(DC, baseType1)) {
return SolutionKind::Solved;
}
}
}
return matchTypes(baseType1, return matchTypes(baseType1,
baseType2, baseType2,
TypeMatchKind::Subtype, TypeMatchKind::Subtype,
flags, flags,
locator); locator);
} }
// T < U ===> Array<T> is bridged to Array<U>
case ConversionRestrictionKind::ArrayBridged: {
addContextualScore();
assert(matchKind >= TypeMatchKind::Conversion);
Type baseType1;
Type baseType2;
auto t1 = type1->getDesugaredType();
auto t2 = type2->getDesugaredType();
if (auto sliceType1 = dyn_cast<ArraySliceType>(t1)) {
baseType1 = sliceType1->getBaseType();
} else {
auto arrayType1 = cast<BoundGenericStructType>(t1);
baseType1 = arrayType1->getGenericArgs()[0];
}
if (auto sliceType2 = dyn_cast<ArraySliceType>(t2)) {
baseType2 = sliceType2->getBaseType();
} else {
auto arrayType2 = cast<BoundGenericStructType>(t2);
baseType2 = arrayType2->getGenericArgs()[0];
}
if (auto nominalType1 = dyn_cast<NominalType>(baseType1.getPointer())) {
auto bridgedType1 = TC.getBridgedType(DC, nominalType1);
// Allow assignments from Array<T> to Array<AnyObject> if T is a bridged
// type.
if (!bridgedType1.isNull()) {
if (auto protoTy = baseType2->getAs<ProtocolType>()) {
if(protoTy->getDecl()->isSpecificProtocol(KnownProtocolKind::
AnyObject)) {
return SolutionKind::Solved;
}
}
// 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);
}
}
}
return ConstraintSystem::SolutionKind::Error;
}
// T' < U, hasMember(T, conversion, T -> T') ===> T <c U // T' < U, hasMember(T, conversion, T -> T') ===> T <c U
case ConversionRestrictionKind::User: case ConversionRestrictionKind::User:

View File

@@ -296,6 +296,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
return "[force-unchecked]"; return "[force-unchecked]";
case ConversionRestrictionKind::ArrayUpcast: case ConversionRestrictionKind::ArrayUpcast:
return "[array-upcast]"; return "[array-upcast]";
case ConversionRestrictionKind::ArrayBridged:
return "[array-bridged]";
case ConversionRestrictionKind::User: case ConversionRestrictionKind::User:
return "[user]"; return "[user]";
} }

View File

@@ -163,6 +163,8 @@ enum class ConversionRestrictionKind {
ForceUnchecked, ForceUnchecked,
/// Implicit upcast conversion of array types /// Implicit upcast conversion of array types
ArrayUpcast, ArrayUpcast,
/// Implicit bridged conversion between array types
ArrayBridged,
/// User-defined conversions. /// User-defined conversions.
User User
}; };

View File

@@ -678,12 +678,14 @@ enum ScoreKind {
SK_FunctionConversion, SK_FunctionConversion,
/// A literal expression bound to a non-default literal type. /// A literal expression bound to a non-default literal type.
SK_NonDefaultLiteral, SK_NonDefaultLiteral,
/// An implicit conversion between array types. /// An implicit upcast conversion between array types.
SK_ArrayConversion, SK_ArrayUpcastConversion,
/// An implicit bridged conversion between array types.
SK_ArrayBridgedConversion,
}; };
/// The number of score kinds. /// The number of score kinds.
const unsigned NumScoreKinds = 6; const unsigned NumScoreKinds = 7;
/// Describes the fixed score of a solution to the constraint system. /// Describes the fixed score of a solution to the constraint system.
struct Score { struct Score {

View File

@@ -216,6 +216,7 @@ static void diagModuleOrMetatypeValue(TypeChecker &TC, const Expr *E) {
case ExprKind::CovariantReturnConversion: case ExprKind::CovariantReturnConversion:
case ExprKind::MetatypeConversion: case ExprKind::MetatypeConversion:
case ExprKind::ArrayUpcastConversion: case ExprKind::ArrayUpcastConversion:
case ExprKind::ArrayBridgedConversion:
case ExprKind::Erasure: case ExprKind::Erasure:
case ExprKind::MetatypeErasure: case ExprKind::MetatypeErasure:
case ExprKind::DerivedToBase: case ExprKind::DerivedToBase:

View File

@@ -120,6 +120,15 @@ Type TypeChecker::getBridgedType(DeclContext *dc, Type type) {
return Type(); 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) { void TypeChecker::forceExternalDeclMembers(NominalTypeDecl *nominalDecl) {
// Force any delayed members added to the nominal type declaration. // Force any delayed members added to the nominal type declaration.
if (nominalDecl->hasDelayedProtocolDecls() || if (nominalDecl->hasDelayedProtocolDecls() ||

View File

@@ -688,6 +688,10 @@ public:
/// \brief Return the bridged Objective-C type if possbile, otherwise return /// \brief Return the bridged Objective-C type if possbile, otherwise return
/// a null Type. /// a null Type.
Type getBridgedType(DeclContext *dc, Type type); Type getBridgedType(DeclContext *dc, Type type);
/// \brief Return true if the type conforms to the
/// _ConditionallyBridgedToObjectiveC protocol
bool isConditionallyBridgedType(DeclContext *dc, Type type);
/// \brief Type check the given expression as an array bound, which converts /// \brief Type check the given expression as an array bound, which converts
/// it to a builtin integer value. /// it to a builtin integer value.

View File

@@ -0,0 +1,34 @@
// RUN: %swift -parse %s -verify
class A : ObjCClassType {
var x = 0
}
class B : _BridgedToObjectiveC {
func bridgeToObjectiveC() -> A {
return A()
}
}
var a: A[] = []
var b: B[] = []
a = b
b = a // expected-error {{cannot convert the expression's type '()' to type 'B[]'}}
// In this case, bridged conversion should win
class E : ObjCClassType {
var x = 0
}
class F : E, _BridgedToObjectiveC {
func bridgeToObjectiveC() -> E {
return self
}
}
var e: E[] = []
var f: F[] = []
e = f
f = e // expected-error {{cannot convert the expression's type '()' to type 'F[]'}}