mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Embedded] Diagnose untyped throws as an Embedded Swift restriction
Untyped throws depends on existentials (`any Error`), and is therefore not available in Embedded Swift. Introduce a diagnostic that diagnoses any use of untyped throws, suggesting that one use typed throws instead. Make this an opt-in diagnostic enabled with `-Wwarning EmbeddedRestrictions`, whether in Embedded Swift or not, using the "default ignore" flag on these new warnings. Document this new diagnostic group, and put the existing Embedded Swift error about weak/unowned references in it as well. Part of the general push to have the type checker identify code that will not compile as Embedded Swift earlier, rdar://133874555.
This commit is contained in:
@@ -701,6 +701,10 @@ namespace swift {
|
||||
ignoredDiagnostics[(unsigned)id] = ignored;
|
||||
}
|
||||
|
||||
bool isIgnoredDiagnostic(DiagID id) const {
|
||||
return ignoredDiagnostics[(unsigned)id];
|
||||
}
|
||||
|
||||
void swap(DiagnosticState &other) {
|
||||
std::swap(showDiagnosticsAfterFatalError, other.showDiagnosticsAfterFatalError);
|
||||
std::swap(suppressWarnings, other.suppressWarnings);
|
||||
@@ -947,6 +951,10 @@ namespace swift {
|
||||
state.setIgnoredDiagnostic(id, true);
|
||||
}
|
||||
|
||||
bool isIgnoredDiagnostic(DiagID id) const {
|
||||
return state.isIgnoredDiagnostic(id);
|
||||
}
|
||||
|
||||
void resetHadAnyError() {
|
||||
state.resetHadAnyError();
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ GROUP(ClangDeclarationImport, "clang-declaration-import")
|
||||
GROUP(ConformanceIsolation, "conformance-isolation")
|
||||
GROUP(DeprecatedDeclaration, "deprecated-declaration")
|
||||
GROUP(DynamicCallable, "dynamic-callable-requirements")
|
||||
GROUP(EmbeddedRestrictions, "embedded-restrictions")
|
||||
GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version")
|
||||
GROUP(ExclusivityViolation, "exclusivity-violation")
|
||||
GROUP(ExistentialAny, "existential-any")
|
||||
|
||||
@@ -2234,9 +2234,6 @@ ERROR(attr_only_at_non_generic_scope, none,
|
||||
ERROR(attr_only_on_static_properties, none,
|
||||
"properties with attribute %0 must be static", (DeclAttribute))
|
||||
|
||||
ERROR(weak_unowned_in_embedded_swift, none,
|
||||
"attribute %0 cannot be used in embedded Swift", (ReferenceOwnership))
|
||||
|
||||
ERROR(access_control_in_protocol,none,
|
||||
"%0 modifier cannot be used in protocols", (DeclAttribute))
|
||||
NOTE(access_control_in_protocol_detail,none,
|
||||
@@ -8606,6 +8603,18 @@ ERROR(inlinearray_literal_incorrect_count,none,
|
||||
ERROR(inline_array_type_backwards,none,
|
||||
"element count must precede inline array element type", ())
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: Embedded Swift
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
GROUPED_ERROR(weak_unowned_in_embedded_swift, EmbeddedRestrictions, none,
|
||||
"attribute %0 cannot be used in Embedded Swift",
|
||||
(ReferenceOwnership))
|
||||
|
||||
GROUPED_WARNING(untyped_throws_in_embedded_swift, EmbeddedRestrictions,
|
||||
DefaultIgnore,
|
||||
"untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'", ())
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: @abi Attribute
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -69,6 +69,7 @@ add_swift_host_library(swiftSema STATIC
|
||||
TypeCheckEffects.cpp
|
||||
TypeCheckExpr.cpp
|
||||
TypeCheckExprObjC.cpp
|
||||
TypeCheckEmbedded.cpp
|
||||
TypeCheckGeneric.cpp
|
||||
TypeCheckInvertible.cpp
|
||||
TypeCheckMacros.cpp
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "OpenedExistentials.h"
|
||||
#include "TypeCheckAvailability.h"
|
||||
#include "TypeCheckConcurrency.h"
|
||||
#include "TypeCheckEmbedded.h"
|
||||
#include "TypeCheckMacros.h"
|
||||
#include "TypeCheckType.h"
|
||||
#include "TypeChecker.h"
|
||||
@@ -1456,6 +1457,9 @@ FunctionType::ExtInfo ClosureEffectsRequest::evaluate(
|
||||
bool sendable = expr->getAttrs().hasAttribute<SendableAttr>();
|
||||
|
||||
if (throws || async) {
|
||||
if (expr->getThrowsLoc().isValid() && !expr->getExplicitThrownTypeRepr())
|
||||
diagnoseUntypedThrowsInEmbedded(expr, expr->getThrowsLoc());
|
||||
|
||||
return ASTExtInfoBuilder()
|
||||
.withThrows(throws, /*FIXME:*/Type())
|
||||
.withAsync(async)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "TypeCheckAvailability.h"
|
||||
#include "TypeCheckConcurrency.h"
|
||||
#include "TypeCheckDistributed.h"
|
||||
#include "TypeCheckEmbedded.h"
|
||||
#include "TypeCheckMacros.h"
|
||||
#include "TypeCheckObjC.h"
|
||||
#include "TypeCheckType.h"
|
||||
@@ -5501,12 +5502,14 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type,
|
||||
}
|
||||
|
||||
// Embedded Swift prohibits weak/unowned but allows unowned(unsafe).
|
||||
if (ctx.LangOpts.hasFeature(Feature::Embedded)) {
|
||||
if (auto behavior = shouldDiagnoseEmbeddedLimitations(
|
||||
dc, attr->getLocation(),
|
||||
/*wasAlwaysEmbeddedError=*/true)) {
|
||||
if (ownershipKind == ReferenceOwnership::Weak ||
|
||||
ownershipKind == ReferenceOwnership::Unowned) {
|
||||
Diags.diagnose(attr->getLocation(), diag::weak_unowned_in_embedded_swift,
|
||||
ownershipKind);
|
||||
attr->setInvalid();
|
||||
ownershipKind)
|
||||
.limitBehavior(*behavior);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "TypeCheckAvailability.h"
|
||||
#include "TypeCheckConcurrency.h"
|
||||
#include "TypeCheckDecl.h"
|
||||
#include "TypeCheckEmbedded.h"
|
||||
#include "TypeCheckMacros.h"
|
||||
#include "TypeCheckObjC.h"
|
||||
#include "TypeCheckType.h"
|
||||
@@ -3535,6 +3536,7 @@ public:
|
||||
|
||||
TypeChecker::checkDeclAttributes(FD);
|
||||
TypeChecker::checkDistributedFunc(FD);
|
||||
checkEmbeddedRestrictionsInSignature(FD);
|
||||
|
||||
if (!checkOverrides(FD)) {
|
||||
// If a method has an 'override' keyword but does not
|
||||
@@ -3920,6 +3922,7 @@ public:
|
||||
|
||||
TypeChecker::checkDeclAttributes(CD);
|
||||
TypeChecker::checkParameterList(CD->getParameters(), CD);
|
||||
checkEmbeddedRestrictionsInSignature(CD);
|
||||
|
||||
if (CD->getAsyncLoc().isValid())
|
||||
TypeChecker::checkConcurrencyAvailability(CD->getAsyncLoc(), CD);
|
||||
|
||||
79
lib/Sema/TypeCheckEmbedded.cpp
Normal file
79
lib/Sema/TypeCheckEmbedded.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
//===--- TypeCheckEmbedded.cpp - Embedded ----------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements type checking support for Embedded Swift.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TypeCheckEmbedded.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/DiagnosticsSema.h"
|
||||
#include "swift/AST/Effects.h"
|
||||
#include "swift/AST/Types.h"
|
||||
|
||||
using namespace swift;
|
||||
|
||||
std::optional<DiagnosticBehavior>
|
||||
swift::shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc,
|
||||
bool wasAlwaysEmbeddedError) {
|
||||
// In Embedded Swift, things that were always errors will still be emitted
|
||||
// as errors. Use "unspecified" so we don't change anything.
|
||||
if (dc->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
|
||||
wasAlwaysEmbeddedError) {
|
||||
return DiagnosticBehavior::Unspecified;
|
||||
}
|
||||
|
||||
// Check one of the Embedded restriction diagnostics that is ignored by
|
||||
// default. If it's still ignored, we won't diagnose anything.
|
||||
// limitations.
|
||||
auto &diags = dc->getASTContext().Diags;
|
||||
if (diags.isIgnoredDiagnostic(diag::untyped_throws_in_embedded_swift.ID))
|
||||
return std::nullopt;
|
||||
|
||||
// If this was always an error in Embedded Swift, we aren't in Embedded Swift
|
||||
// now, so downgrade to a warning.
|
||||
if (wasAlwaysEmbeddedError)
|
||||
return DiagnosticBehavior::Warning;
|
||||
|
||||
// Leave it as-is.
|
||||
return DiagnosticBehavior::Unspecified;
|
||||
}
|
||||
|
||||
/// Check embedded restrictions in the signature of the given function.
|
||||
void swift::checkEmbeddedRestrictionsInSignature(
|
||||
const AbstractFunctionDecl *func) {
|
||||
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
|
||||
auto behavior = shouldDiagnoseEmbeddedLimitations(func, func->getLoc());
|
||||
if (!behavior)
|
||||
return;
|
||||
|
||||
// Untyped throws is not permitted.
|
||||
SourceLoc throwsLoc = func->getThrowsLoc();
|
||||
if (throwsLoc.isValid() && !func->getThrownTypeRepr() &&
|
||||
!func->hasPolymorphicEffect(EffectKind::Throws)) {
|
||||
diagnoseUntypedThrowsInEmbedded(func, throwsLoc);
|
||||
}
|
||||
}
|
||||
|
||||
void swift::diagnoseUntypedThrowsInEmbedded(
|
||||
const DeclContext *dc, SourceLoc throwsLoc) {
|
||||
// If we are not supposed to diagnose Embedded Swift limitations, do nothing.
|
||||
auto behavior = shouldDiagnoseEmbeddedLimitations(dc, throwsLoc);
|
||||
if (!behavior)
|
||||
return;
|
||||
|
||||
dc->getASTContext().Diags.diagnose(
|
||||
throwsLoc, diag::untyped_throws_in_embedded_swift)
|
||||
.limitBehavior(*behavior)
|
||||
.fixItInsertAfter(throwsLoc, "(<#thrown error type#>)");
|
||||
}
|
||||
50
lib/Sema/TypeCheckEmbedded.h
Normal file
50
lib/Sema/TypeCheckEmbedded.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//===--- TypeCheckEmbedded.h - Embedded -------------------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides type checking support for Embedded Swift.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_SEMA_TYPECHECKEMBEDDED_H
|
||||
#define SWIFT_SEMA_TYPECHECKEMBEDDED_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace swift {
|
||||
|
||||
class AbstractFunctionDecl;
|
||||
class DeclContext;
|
||||
struct DiagnosticBehavior;
|
||||
class SourceLoc;
|
||||
|
||||
/// Whether we should diagnose language-level limitations of Embedded Swift
|
||||
/// at the given source location, and how.
|
||||
///
|
||||
/// @param dc The declaration context in which the diagnostic would be emitted.
|
||||
/// @param loc The source location at which the diagnostic would be emitted.
|
||||
/// @param wasAlwaysEmbeddedError Whether this diagnostic was always an error
|
||||
/// in Embedded Swift, which is used to avoid downgrading for
|
||||
/// source-compatibility reasons.
|
||||
/// @returns `std::nullopt` if no diagnostic should be emitted. Otherwise, a
|
||||
/// behavior limit to place on the diagnostic when it is emitted.
|
||||
std::optional<DiagnosticBehavior>
|
||||
shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc,
|
||||
bool wasAlwaysEmbeddedError = false);
|
||||
|
||||
/// Check embedded restrictions in the signature of the given function.
|
||||
void checkEmbeddedRestrictionsInSignature(const AbstractFunctionDecl *func);
|
||||
|
||||
/// Diagnose a declaration of typed throws at the given location.
|
||||
void diagnoseUntypedThrowsInEmbedded(const DeclContext *dc, SourceLoc throwsLoc);
|
||||
|
||||
}
|
||||
#endif // SWIFT_SEMA_TYPECHECKEMBEDDED_H
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "NonisolatedNonsendingByDefaultMigration.h"
|
||||
#include "TypeCheckAvailability.h"
|
||||
#include "TypeCheckConcurrency.h"
|
||||
#include "TypeCheckEmbedded.h"
|
||||
#include "TypeCheckInvertible.h"
|
||||
#include "TypeCheckProtocol.h"
|
||||
#include "TypeChecker.h"
|
||||
@@ -4428,6 +4429,8 @@ NeverNullType TypeResolver::resolveASTFunctionType(
|
||||
thrownTy);
|
||||
}
|
||||
}
|
||||
} else if (repr->getThrowsLoc().isValid()) {
|
||||
diagnoseUntypedThrowsInEmbedded(getDeclContext(), repr->getThrowsLoc());
|
||||
}
|
||||
|
||||
bool hasSendingResult =
|
||||
@@ -7011,6 +7014,7 @@ Type ExplicitCaughtTypeRequest::evaluate(
|
||||
|
||||
// Explicit 'throws' implies that this throws 'any Error'.
|
||||
if (closure->getThrowsLoc().isValid()) {
|
||||
diagnoseUntypedThrowsInEmbedded(closure, closure->getThrowsLoc());
|
||||
return ctx.getErrorExistentialType();
|
||||
}
|
||||
|
||||
@@ -7030,6 +7034,8 @@ Type ExplicitCaughtTypeRequest::evaluate(
|
||||
|
||||
// If there is no explicitly-specified thrown error type, it's 'any Error'.
|
||||
if (!typeRepr) {
|
||||
diagnoseUntypedThrowsInEmbedded(doCatch->getDeclContext(),
|
||||
doCatch->getThrowsLoc());
|
||||
return ctx.getErrorExistentialType();
|
||||
}
|
||||
|
||||
|
||||
65
test/embedded/restrictions.swift
Normal file
65
test/embedded/restrictions.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
// RUN: %target-typecheck-verify-swift -Wwarning EmbeddedRestrictions -verify-additional-prefix nonembedded-
|
||||
// RUN: %target-typecheck-verify-swift -Wwarning EmbeddedRestrictions -enable-experimental-feature Embedded -verify-additional-prefix embedded-
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Untyped throws
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enum MyError: Error {
|
||||
case failed
|
||||
}
|
||||
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{28-28=(<#thrown error type#>)}}
|
||||
func untypedThrows() throws { }
|
||||
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{41-41=(<#thrown error type#>)}}
|
||||
func rethrowingFunction(param: () throws -> Void) rethrows { }
|
||||
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{29-29=(<#thrown error type#>)}}
|
||||
typealias FnType = () throws -> Void
|
||||
|
||||
func untypedThrowsInBody() {
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{12-12=(<#thrown error type#>)}}
|
||||
do throws {
|
||||
throw MyError.failed
|
||||
} catch {
|
||||
}
|
||||
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{19-19=(<#thrown error type#>)}}
|
||||
_ = { (x) throws in x + 1 }
|
||||
}
|
||||
|
||||
struct SomeStruct {
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{16-16=(<#thrown error type#>)}}
|
||||
init() throws { }
|
||||
|
||||
var value: Int {
|
||||
// expected-warning@+1{{untyped throws is not available in Embedded Swift; add a thrown error type with '(type)'}}{{15-15=(<#thrown error type#>)}}
|
||||
get throws {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// weak/unowned references
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Note: this have always been an error in Embedded Swift. Make sure they stay
|
||||
// that way, but are emitted as warnings when the restrictions are enabled.
|
||||
|
||||
public class MyClass { }
|
||||
|
||||
public struct MyStruct {
|
||||
var normalVar: MyClass
|
||||
weak var weakVar: MyClass? // expected-nonembedded-warning {{attribute 'weak' cannot be used in Embedded Swift}}
|
||||
// expected-embedded-error@-1 {{attribute 'weak' cannot be used in Embedded Swift}}
|
||||
|
||||
unowned var unownedVar: MyClass // expected-nonembedded-warning {{attribute 'unowned' cannot be used in Embedded Swift}}
|
||||
// expected-embedded-error @-1{{attribute 'unowned' cannot be used in Embedded Swift}}
|
||||
|
||||
unowned(unsafe) var unownedUnsafe: MyClass
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public class MyClass { }
|
||||
|
||||
public struct MyStruct {
|
||||
var normalVar: MyClass
|
||||
weak var weakVar: MyClass? // expected-error {{attribute 'weak' cannot be used in embedded Swift}}
|
||||
unowned var unownedVar: MyClass // expected-error {{attribute 'unowned' cannot be used in embedded Swift}}
|
||||
weak var weakVar: MyClass? // expected-error {{attribute 'weak' cannot be used in Embedded Swift}}
|
||||
unowned var unownedVar: MyClass // expected-error {{attribute 'unowned' cannot be used in Embedded Swift}}
|
||||
unowned(unsafe) var unownedUnsafe: MyClass
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ Or upgrade all warnings except deprecated declaration to errors:
|
||||
- <doc:compilation-caching>
|
||||
- <doc:deprecated-declaration>
|
||||
- <doc:implementation-only-deprecated>
|
||||
- <doc:embedded-restrictions>
|
||||
- <doc:preconcurrency-import>
|
||||
- <doc:clang-declaration-import>
|
||||
- <doc:missing-module-on-known-paths>
|
||||
|
||||
13
userdocs/diagnostics/embedded-restrictions.md
Normal file
13
userdocs/diagnostics/embedded-restrictions.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 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.
|
||||
|
||||
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:
|
||||
|
||||
class Node {
|
||||
weak var parent: Node? // error: attribute 'weak' cannot be used 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