Skip to content

Commit b6eb0a0

Browse files
rjmccalljckarter
authored andcommitted
Add implicit conversions and casts from T:Hashable <-> AnyHashable.
rdar://27615802
1 parent 569a2d2 commit b6eb0a0

23 files changed

+678
-64
lines changed

include/swift/AST/Expr.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3010,12 +3010,36 @@ class ErasureExpr : public ImplicitConversionExpr {
30103010
ArrayRef<ProtocolConformanceRef> getConformances() const {
30113011
return Conformances;
30123012
}
3013-
3013+
30143014
static bool classof(const Expr *E) {
30153015
return E->getKind() == ExprKind::Erasure;
30163016
}
30173017
};
30183018

3019+
/// AnyHashableErasureExpr - Perform type erasure by converting a value
3020+
/// to AnyHashable type.
3021+
///
3022+
/// The type of the sub-expression should always be a type that implements
3023+
/// the Hashable protocol.
3024+
class AnyHashableErasureExpr : public ImplicitConversionExpr {
3025+
ProtocolConformanceRef Conformance;
3026+
3027+
public:
3028+
AnyHashableErasureExpr(Expr *subExpr, Type type,
3029+
ProtocolConformanceRef conformance)
3030+
: ImplicitConversionExpr(ExprKind::AnyHashableErasure, subExpr, type),
3031+
Conformance(conformance) {}
3032+
3033+
/// \brief Retrieve the mapping specifying how the type of the
3034+
/// subexpression conforms to the Hashable protocol.
3035+
ProtocolConformanceRef getConformance() const {
3036+
return Conformance;
3037+
}
3038+
3039+
static bool classof(const Expr *E) {
3040+
return E->getKind() == ExprKind::AnyHashableErasure;
3041+
}
3042+
};
30193043
/// UnresolvedSpecializeExpr - Represents an explicit specialization using
30203044
/// a type parameter list (e.g. "Vector<Int>") that has not been resolved.
30213045
class UnresolvedSpecializeExpr : public Expr {

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
127127
EXPR(MetatypeConversion, ImplicitConversionExpr)
128128
EXPR(CollectionUpcastConversion, ImplicitConversionExpr)
129129
EXPR(Erasure, ImplicitConversionExpr)
130+
EXPR(AnyHashableErasure, ImplicitConversionExpr)
130131
EXPR(DerivedToBase, ImplicitConversionExpr)
131132
EXPR(ArchetypeToSuper, ImplicitConversionExpr)
132133
EXPR(InjectIntoOptional, ImplicitConversionExpr)

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ FUNC_DECL(ConditionallyBridgeFromObjectiveCBridgeable,
6868
FUNC_DECL(BridgeAnythingToObjectiveC,
6969
"_bridgeAnythingToObjectiveC")
7070

71+
FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable")
72+
7173
FUNC_DECL(DidEnterMain, "_stdlib_didEnterMain")
7274
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
7375

include/swift/Runtime/Metadata.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,6 +1980,10 @@ struct TargetValueMetadata : public TargetMetadata<Runtime> {
19801980
return (asWords + Description->GenericParams.Offset);
19811981
}
19821982

1983+
const TargetNominalTypeDescriptor<Runtime> *getDescription() const {
1984+
return Description.get();
1985+
}
1986+
19831987
StoredPointer offsetToDescriptorOffset() const {
19841988
return offsetof(TargetValueMetadata<Runtime>, Description);
19851989
}

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,13 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
19161916
printRec(E->getSubExpr());
19171917
OS << ')';
19181918
}
1919+
void visitAnyHashableErasureExpr(AnyHashableErasureExpr *E) {
1920+
printCommon(E, "any_hashable_erasure_expr") << '\n';
1921+
printRec(E->getConformance());
1922+
OS << '\n';
1923+
printRec(E->getSubExpr());
1924+
OS << ')';
1925+
}
19191926
void visitLoadExpr(LoadExpr *E) {
19201927
printCommon(E, "load_expr") << '\n';
19211928
printRec(E->getSubExpr());

lib/AST/ASTVerifier.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,33 @@ struct ASTNodeBase {};
12151215
verifyCheckedBase(E);
12161216
}
12171217

1218+
void verifyChecked(AnyHashableErasureExpr *E) {
1219+
auto anyHashableDecl = Ctx.getAnyHashableDecl();
1220+
if (!anyHashableDecl) {
1221+
Out << "AnyHashable declaration could not be found\n";
1222+
abort();
1223+
}
1224+
1225+
auto hashableDecl = Ctx.getProtocol(KnownProtocolKind::Hashable);
1226+
if (!hashableDecl) {
1227+
Out << "Hashable declaration could not be found\n";
1228+
abort();
1229+
}
1230+
1231+
checkSameType(E->getType(), anyHashableDecl->getDeclaredType(),
1232+
"AnyHashableErasureExpr and the standard AnyHashable type");
1233+
1234+
if (E->getConformance().getRequirement() != hashableDecl) {
1235+
Out << "conformance on AnyHashableErasureExpr was not for Hashable\n";
1236+
E->getConformance().dump();
1237+
abort();
1238+
}
1239+
1240+
verifyConformance(E->getSubExpr()->getType(), E->getConformance());
1241+
1242+
verifyCheckedBase(E);
1243+
}
1244+
12181245
void verifyChecked(TupleElementExpr *E) {
12191246
PrettyStackTraceExpr debugStack(Ctx, "verifying TupleElementExpr", E);
12201247

@@ -1795,6 +1822,32 @@ struct ASTNodeBase {};
17951822
}
17961823
}
17971824

1825+
/// Verify that the given conformance makes sense for the given
1826+
/// type.
1827+
void verifyConformance(Type type, ProtocolConformanceRef conformance) {
1828+
if (conformance.isAbstract()) {
1829+
if (!type->is<ArchetypeType>() && !type->isAnyExistentialType()) {
1830+
Out << "type " << type
1831+
<< " should not have an abstract conformance to "
1832+
<< conformance.getRequirement()->getName();
1833+
abort();
1834+
}
1835+
1836+
return;
1837+
}
1838+
1839+
if (type->getCanonicalType() !=
1840+
conformance.getConcrete()->getType()->getCanonicalType()) {
1841+
Out << "conforming type does not match conformance\n";
1842+
Out << "conforming type:\n";
1843+
type.dump(Out, 2);
1844+
Out << "\nconformance:\n";
1845+
conformance.getConcrete()->dump(Out, 2);
1846+
Out << "\n";
1847+
abort();
1848+
}
1849+
}
1850+
17981851
/// Check the given explicit protocol conformance.
17991852
void verifyConformance(Decl *decl, ProtocolConformance *conformance) {
18001853
PrettyStackTraceDecl debugStack("verifying protocol conformance", decl);

lib/AST/Expr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const {
462462
PASS_THROUGH_REFERENCE(MetatypeConversion, getSubExpr);
463463
PASS_THROUGH_REFERENCE(CollectionUpcastConversion, getSubExpr);
464464
PASS_THROUGH_REFERENCE(Erasure, getSubExpr);
465+
PASS_THROUGH_REFERENCE(AnyHashableErasure, getSubExpr);
465466
PASS_THROUGH_REFERENCE(DerivedToBase, getSubExpr);
466467
PASS_THROUGH_REFERENCE(ArchetypeToSuper, getSubExpr);
467468
PASS_THROUGH_REFERENCE(InjectIntoOptional, getSubExpr);
@@ -754,6 +755,7 @@ bool Expr::canAppendCallParentheses() const {
754755
case ExprKind::MetatypeConversion:
755756
case ExprKind::CollectionUpcastConversion:
756757
case ExprKind::Erasure:
758+
case ExprKind::AnyHashableErasure:
757759
case ExprKind::DerivedToBase:
758760
case ExprKind::ArchetypeToSuper:
759761
case ExprKind::InjectIntoOptional:

lib/SIL/DynamicCasts.cpp

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,45 @@ classifyDynamicCastToProtocol(CanType source,
173173
return DynamicCastFeasibility::MaySucceed;
174174
}
175175

176+
static DynamicCastFeasibility
177+
classifyDynamicCastFromProtocol(ModuleDecl *M, CanType source, CanType target,
178+
bool isWholeModuleOpts) {
179+
assert(source.isExistentialType() &&
180+
"source should be an existential type");
181+
182+
if (source == target)
183+
return DynamicCastFeasibility::WillSucceed;
184+
185+
// Casts from class existential into a non-class can never succeed.
186+
if (source->isClassExistentialType() &&
187+
!target.isAnyExistentialType() &&
188+
!target.getClassOrBoundGenericClass() &&
189+
!isa<ArchetypeType>(target) &&
190+
!mayBridgeToObjectiveC(M, target)) {
191+
assert((target.getEnumOrBoundGenericEnum() ||
192+
target.getStructOrBoundGenericStruct() ||
193+
isa<TupleType>(target) ||
194+
isa<SILFunctionType>(target) ||
195+
isa<FunctionType>(target) ||
196+
isa<MetatypeType>(target)) &&
197+
"Target should be an enum, struct, tuple, metatype or function type");
198+
return DynamicCastFeasibility::WillFail;
199+
}
200+
201+
// TODO: maybe prove that certain conformances are impossible?
202+
203+
return DynamicCastFeasibility::MaySucceed;
204+
}
205+
206+
/// Returns the existential type associated with the Hashable
207+
/// protocol, if it can be found.
208+
static CanType getHashableExistentialType(Module *M) {
209+
auto hashable =
210+
M->getASTContext().getProtocol(KnownProtocolKind::Hashable);
211+
if (!hashable) return CanType();
212+
return hashable->getDeclaredType()->getCanonicalType();
213+
}
214+
176215
/// Check if a given type conforms to _BridgedToObjectiveC protocol.
177216
bool swift::isObjectiveCBridgeable(Module *M, CanType Ty) {
178217
// Retrieve the _BridgedToObjectiveC protocol.
@@ -263,21 +302,11 @@ swift::classifyDynamicCast(Module *M,
263302
target.isExistentialType())
264303
return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);
265304

266-
// Casts from class existential into a non-class can never succeed.
267-
if (source->isClassExistentialType() &&
268-
!target.isAnyExistentialType() &&
269-
!target.getClassOrBoundGenericClass() &&
270-
!isa<ArchetypeType>(target) &&
271-
!mayBridgeToObjectiveC(M, target)) {
272-
assert((target.getEnumOrBoundGenericEnum() ||
273-
target.getStructOrBoundGenericStruct() ||
274-
isa<TupleType>(target) ||
275-
isa<SILFunctionType>(target) ||
276-
isa<FunctionType>(target) ||
277-
isa<MetatypeType>(target)) &&
278-
"Target should be an enum, struct, tuple, metatype or function type");
279-
return DynamicCastFeasibility::WillFail;
280-
}
305+
// Check conversions from protocol types to non-protocol types.
306+
if (source.isExistentialType() &&
307+
!target.isExistentialType())
308+
return classifyDynamicCastFromProtocol(M, source, target,
309+
isWholeModuleOpts);
281310

282311
return DynamicCastFeasibility::MaySucceed;
283312
}
@@ -540,7 +569,6 @@ swift::classifyDynamicCast(Module *M,
540569
// Check for a viable collection cast.
541570
if (auto sourceStruct = dyn_cast<BoundGenericStructType>(source)) {
542571
if (auto targetStruct = dyn_cast<BoundGenericStructType>(target)) {
543-
544572
// Both types have to be the same kind of collection.
545573
auto typeDecl = sourceStruct->getDecl();
546574
if (typeDecl == targetStruct->getDecl()) {
@@ -571,6 +599,30 @@ swift::classifyDynamicCast(Module *M,
571599
}
572600
}
573601

602+
// Casts from AnyHashable.
603+
if (auto sourceStruct = dyn_cast<StructType>(source)) {
604+
if (sourceStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
605+
if (auto hashable = getHashableExistentialType(M)) {
606+
// Succeeds if Hashable can be cast to the target type.
607+
return classifyDynamicCastFromProtocol(M, hashable, target,
608+
isWholeModuleOpts);
609+
}
610+
}
611+
}
612+
613+
// Casts to AnyHashable.
614+
if (auto targetStruct = dyn_cast<StructType>(target)) {
615+
if (targetStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
616+
// Succeeds if the source type can be dynamically cast to Hashable.
617+
// Hashable is not actually a legal existential type right now, but
618+
// the check doesn't care about that.
619+
if (auto hashable = getHashableExistentialType(M)) {
620+
return classifyDynamicCastToProtocol(source, hashable,
621+
isWholeModuleOpts);
622+
}
623+
}
624+
}
625+
574626
return DynamicCastFeasibility::WillFail;
575627
}
576628

lib/SILGen/SILGenExpr.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ namespace {
173173
CovariantReturnConversionExpr *E,
174174
SGFContext C);
175175
RValue visitErasureExpr(ErasureExpr *E, SGFContext C);
176+
RValue visitAnyHashableErasureExpr(AnyHashableErasureExpr *E, SGFContext C);
176177
RValue visitForcedCheckedCastExpr(ForcedCheckedCastExpr *E,
177178
SGFContext C);
178179
RValue visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *E,
@@ -1326,6 +1327,25 @@ RValue RValueEmitter::visitErasureExpr(ErasureExpr *E, SGFContext C) {
13261327
return RValue(SGF, E, mv);
13271328
}
13281329

1330+
RValue RValueEmitter::visitAnyHashableErasureExpr(AnyHashableErasureExpr *E,
1331+
SGFContext C) {
1332+
// Ensure that the intrinsic function exists.
1333+
auto convertFn = SGF.SGM.getConvertToAnyHashable(E);
1334+
if (!convertFn) return SGF.emitUndefRValue(E, E->getType());
1335+
1336+
// Construct the substitution for T: Hashable.
1337+
ProtocolConformanceRef conformances[] = { E->getConformance() };
1338+
Substitution sub(E->getSubExpr()->getType(),
1339+
SGF.getASTContext().AllocateCopy(conformances));
1340+
1341+
// Emit the source value into a temporary.
1342+
auto sourceOrigType = AbstractionPattern::getOpaque();
1343+
auto source =
1344+
SGF.emitMaterializedRValueAsOrig(E->getSubExpr(), sourceOrigType);
1345+
1346+
return SGF.emitApplyOfLibraryIntrinsic(E, convertFn, sub, source, C);
1347+
}
1348+
13291349
/// Treating this as a successful operation, turn a CMV into a +1 MV.
13301350
ManagedValue SILGenFunction::getManagedValue(SILLocation loc,
13311351
ConsumableManagedValue value) {

lib/SILGen/SILGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
10061006
ManagedValue emitRValueAsOrig(Expr *E, AbstractionPattern origPattern,
10071007
const TypeLowering &origTL,
10081008
SGFContext C = SGFContext());
1009+
1010+
/// Emit an r-value into temporary memory and return the managed address.
1011+
ManagedValue
1012+
emitMaterializedRValueAsOrig(Expr *E, AbstractionPattern origPattern);
10091013

10101014
/// Emit the given expression, ignoring its result.
10111015
void emitIgnoredExpr(Expr *E);

lib/SILGen/SILGenPoly.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,6 +2587,26 @@ RValue SILGenFunction::emitSubstToOrigValue(SILLocation loc, RValue &&v,
25872587
ctxt);
25882588
}
25892589

2590+
ManagedValue
2591+
SILGenFunction::emitMaterializedRValueAsOrig(Expr *expr,
2592+
AbstractionPattern origType) {
2593+
// Create a temporary.
2594+
auto &origTL = getTypeLowering(origType, expr->getType());
2595+
auto temporary = emitTemporary(expr, origTL);
2596+
2597+
// Emit the reabstracted r-value.
2598+
auto result =
2599+
emitRValueAsOrig(expr, origType, origTL, SGFContext(temporary.get()));
2600+
2601+
// Force the result into the temporary.
2602+
if (!result.isInContext()) {
2603+
temporary->copyOrInitValueInto(*this, expr, result, /*init*/ true);
2604+
temporary->finishInitialization(*this);
2605+
}
2606+
2607+
return temporary->getManagedAddress();
2608+
}
2609+
25902610
ManagedValue
25912611
SILGenFunction::emitRValueAsOrig(Expr *expr, AbstractionPattern origPattern,
25922612
const TypeLowering &origTL, SGFContext ctxt) {

lib/Sema/CSApply.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5304,6 +5304,27 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
53045304
isBridged);
53055305
}
53065306

5307+
case ConversionRestrictionKind::HashableToAnyHashable: {
5308+
// Look through implicitly unwrapped optionals.
5309+
if (auto objTy
5310+
= cs.lookThroughImplicitlyUnwrappedOptionalType(expr->getType())) {
5311+
expr = coerceImplicitlyUnwrappedOptionalToValue(expr, objTy, locator);
5312+
}
5313+
5314+
// Find the conformance of the source type to Hashable.
5315+
auto hashable = tc.Context.getProtocol(KnownProtocolKind::Hashable);
5316+
ProtocolConformance *conformance;
5317+
bool conforms = tc.conformsToProtocol(expr->getType(), hashable, cs.DC,
5318+
ConformanceCheckFlags::InExpression,
5319+
&conformance);
5320+
assert(conforms && "must conform to Hashable");
5321+
(void)conforms;
5322+
ProtocolConformanceRef conformanceRef(hashable, conformance);
5323+
5324+
return new (tc.Context) AnyHashableErasureExpr(expr, toType,
5325+
conformanceRef);
5326+
}
5327+
53075328
case ConversionRestrictionKind::DictionaryUpcast: {
53085329
// Look through implicitly unwrapped optionals.
53095330
if (auto objTy

0 commit comments

Comments
 (0)