Skip to content

Commit ade6e55

Browse files
committed
[Embedded] Diagnose uses of generic methods on existential values
Generic methods declared in protocols (and extensions thereof) cannot be used on existential values, because there is no way to specialize them for all potential types. Diagnose such cases in Embedded Swift mode and via `-Wwarning EmbeddedRestrictions`. This adds a bunch more warnings to the standard library that we'll need to clean up, probably by `#if`'ing more code out. Part of rdar://119383905.
1 parent 844ba5f commit ade6e55

File tree

7 files changed

+93
-9
lines changed

7 files changed

+93
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8615,6 +8615,10 @@ GROUPED_WARNING(untyped_throws_in_embedded_swift, EmbeddedRestrictions,
86158615
GROUPED_ERROR(generic_nonfinal_in_embedded_swift, EmbeddedRestrictions, none,
86168616
"generic %kind0 in a class %select{must be 'final'|cannot be 'required'}1 in Embedded Swift",
86178617
(const Decl *, bool))
8618+
GROUPED_WARNING(use_generic_member_of_existential_in_embedded_swift,
8619+
EmbeddedRestrictions, DefaultIgnore,
8620+
"cannot use generic %kind0 on a value of type %1 in Embedded Swift",
8621+
(const Decl *, Type))
86188622

86198623
//===----------------------------------------------------------------------===//
86208624
// MARK: @abi Attribute

lib/Sema/CSApply.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "MiscDiagnostics.h"
2222
#include "OpenedExistentials.h"
2323
#include "TypeCheckConcurrency.h"
24+
#include "TypeCheckEmbedded.h"
2425
#include "TypeCheckMacros.h"
2526
#include "TypeCheckProtocol.h"
2627
#include "TypeCheckType.h"
@@ -924,8 +925,9 @@ namespace {
924925
/// \returns An OpaqueValueExpr that provides a reference to the value
925926
/// stored within the expression or its metatype (if the base was a
926927
/// metatype).
927-
Expr *openExistentialReference(Expr *base, ExistentialArchetypeType *archetype,
928-
ValueDecl *member) {
928+
Expr *openExistentialReference(Expr *base,
929+
ExistentialArchetypeType *archetype,
930+
ValueDecl *member, SourceLoc memberLoc) {
929931
assert(archetype && "archetype not already opened?");
930932

931933
// Dig out the base type.
@@ -955,6 +957,11 @@ namespace {
955957

956958
assert(baseTy->isAnyExistentialType() && "Type must be existential");
957959

960+
// Embedded Swift has limitations on the use of generic members of
961+
// existentials. Diagnose them here.
962+
diagnoseGenericMemberOfExistentialInEmbedded(
963+
dc, memberLoc, baseTy, member);
964+
958965
// If the base was an lvalue but it will only be treated as an
959966
// rvalue, turn the base into an rvalue now. This results in
960967
// better SILGen.
@@ -1983,7 +1990,8 @@ namespace {
19831990
(!member->getDeclContext()->getSelfProtocolDecl() &&
19841991
baseIsInstance && member->isInstanceMember())) {
19851992
// Open the existential before performing the member reference.
1986-
base = openExistentialReference(base, knownOpened->second, member);
1993+
base = openExistentialReference(base, knownOpened->second, member,
1994+
memberLoc.getBaseNameLoc());
19871995
baseTy = baseOpenedTy;
19881996
selfTy = baseTy;
19891997
openedExistential = true;
@@ -2490,7 +2498,8 @@ namespace {
24902498
auto memberLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
24912499
auto knownOpened = solution.OpenedExistentialTypes.find(memberLoc);
24922500
if (knownOpened != solution.OpenedExistentialTypes.end()) {
2493-
base = openExistentialReference(base, knownOpened->second, subscript);
2501+
base = openExistentialReference(base, knownOpened->second, subscript,
2502+
args->getLoc());
24942503
baseTy = knownOpened->second;
24952504
}
24962505

@@ -6551,7 +6560,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
65516560
cs.getConstraintLocator(argLoc));
65526561
if (knownOpened != solution.OpenedExistentialTypes.end()) {
65536562
argExpr = openExistentialReference(
6554-
argExpr, knownOpened->second, callee.getDecl());
6563+
argExpr, knownOpened->second, callee.getDecl(), apply->getLoc());
65556564
argType = cs.getType(argExpr);
65566565
}
65576566
}

lib/Sema/TypeCheckEmbedded.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,20 @@ void swift::diagnoseUntypedThrowsInEmbedded(
152152
.limitBehavior(*behavior)
153153
.fixItInsertAfter(throwsLoc, "(<#thrown error type#>)");
154154
}
155+
156+
void swift::diagnoseGenericMemberOfExistentialInEmbedded(
157+
const DeclContext *dc, SourceLoc loc,
158+
Type baseType, const ValueDecl *member) {
159+
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
160+
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, loc, true);
161+
if (!behavior)
162+
return;
163+
164+
if (isABIMoreGenericThan(
165+
member->getInnermostDeclContext()->getGenericSignatureOfContext(),
166+
member->getDeclContext()->getGenericSignatureOfContext())) {
167+
dc->getASTContext().Diags.diagnose(loc, diag::use_generic_member_of_existential_in_embedded_swift, member,
168+
baseType)
169+
.limitBehavior(*behavior);
170+
}
171+
}

lib/Sema/TypeCheckEmbedded.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ class AbstractFunctionDecl;
2525
class DeclContext;
2626
struct DiagnosticBehavior;
2727
class SourceLoc;
28-
28+
class Type;
29+
class ValueDecl;
30+
2931
/// Whether we should diagnose language-level limitations of Embedded Swift
3032
/// at the given source location, and how.
3133
///
@@ -46,5 +48,10 @@ void checkEmbeddedRestrictionsInSignature(const AbstractFunctionDecl *func);
4648
/// Diagnose a declaration of typed throws at the given location.
4749
void diagnoseUntypedThrowsInEmbedded(const DeclContext *dc, SourceLoc throwsLoc);
4850

51+
/// Diagnose references to a generic member via an existential type, which are
52+
/// not available in Embedded Swift.
53+
void diagnoseGenericMemberOfExistentialInEmbedded(
54+
const DeclContext *dc, SourceLoc loc,
55+
Type baseType, const ValueDecl *member);
4956
}
5057
#endif // SWIFT_SEMA_TYPECHECKEMBEDDED_H

test/embedded/existential-generic-error.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ public protocol MyProtocol: AnyObject {
99
}
1010

1111
func test_some(p: some MyProtocol) {
12-
p.foo(ptr: nil, value: 0) // expected-error {{a protocol type cannot contain a generic method 'foo(ptr:value:)' in embedded Swift}}
12+
p.foo(ptr: nil, value: 0) // expected-error {{a protocol type cannot contain a generic method 'foo(ptr:value:)' in embedded Swift}}
1313
}
1414

1515
public func test_any(p: any MyProtocol) {
16-
test_some(p: p)
16+
test_some(p: p)
17+
// expected-warning@-1{{cannot use generic global function 'test_some(p:)' on a value of type 'any MyProtocol' in Embedded Swift}}
1718
}

test/embedded/restrictions.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,35 @@ class MyGenericClass<T> {
8585
// expected-embedded-error@-1{{generic initializer 'init(something:)' in a class cannot be 'required' in Embedded Swift}}
8686
}
8787

88+
// ---------------------------------------------------------------------------
89+
// generic functions on existentials
90+
// ---------------------------------------------------------------------------
91+
92+
public protocol Q {
93+
func f<T>(_ value: T)
94+
func okay()
95+
}
96+
97+
extension Q {
98+
public func g<T>(_ value: T) {
99+
f(value)
100+
}
101+
102+
public mutating func h<T>(_ value: T) {
103+
f(value)
104+
}
105+
}
106+
107+
public func existentials(q: any AnyObject & Q, i: Int) {
108+
q.okay()
109+
q.f(i) // expected-warning{{cannot use generic instance method 'f' on a value of type 'any AnyObject & Q' in Embedded Swift}}
110+
111+
q.g(i) // expected-warning{{cannot use generic instance method 'g' on a value of type 'any AnyObject & Q' in Embedded Swift}}
112+
113+
var qm = q
114+
qm.h(i) // expected-warning{{cannot use generic instance method 'h' on a value of type 'any AnyObject & Q' in Embedded Swift}}
115+
}
116+
88117
// ---------------------------------------------------------------------------
89118
// #if handling to suppress diagnostics for non-Embedded-only code
90119
// ---------------------------------------------------------------------------

userdocs/diagnostics/embedded-restrictions.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Embedded Swift language restrictions (EmbeddedRestrictions)
22

3-
Embedded Swift is a subset of the Swift language that compiles to smaller binaries that do not rely on the Swift runtime. Embedded Swift produces some restrictions on the use of the Swift language to eliminate the runtime dependency, which are captured by the `EmbeddedRestrictions` diagnostic group.
3+
Embedded Swift is a subset of the Swift language that that introduces some restrictions on the use of language features to eliminate the need for the Swift runtime. These restrictions are captured by the `EmbeddedRestrictions` diagnostic group.
44

55
The Embedded Swift compilation model can produce extremely small binaries without external dependencies, suitable for restricted environments including embedded (microcontrollers) and baremetal setups (no operating system at all), and low-level environments (firmware, kernels, device drivers, low-level components of userspace OS runtimes). While the vast majority of Swift language features are available in Embedded Swift, there are some language features that require the full Swift standard library and runtime, which are not available in Embedded Swift.
66

@@ -22,6 +22,23 @@ Diagnostics in the `EmbeddedRestrictions` group describe those language features
2222
class func h() where T: P { } // warning: generic class method 'h()' in a class must be 'final' in Embedded Swift
2323
}
2424

25+
* Generic methods used on values of protocol type, which are prohibited because they cannot be specialized for every possible call site. For example:
26+
27+
protocol P: AnyObject {
28+
func doNothing()
29+
func doSomething<T>(on value: T)
30+
}
31+
32+
func testGenerics<Value: P>(value: value, i: Int) {
33+
value.doNothing() // okay
34+
value.doSomething(on: i) // okay, always specialized
35+
}
36+
37+
func testValuesOfProtocolType(value: any P, i: Int) {
38+
value.doNothing() // okay
39+
value.doSomething(on: i) // warning: cannot use generic instance method 'doSomething(on:)' on a value of type 'any P' in Embedded Swift
40+
}
41+
2542
## See Also
2643

2744
- [A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)

0 commit comments

Comments
 (0)