Skip to content

Commit a16c9f7

Browse files
committed
[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.
1 parent 74d5ee2 commit a16c9f7

File tree

7 files changed

+77
-1
lines changed

7 files changed

+77
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8620,6 +8620,10 @@ GROUPED_WARNING(use_generic_member_of_existential_in_embedded_swift,
86208620
EmbeddedRestrictions, DefaultIgnore,
86218621
"cannot use generic %kind0 on a value of type %1 in Embedded Swift",
86228622
(const Decl *, Type))
8623+
GROUPED_WARNING(dynamic_cast_involving_protocol_in_embedded_swift,
8624+
EmbeddedRestrictions, DefaultIgnore,
8625+
"cannot perform a dynamic cast to a type involving %kind0 in Embedded Swift",
8626+
(const Decl *))
86238627

86248628
//===----------------------------------------------------------------------===//
86258629
// MARK: @abi Attribute

lib/Sema/MiscDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "MiscDiagnostics.h"
1818
#include "TypeCheckAvailability.h"
1919
#include "TypeCheckConcurrency.h"
20+
#include "TypeCheckEmbedded.h"
2021
#include "TypeCheckInvertible.h"
2122
#include "TypeChecker.h"
2223
#include "swift/AST/ASTBridging.h"
@@ -390,6 +391,9 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
390391
}
391392
}
392393

394+
// Embedded Swift places restrictions on dynamic casting.
395+
diagnoseDynamicCastInEmbedded(DC, cast);
396+
393397
// now, look for conditional casts to marker protocols.
394398

395399
if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))

lib/Sema/TypeCheckEmbedded.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "swift/AST/Decl.h"
2020
#include "swift/AST/DiagnosticsSema.h"
2121
#include "swift/AST/Effects.h"
22+
#include "swift/AST/Expr.h"
23+
#include "swift/AST/ExistentialLayout.h"
2224
#include "swift/AST/SourceFile.h"
2325
#include "swift/AST/Types.h"
2426
#include "swift/Basic/SourceLoc.h"
@@ -157,7 +159,7 @@ void swift::diagnoseGenericMemberOfExistentialInEmbedded(
157159
const DeclContext *dc, SourceLoc loc,
158160
Type baseType, const ValueDecl *member) {
159161
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
160-
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, loc, true);
162+
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, loc);
161163
if (!behavior)
162164
return;
163165

@@ -169,3 +171,28 @@ void swift::diagnoseGenericMemberOfExistentialInEmbedded(
169171
.limitBehavior(*behavior);
170172
}
171173
}
174+
175+
void swift::diagnoseDynamicCastInEmbedded(
176+
const DeclContext *dc, const CheckedCastExpr *cast) {
177+
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
178+
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, cast->getLoc());
179+
if (!behavior)
180+
return;
181+
182+
// We only care about casts to existential types.
183+
Type toType = cast->getCastType()->lookThroughAllOptionalTypes();
184+
if (!toType->isAnyExistentialType())
185+
return;
186+
187+
ExistentialLayout layout = toType->getExistentialLayout();
188+
for (auto proto : layout.getProtocols()) {
189+
if (proto->isMarkerProtocol())
190+
continue;
191+
192+
dc->getASTContext().Diags.diagnose(
193+
cast->getLoc(),
194+
diag::dynamic_cast_involving_protocol_in_embedded_swift, proto)
195+
.limitBehaviorIf(behavior);
196+
return;
197+
}
198+
}

lib/Sema/TypeCheckEmbedded.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace swift {
2424
class AbstractFunctionDecl;
2525
class DeclContext;
2626
struct DiagnosticBehavior;
27+
class CheckedCastExpr;
2728
class SourceLoc;
2829
class Type;
2930
class ValueDecl;
@@ -53,5 +54,11 @@ void diagnoseUntypedThrowsInEmbedded(const DeclContext *dc, SourceLoc throwsLoc)
5354
void diagnoseGenericMemberOfExistentialInEmbedded(
5455
const DeclContext *dc, SourceLoc loc,
5556
Type baseType, const ValueDecl *member);
57+
58+
/// Diagnose dynamic casts (is/as?/as!) to a type, which is not always available
59+
/// in Embedded Swift.
60+
void diagnoseDynamicCastInEmbedded(
61+
const DeclContext *dc, const CheckedCastExpr *cast);
62+
5663
}
5764
#endif // SWIFT_SEMA_TYPECHECKEMBEDDED_H

test/embedded/metatypes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public func test() -> Int {
1414

1515
func castToExistential<T>(x: T) {
1616
if x is any FixedWidthInteger { // expected-error {{cannot do dynamic casting in embedded Swift}}
17+
// expected-warning@-1{{cannot perform a dynamic cast to a type involving protocol 'FixedWidthInteger' in Embedded Swift}}
1718
}
1819
}
1920

test/embedded/restrictions.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,30 @@ public func existentials(q: any AnyObject & Q, i: Int) {
111111
qm.h(i) // expected-warning{{cannot use generic instance method 'h' on a value of type 'any AnyObject & Q' in Embedded Swift}}
112112
}
113113

114+
// ---------------------------------------------------------------------------
115+
// Dynamic casting restrictions
116+
// ---------------------------------------------------------------------------
117+
118+
class ConformsToQ: Q {
119+
final func f<T>(_ value: T) { }
120+
func okay() { }
121+
}
122+
123+
func dynamicCasting(object: AnyObject, cq: ConformsToQ) {
124+
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
125+
if let q = object as? any AnyObject & Q {
126+
_ = q
127+
}
128+
129+
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
130+
if object is any AnyObject & Q { }
131+
132+
// expected-warning@+1{{cannot perform a dynamic cast to a type involving protocol 'Q' in Embedded Swift}}
133+
_ = object as! AnyObject & Q
134+
135+
_ = cq as AnyObject & Q
136+
}
137+
114138
// ---------------------------------------------------------------------------
115139
// #if handling to suppress diagnostics for non-Embedded-only code
116140
// ---------------------------------------------------------------------------

userdocs/diagnostics/embedded-restrictions.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ Diagnostics in the `EmbeddedRestrictions` group describe those language features
1212
weak var parent: Node? // error: attribute 'weak' cannot be used in Embedded Swift
1313
}
1414

15+
* Dynamic casts to a type involving a protocol are not supported, because Embedded Swift does not include runtime metadata about protocol conformances. For example:
16+
17+
protocol P: AnyObject { }
18+
func casting(object: AnyObject) {
19+
if let p = object as? P { // error: cannot perform a dynamic cast to a type involving protocol 'P' in Embedded Swift
20+
// ...
21+
}
22+
}
23+
1524
* Non-final generic methods in a class, which are prohibited because they cannot be specialized for every possible call site. For example:
1625

1726
class MyGenericClass<T> {

0 commit comments

Comments
 (0)