Skip to content

Commit 85611db

Browse files
committed
Allow marker protocols as existentials (but still ban casts)
There is no problem with having an existential value that involves a marker protocol. However, since there is no runtime checking for marker protocols, ban "as?" and "is" casts to them.
1 parent 1c2b80f commit 85611db

File tree

4 files changed

+31
-22
lines changed

4 files changed

+31
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5625,8 +5625,8 @@ ERROR(marker_protocol_requirement, none,
56255625
ERROR(marker_protocol_inherit_nonmarker, none,
56265626
"marker protocol %0 cannot inherit non-marker protocol %1",
56275627
(DeclName, DeclName))
5628-
ERROR(marker_protocol_value,none,
5629-
"marker protocol %0 can only be used in generic constraints", (DeclName))
5628+
ERROR(marker_protocol_cast,none,
5629+
"marker protocol %0 cannot be used in a conditional cast", (DeclName))
56305630

56315631
//------------------------------------------------------------------------------
56325632
// MARK: differentiable programming diagnostics

lib/Sema/MiscDiagnostics.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "TypeCheckConcurrency.h"
2020
#include "TypeChecker.h"
2121
#include "swift/AST/ASTWalker.h"
22+
#include "swift/AST/ExistentialLayout.h"
2223
#include "swift/AST/NameLookup.h"
2324
#include "swift/AST/NameLookupRequests.h"
2425
#include "swift/AST/Pattern.h"
@@ -65,6 +66,7 @@ static Expr *isImplicitPromotionToOptional(Expr *E) {
6566
/// - Warn about promotions to optional in specific syntactic forms.
6667
/// - Error about collection literals that default to Any collections in
6768
/// invalid positions.
69+
/// - Marker protocols cannot occur as the type of an as? or is expression.
6870
///
6971
static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
7072
bool isExprStmt) {
@@ -311,9 +313,31 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
311313
}
312314
}
313315

316+
// Diagnose checked casts that involve marker protocols.
317+
if (auto cast = dyn_cast<CheckedCastExpr>(E)) {
318+
checkCheckedCastExpr(cast);
319+
}
320+
314321
return { true, E };
315322
}
316323

324+
void checkCheckedCastExpr(CheckedCastExpr *cast) {
325+
if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))
326+
return;
327+
328+
Type castType = cast->getCastType();
329+
if (!castType || !castType->isExistentialType())
330+
return;
331+
332+
auto layout = castType->getExistentialLayout();
333+
for (auto proto : layout.getProtocols()) {
334+
if (proto->getDecl()->isMarkerProtocol()) {
335+
Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
336+
proto->getDecl()->getName());
337+
}
338+
}
339+
}
340+
317341
/// Visit the argument/s represented by either a ParenExpr or TupleExpr,
318342
/// unshuffling if needed. If any other kind of expression, will pass it
319343
/// straight back.

lib/Sema/TypeCheckType.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3906,12 +3906,6 @@ class UnsupportedProtocolVisitor
39063906
proto->getName());
39073907
T->setInvalid();
39083908
}
3909-
if (proto->isMarkerProtocol()) {
3910-
Ctx.Diags.diagnose(comp->getNameLoc(),
3911-
diag::marker_protocol_value,
3912-
proto->getName());
3913-
T->setInvalid();
3914-
}
39153909
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
39163910
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
39173911
type.findIf([&](Type type) -> bool {
@@ -3921,15 +3915,6 @@ class UnsupportedProtocolVisitor
39213915
auto layout = type->getExistentialLayout();
39223916
for (auto *proto : layout.getProtocols()) {
39233917
auto *protoDecl = proto->getDecl();
3924-
3925-
if (protoDecl->isMarkerProtocol()) {
3926-
Ctx.Diags.diagnose(comp->getNameLoc(),
3927-
diag::marker_protocol_value,
3928-
protoDecl->getName());
3929-
T->setInvalid();
3930-
continue;
3931-
}
3932-
39333918
if (protoDecl->existentialTypeSupported())
39343919
continue;
39353920

test/attr/attr_marker_protocol.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ func testGenericOk(i: Int, arr: [Int], nope: [Double]) {
3838

3939
// Incorrect uses of marker protocols in types.
4040
func testNotOkay(a: Any) {
41-
var mp1: P3 = 17 // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
41+
var mp1: P3 = 17
4242
_ = mp1
4343
mp1 = 17
4444

45-
if let mp2 = a as? P3 { _ = mp2 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
46-
if let mp3 = a as? AnyObject & P3 { _ = mp3 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
47-
if a is AnyObject & P3 { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
45+
if let mp2 = a as? P3 { _ = mp2 } // expected-error{{marker protocol 'P3' cannot be used in a conditional cast}}
46+
if let mp3 = a as? AnyObject & P3 { _ = mp3 } // expected-error{{marker protocol 'P3' cannot be used in a conditional cast}}
47+
if a is AnyObject & P3 { } // expected-error{{marker protocol 'P3' cannot be used in a conditional cast}}
4848

49-
func inner(p3: P3) { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
49+
func inner(p3: P3) { }
5050
}
5151

0 commit comments

Comments
 (0)