Skip to content

Commit ab82049

Browse files
committed
[Sema] Don't treat non-class archetypes as non-optional when casting to them
1 parent 6cf1aa3 commit ab82049

File tree

4 files changed

+103
-16
lines changed

4 files changed

+103
-16
lines changed

include/swift/AST/Types.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,12 @@ class alignas(1 << TypeAlignInBits) TypeBase {
573573
/// whether a type parameter exists at any position.
574574
bool isTypeParameter();
575575

576+
/// \brief Determine whether this type can dynamically be an optional type.
577+
///
578+
/// \param includeExistential Whether an existential type should be considered
579+
/// such a type.
580+
bool canDynamicallyBeOptionalType(bool includeExistential);
581+
576582
/// Determine whether this type contains a type parameter somewhere in it.
577583
bool hasTypeParameter() {
578584
return getRecursiveProperties().hasTypeParameter();
@@ -4998,6 +5004,14 @@ inline bool TypeBase::isOpenedExistentialWithError() {
49985004
return false;
49995005
}
50005006

5007+
inline bool TypeBase::canDynamicallyBeOptionalType(bool includeExistential) {
5008+
CanType T = getCanonicalType();
5009+
auto isArchetypeOrExistential = isa<ArchetypeType>(T) ||
5010+
(includeExistential && T.isExistentialType());
5011+
5012+
return isArchetypeOrExistential && !T.isAnyClassReferenceType();
5013+
}
5014+
50015015
inline ClassDecl *TypeBase::getClassOrBoundGenericClass() {
50025016
return getCanonicalType().getClassOrBoundGenericClass();
50035017
}

lib/SIL/DynamicCasts.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,13 @@ bool swift::isError(ModuleDecl *M, CanType Ty) {
239239
return false;
240240
}
241241

242-
/// Given that a type is not statically known to be an optional type, check whether
243-
/// it might dynamically be an optional type.
244-
static bool canDynamicallyBeOptionalType(CanType type) {
242+
/// Given that a type is not statically known to be an optional type, check
243+
/// whether it might dynamically be able to store an optional.
244+
static bool canDynamicallyStoreOptional(CanType type) {
245245
assert(!type.getOptionalObjectType());
246-
return (isa<ArchetypeType>(type) || type.isExistentialType())
247-
&& !type.isAnyClassReferenceType();
246+
return type->canDynamicallyBeOptionalType(/* includeExistential */ true);
248247
}
249-
248+
250249
/// Given two class types, check whether there's a hierarchy relationship
251250
/// between them.
252251
static DynamicCastFeasibility
@@ -326,7 +325,7 @@ swift::classifyDynamicCast(ModuleDecl *M,
326325
auto result = classifyDynamicCast(M, source, targetObject,
327326
/* isSourceTypeExact */ false,
328327
isWholeModuleOpts);
329-
if (canDynamicallyBeOptionalType(source))
328+
if (canDynamicallyStoreOptional(source))
330329
result = atWorst(result, DynamicCastFeasibility::MaySucceed);
331330
return result;
332331

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,14 +3299,21 @@ namespace {
32993299
auto destValueType
33003300
= finalResultType->lookThroughAllOptionalTypes(destOptionals);
33013301

3302-
// When performing a bridging operation, if the destination value type
3303-
// is 'AnyObject', leave any extra optionals on the source in place.
3304-
if (castKind == OptionalBindingsCastKind::Bridged &&
3305-
srcOptionals.size() > destOptionals.size() &&
3306-
destValueType->isAnyObject()) {
3307-
srcType = srcOptionals[destOptionals.size()];
3308-
srcOptionals.erase(srcOptionals.begin() + destOptionals.size(),
3309-
srcOptionals.end());
3302+
auto isBridgeToAnyObject =
3303+
castKind == OptionalBindingsCastKind::Bridged &&
3304+
destValueType->isAnyObject();
3305+
3306+
// If the destination value type is 'AnyObject' when performing a
3307+
// bridging operation, or if the destination value type could dynamically
3308+
// be an optional type, leave any extra optionals on the source in place.
3309+
if (isBridgeToAnyObject || destValueType->canDynamicallyBeOptionalType(
3310+
/* includeExistential */ false)) {
3311+
auto destOptionalsCount = destOptionals.size() - destExtraOptionals;
3312+
if (srcOptionals.size() > destOptionalsCount) {
3313+
srcType = srcOptionals[destOptionalsCount];
3314+
srcOptionals.erase(srcOptionals.begin() + destOptionalsCount,
3315+
srcOptionals.end());
3316+
}
33103317
}
33113318

33123319
// When performing a bridging operation, if the destination type

test/SILGen/generic_casts.swift

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,74 @@ func class_existential_is_class(_ p: ClassBound) -> Bool {
217217
}
218218

219219
// CHECK-LABEL: sil hidden @$S13generic_casts27optional_anyobject_to_classyAA1CCSgyXlSgF
220-
// CHECK: checked_cast_br {{%.*}} : $AnyObject to $C
221220
func optional_anyobject_to_class(_ p: AnyObject?) -> C? {
222221
return p as? C
222+
// CHECK: checked_cast_br {{%.*}} : $AnyObject to $C
223223
}
224+
225+
// The below tests are to ensure we don't dig into an optional operand when
226+
// casting to a non-class archetype, as it could dynamically be an optional type.
227+
228+
// CHECK-LABEL: sil hidden @$S13generic_casts32optional_any_to_opaque_archetype{{[_0-9a-zA-Z]*}}F
229+
func optional_any_to_opaque_archetype<T>(_ x: Any?) -> T {
230+
return x as! T
231+
// CHECK: bb0([[RET:%.*]] : @trivial $*T, {{%.*}} : @trivial $*Optional<Any>):
232+
// CHECK: unconditional_checked_cast_addr Optional<Any> in {{%.*}} : $*Optional<Any> to T in [[RET]] : $*T
233+
}
234+
235+
// CHECK-LABEL: sil hidden @$S13generic_casts46optional_any_conditionally_to_opaque_archetype{{[_0-9a-zA-Z]*}}F
236+
func optional_any_conditionally_to_opaque_archetype<T>(_ x: Any?) -> T? {
237+
return x as? T
238+
// CHECK: checked_cast_addr_br take_always Optional<Any> in {{%.*}} : $*Optional<Any> to T in {{%.*}} : $*T
239+
}
240+
241+
// CHECK-LABEL: sil hidden @$S13generic_casts32optional_any_is_opaque_archetype{{[_0-9a-zA-Z]*}}F
242+
func optional_any_is_opaque_archetype<T>(_ x: Any?, _: T) -> Bool {
243+
return x is T
244+
// CHECK: checked_cast_addr_br take_always Optional<Any> in {{%.*}} : $*Optional<Any> to T in {{%.*}} : $*T
245+
}
246+
247+
// But we can dig into at most one layer of the operand if it's
248+
// an optional archetype...
249+
250+
// CHECK-LABEL: sil hidden @$S13generic_casts016optional_any_to_C17_opaque_archetype{{[_0-9a-zA-Z]*}}F
251+
func optional_any_to_optional_opaque_archetype<T>(_ x: Any?) -> T? {
252+
return x as! T?
253+
// CHECK: unconditional_checked_cast_addr Any in {{%.*}} : $*Any to T in {{%.*}} : $*T
254+
}
255+
256+
// CHECK-LABEL: sil hidden @$S13generic_casts030optional_any_conditionally_to_C17_opaque_archetype{{[_0-9a-zA-Z]*}}F
257+
func optional_any_conditionally_to_optional_opaque_archetype<T>(_ x: Any?) -> T?? {
258+
return x as? T?
259+
// CHECK: checked_cast_addr_br take_always Any in {{%.*}} : $*Any to T in {{%.*}} : $*T
260+
}
261+
262+
// CHECK-LABEL: sil hidden @$S13generic_casts016optional_any_is_C17_opaque_archetype{{[_0-9a-zA-Z]*}}F
263+
func optional_any_is_optional_opaque_archetype<T>(_ x: Any?, _: T) -> Bool {
264+
return x is T?
265+
// Because the levels of optional are the same, 'is' doesn't transform into an 'as?',
266+
// so we just cast directly without digging into the optional operand.
267+
// CHECK: checked_cast_addr_br take_always Optional<Any> in {{%.*}} : $*Optional<Any> to Optional<T> in {{%.*}} : $*Optional<T>
268+
}
269+
270+
// And we can dig into the operand when casting to a class archetype, as it
271+
// cannot dynamically be optional...
272+
273+
// CHECK-LABEL: sil hidden @$S13generic_casts31optional_any_to_class_archetype{{[_0-9a-zA-Z]*}}F
274+
func optional_any_to_class_archetype<T : AnyObject>(_ x: Any?) -> T {
275+
return x as! T
276+
// CHECK: unconditional_checked_cast_addr Any in {{%.*}} : $*Any to T in {{%.*}} : $*T
277+
}
278+
279+
// CHECK-LABEL: sil hidden @$S13generic_casts45optional_any_conditionally_to_class_archetype{{[_0-9a-zA-Z]*}}F
280+
func optional_any_conditionally_to_class_archetype<T : AnyObject>(_ x: Any?) -> T? {
281+
return x as? T
282+
// CHECK: checked_cast_addr_br take_always Any in {{%.*}} : $*Any to T in {{%.*}} : $*T
283+
}
284+
285+
// CHECK-LABEL: sil hidden @$S13generic_casts31optional_any_is_class_archetype{{[_0-9a-zA-Z]*}}F
286+
func optional_any_is_class_archetype<T : AnyObject>(_ x: Any?, _: T) -> Bool {
287+
return x is T
288+
// CHECK: checked_cast_addr_br take_always Any in {{%.*}} : $*Any to T in {{%.*}} : $*T
289+
}
290+

0 commit comments

Comments
 (0)