mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Embedded] Diagnose non-final generic methods in class in the type checker
Move the diagnostic about non-final generic methods in classes up to the type checker, so that it is available to `-Wwarning EmbeddedRestrictions` and earlier in the pipeline. The SIL version of this is still available as a backstop. Yet another part of rdar://133874555.
This commit is contained in:
@@ -8612,6 +8612,9 @@ GROUPED_ERROR(weak_unowned_in_embedded_swift, EmbeddedRestrictions, none,
|
||||
GROUPED_WARNING(untyped_throws_in_embedded_swift, EmbeddedRestrictions,
|
||||
DefaultIgnore,
|
||||
"untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'", ())
|
||||
GROUPED_ERROR(generic_nonfinal_in_embedded_swift, EmbeddedRestrictions, none,
|
||||
"generic %kind0 in a class %select{must be 'final'|cannot be 'required'}1 in Embedded Swift",
|
||||
(const Decl *, bool))
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: @abi Attribute
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
static DiagnosticBehavior
|
||||
defaultEmbeddedLimitationForError(const DeclContext *dc, SourceLoc loc) {
|
||||
if (dc->getASTContext().LangOpts.hasFeature(Feature::Embedded))
|
||||
return DiagnosticBehavior::Unspecified;
|
||||
|
||||
return DiagnosticBehavior::Warning;
|
||||
}
|
||||
|
||||
std::optional<DiagnosticBehavior>
|
||||
swift::shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc,
|
||||
bool wasAlwaysEmbeddedError) {
|
||||
@@ -33,7 +41,7 @@ swift::shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc,
|
||||
// as errors. Use "unspecified" so we don't change anything.
|
||||
if (dc->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
|
||||
wasAlwaysEmbeddedError) {
|
||||
return DiagnosticBehavior::Unspecified;
|
||||
return defaultEmbeddedLimitationForError(dc, loc);
|
||||
}
|
||||
|
||||
// Check one of the Embedded restriction diagnostics that is ignored by
|
||||
@@ -66,6 +74,42 @@ swift::shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc,
|
||||
return DiagnosticBehavior::Unspecified;
|
||||
}
|
||||
|
||||
/// Determine whether the inner signature is more generic than the outer
|
||||
/// signature, ignoring differences that
|
||||
static bool isABIMoreGenericThan(GenericSignature innerSig, GenericSignature outerSig) {
|
||||
auto canInnerSig = innerSig.getCanonicalSignature();
|
||||
auto canOuterSig = outerSig.getCanonicalSignature();
|
||||
if (canInnerSig == canOuterSig)
|
||||
return false;
|
||||
|
||||
// The inner signature added generic parameters.
|
||||
if (canOuterSig.getGenericParams().size() !=
|
||||
canInnerSig.getGenericParams().size())
|
||||
return true;
|
||||
|
||||
// Look at the requirements of the inner signature that aren't satisfied
|
||||
// by the outer signature, to see if there are any requirements that aren't
|
||||
// just marker protocols.
|
||||
auto requirements = canInnerSig.requirementsNotSatisfiedBy(canOuterSig);
|
||||
for (const auto &req : requirements) {
|
||||
switch (req.getKind()) {
|
||||
case RequirementKind::Conformance:
|
||||
if (req.getProtocolDecl()->isMarkerProtocol())
|
||||
continue;
|
||||
|
||||
return true;
|
||||
|
||||
case RequirementKind::Superclass:
|
||||
case RequirementKind::Layout:
|
||||
case RequirementKind::SameShape:
|
||||
case RequirementKind::SameType:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check embedded restrictions in the signature of the given function.
|
||||
void swift::checkEmbeddedRestrictionsInSignature(
|
||||
const AbstractFunctionDecl *func) {
|
||||
@@ -80,6 +124,20 @@ void swift::checkEmbeddedRestrictionsInSignature(
|
||||
!func->hasPolymorphicEffect(EffectKind::Throws)) {
|
||||
diagnoseUntypedThrowsInEmbedded(func, throwsLoc);
|
||||
}
|
||||
|
||||
// If we're in a class, one cannot have a non-final generic function.
|
||||
if (auto classDecl = dyn_cast<ClassDecl>(func->getDeclContext())) {
|
||||
if (!classDecl->isSemanticallyFinal() &&
|
||||
((isa<FuncDecl>(func) && !func->isSemanticallyFinal()) ||
|
||||
(isa<ConstructorDecl>(func) &&
|
||||
cast<ConstructorDecl>(func)->isRequired())) &&
|
||||
isABIMoreGenericThan(func->getGenericSignature(),
|
||||
classDecl->getGenericSignature())) {
|
||||
func->diagnose(diag::generic_nonfinal_in_embedded_swift, func,
|
||||
isa<ConstructorDecl>(func))
|
||||
.limitBehavior(defaultEmbeddedLimitationForError(func, func->getLoc()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void swift::diagnoseUntypedThrowsInEmbedded(
|
||||
|
||||
@@ -181,12 +181,14 @@ internal class _AnySequenceBox<Element> {
|
||||
@inlinable
|
||||
internal var _underestimatedCount: Int { _abstract() }
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
internal func _map<T>(
|
||||
_ transform: (Element) throws -> T
|
||||
) throws -> [T] {
|
||||
_abstract()
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
internal func _filter(
|
||||
@@ -534,12 +536,16 @@ internal final class _SequenceBox<S: Sequence>: _AnySequenceBox<S.Element> {
|
||||
internal override var _underestimatedCount: Int {
|
||||
return _base.underestimatedCount
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
internal override func _map<T>(
|
||||
_ transform: (Element) throws -> T
|
||||
) throws -> [T] {
|
||||
try _base.map(transform)
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
internal override func _filter(
|
||||
_ isIncluded: (Element) throws -> Bool
|
||||
@@ -627,12 +633,14 @@ internal final class _CollectionBox<S: Collection>: _AnyCollectionBox<S.Element>
|
||||
internal override var _underestimatedCount: Int {
|
||||
return _base.underestimatedCount
|
||||
}
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
internal override func _map<T>(
|
||||
_ transform: (Element) throws -> T
|
||||
) throws -> [T] {
|
||||
try _base.map(transform)
|
||||
}
|
||||
#endif
|
||||
@inlinable
|
||||
internal override func _filter(
|
||||
_ isIncluded: (Element) throws -> Bool
|
||||
@@ -822,12 +830,14 @@ internal final class _BidirectionalCollectionBox<S: BidirectionalCollection>
|
||||
internal override var _underestimatedCount: Int {
|
||||
return _base.underestimatedCount
|
||||
}
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
internal override func _map<T>(
|
||||
_ transform: (Element) throws -> T
|
||||
) throws -> [T] {
|
||||
try _base.map(transform)
|
||||
}
|
||||
#endif
|
||||
@inlinable
|
||||
internal override func _filter(
|
||||
_ isIncluded: (Element) throws -> Bool
|
||||
@@ -1035,12 +1045,14 @@ internal final class _RandomAccessCollectionBox<S: RandomAccessCollection>
|
||||
internal override var _underestimatedCount: Int {
|
||||
return _base.underestimatedCount
|
||||
}
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
internal override func _map<T>(
|
||||
_ transform: (Element) throws -> T
|
||||
) throws -> [T] {
|
||||
try _base.map(transform)
|
||||
}
|
||||
#endif
|
||||
@inlinable
|
||||
internal override func _filter(
|
||||
_ isIncluded: (Element) throws -> Bool
|
||||
@@ -1322,6 +1334,7 @@ extension AnySequence {
|
||||
return _box._underestimatedCount
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
@_alwaysEmitIntoClient
|
||||
public func map<T, E>(
|
||||
@@ -1334,7 +1347,6 @@ extension AnySequence {
|
||||
}
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
// ABI-only entrypoint for the rethrows version of map, which has been
|
||||
// superseded by the typed-throws version. Expressed as "throws", which is
|
||||
// ABI-compatible with "rethrows".
|
||||
@@ -1428,6 +1440,7 @@ extension AnyCollection {
|
||||
return _box._underestimatedCount
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
@_alwaysEmitIntoClient
|
||||
public func map<T, E>(
|
||||
@@ -1440,7 +1453,6 @@ extension AnyCollection {
|
||||
}
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
// ABI-only entrypoint for the rethrows version of map, which has been
|
||||
// superseded by the typed-throws version. Expressed as "throws", which is
|
||||
// ABI-compatible with "rethrows".
|
||||
@@ -1540,6 +1552,7 @@ extension AnyBidirectionalCollection {
|
||||
return _box._underestimatedCount
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
@_alwaysEmitIntoClient
|
||||
public func map<T, E>(
|
||||
@@ -1552,7 +1565,6 @@ extension AnyBidirectionalCollection {
|
||||
}
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
// ABI-only entrypoint for the rethrows version of map, which has been
|
||||
// superseded by the typed-throws version. Expressed as "throws", which is
|
||||
// ABI-compatible with "rethrows".
|
||||
@@ -1654,6 +1666,7 @@ extension AnyRandomAccessCollection {
|
||||
return _box._underestimatedCount
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
@inlinable
|
||||
@_alwaysEmitIntoClient
|
||||
public func map<T, E>(
|
||||
@@ -1666,7 +1679,6 @@ extension AnyRandomAccessCollection {
|
||||
}
|
||||
}
|
||||
|
||||
#if !$Embedded
|
||||
// ABI-only entrypoint for the rethrows version of map, which has been
|
||||
// superseded by the typed-throws version. Expressed as "throws", which is
|
||||
// ABI-compatible with "rethrows".
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
|
||||
public class MyClass {
|
||||
public func foo<T>(t: T) { } // expected-error {{classes cannot have a non-final, generic method 'foo(t:)' in embedded Swift}}
|
||||
public func foo<T>(t: T) { } // expected-error {{generic instance method 'foo(t:)' in a class must be 'final' in Embedded Swift}}
|
||||
public func bar() { }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func testit2() -> C2<S> {
|
||||
}
|
||||
|
||||
open class C3<X> {
|
||||
public func foo<T>(t: T) {} // expected-error {{classes cannot have a non-final, generic method 'foo(t:)' in embedded Swift}}
|
||||
public func foo<T>(t: T) {} // expected-error {{generic instance method 'foo(t:)' in a class must be 'final' in Embedded Swift}}
|
||||
}
|
||||
|
||||
func testit3() -> C3<S> {
|
||||
|
||||
@@ -64,6 +64,27 @@ public struct MyStruct {
|
||||
unowned(unsafe) var unownedUnsafe: MyClass
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// generic, non-final functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
protocol P { }
|
||||
|
||||
class MyGenericClass<T> {
|
||||
func f<U>(value: U) { } // expected-nonembedded-warning{{generic instance method 'f(value:)' in a class must be 'final' in Embedded Swift}}
|
||||
// expected-embedded-error@-1{{generic instance method 'f(value:)' in a class must be 'final' in Embedded Swift}}
|
||||
func g() { }
|
||||
class func h() where T: P { } // expected-nonembedded-warning{{generic class method 'h()' in a class must be 'final' in Embedded Swift}}
|
||||
// expected-embedded-error@-1{{generic class method 'h()' in a class must be 'final' in Embedded Swift}}
|
||||
|
||||
init<U>(value: U) { } // okay, can be directly called
|
||||
|
||||
required init() { } // non-generic is okay
|
||||
|
||||
required init<V>(something: V) { } // expected-nonembedded-warning{{generic initializer 'init(something:)' in a class cannot be 'required' in Embedded Swift}}
|
||||
// expected-embedded-error@-1{{generic initializer 'init(something:)' in a class cannot be 'required' in Embedded Swift}}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// #if handling to suppress diagnostics for non-Embedded-only code
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
# Embedded Swift language restrictions (EmbeddedRestrictions)
|
||||
|
||||
Embedded Swift is a compilation model of Swift that 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.
|
||||
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.
|
||||
|
||||
Diagnostics in the `EmbeddedRestrictions` group describe those language features that cannot be used in Embedded Swift. For example, Embedded Swift uses a simplified reference-counting model that does not support `weak` or `unowned` references. The following will produce a diagnostic in Embedded Swift:
|
||||
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.
|
||||
|
||||
Diagnostics in the `EmbeddedRestrictions` group describe those language features that cannot be used in Embedded Swift. These include:
|
||||
|
||||
* `weak` and `unowned` references, because Embedded Swift uses a simplified reference-counting model that cannot support them. For example:
|
||||
|
||||
class Node {
|
||||
weak var parent: Node? // error: attribute 'weak' cannot be used 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> {
|
||||
func f<U>(value: U) { } // warning: generic instance method 'f(value:)' in a class must be 'final' in Embedded Swift
|
||||
|
||||
func g() { } // okay, not generic relative to the class itself
|
||||
|
||||
class func h() where T: P { } // warning: generic class method 'h()' in a class must be 'final' in Embedded Swift
|
||||
}
|
||||
|
||||
## See Also
|
||||
|
||||
- [A Vision for Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md)
|
||||
|
||||
Reference in New Issue
Block a user