[Embedded] Diagnose dynamic casts to existentials

Embedded Swift doesn't have protocol conformance metadata, so it cannot
handle dynamic casts to existentials (nor should it).

Another part of rdar://119383905.
This commit is contained in:
Doug Gregor
2025-09-18 09:56:54 -07:00
parent 74d5ee26f0
commit a16c9f7ab4
7 changed files with 77 additions and 1 deletions

View File

@@ -8620,6 +8620,10 @@ GROUPED_WARNING(use_generic_member_of_existential_in_embedded_swift,
EmbeddedRestrictions, DefaultIgnore,
"cannot use generic %kind0 on a value of type %1 in Embedded Swift",
(const Decl *, Type))
GROUPED_WARNING(dynamic_cast_involving_protocol_in_embedded_swift,
EmbeddedRestrictions, DefaultIgnore,
"cannot perform a dynamic cast to a type involving %kind0 in Embedded Swift",
(const Decl *))
//===----------------------------------------------------------------------===//
// MARK: @abi Attribute

View File

@@ -17,6 +17,7 @@
#include "MiscDiagnostics.h"
#include "TypeCheckAvailability.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckEmbedded.h"
#include "TypeCheckInvertible.h"
#include "TypeChecker.h"
#include "swift/AST/ASTBridging.h"
@@ -390,6 +391,9 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
}
}
// Embedded Swift places restrictions on dynamic casting.
diagnoseDynamicCastInEmbedded(DC, cast);
// now, look for conditional casts to marker protocols.
if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))

View File

@@ -19,6 +19,8 @@
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Effects.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/Basic/SourceLoc.h"
@@ -157,7 +159,7 @@ void swift::diagnoseGenericMemberOfExistentialInEmbedded(
const DeclContext *dc, SourceLoc loc,
Type baseType, const ValueDecl *member) {
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, loc, true);
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, loc);
if (!behavior)
return;
@@ -169,3 +171,28 @@ void swift::diagnoseGenericMemberOfExistentialInEmbedded(
.limitBehavior(*behavior);
}
}
void swift::diagnoseDynamicCastInEmbedded(
const DeclContext *dc, const CheckedCastExpr *cast) {
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, cast->getLoc());
if (!behavior)
return;
// We only care about casts to existential types.
Type toType = cast->getCastType()->lookThroughAllOptionalTypes();
if (!toType->isAnyExistentialType())
return;
ExistentialLayout layout = toType->getExistentialLayout();
for (auto proto : layout.getProtocols()) {
if (proto->isMarkerProtocol())
continue;
dc->getASTContext().Diags.diagnose(
cast->getLoc(),
diag::dynamic_cast_involving_protocol_in_embedded_swift, proto)
.limitBehaviorIf(behavior);
return;
}
}

View File

@@ -24,6 +24,7 @@ namespace swift {
class AbstractFunctionDecl;
class DeclContext;
struct DiagnosticBehavior;
class CheckedCastExpr;
class SourceLoc;
class Type;
class ValueDecl;
@@ -53,5 +54,11 @@ void diagnoseUntypedThrowsInEmbedded(const DeclContext *dc, SourceLoc throwsLoc)
void diagnoseGenericMemberOfExistentialInEmbedded(
const DeclContext *dc, SourceLoc loc,
Type baseType, const ValueDecl *member);
/// Diagnose dynamic casts (is/as?/as!) to a type, which is not always available
/// in Embedded Swift.
void diagnoseDynamicCastInEmbedded(
const DeclContext *dc, const CheckedCastExpr *cast);
}
#endif // SWIFT_SEMA_TYPECHECKEMBEDDED_H

View File

@@ -14,6 +14,7 @@ public func test() -> Int {
func castToExistential<T>(x: T) {
if x is any FixedWidthInteger { // expected-error {{cannot do dynamic casting in embedded Swift}}
// expected-warning@-1{{cannot perform a dynamic cast to a type involving protocol 'FixedWidthInteger' in Embedded Swift}}
}
}

View File

@@ -111,6 +111,30 @@ public func existentials(q: any AnyObject & Q, i: Int) {
qm.h(i) // expected-warning{{cannot use generic instance method 'h' on a value of type 'any AnyObject & Q' in Embedded Swift}}
}
// ---------------------------------------------------------------------------
// Dynamic casting restrictions
// ---------------------------------------------------------------------------
class ConformsToQ: Q {
final func f<T>(_ value: T) { }
func okay() { }
}
func dynamicCasting(object: AnyObject, cq: ConformsToQ) {
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
if let q = object as? any AnyObject & Q {
_ = q
}
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
if object is any AnyObject & Q { }
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
_ = object as! AnyObject & Q
_ = cq as AnyObject & Q
}
// ---------------------------------------------------------------------------
// #if handling to suppress diagnostics for non-Embedded-only code
// ---------------------------------------------------------------------------

View File

@@ -12,6 +12,15 @@ Diagnostics in the `EmbeddedRestrictions` group describe those language features
weak var parent: Node? // error: attribute 'weak' cannot be used in Embedded Swift
}
* Dynamic casts to a type involving a protocol are not supported, because Embedded Swift does not include runtime metadata about protocol conformances. For example:
protocol P: AnyObject { }
func casting(object: AnyObject) {
if let p = object as? P { // error: cannot perform a dynamic cast to a type involving protocol 'P' in Embedded Swift
// ...
}
}
* Non-final generic methods in a class, which are prohibited because they cannot be specialized for every possible call site. For example:
class MyGenericClass<T> {