mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #27381 from CodaFi/raw-smackdown-live
Move some more common enum diagnostics into the decl checker
This commit is contained in:
@@ -1431,6 +1431,41 @@ static LiteralExpr *getAutomaticRawValueExpr(TypeChecker &TC,
|
|||||||
llvm_unreachable("Unhandled AutomaticEnumValueKind in switch.");
|
llvm_unreachable("Unhandled AutomaticEnumValueKind in switch.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Optional<AutomaticEnumValueKind>
|
||||||
|
computeAutomaticEnumValueKind(TypeChecker &TC, EnumDecl *ED) {
|
||||||
|
Type rawTy = ED->getRawType();
|
||||||
|
assert(rawTy && "Cannot compute value kind without raw type!");
|
||||||
|
|
||||||
|
if (ED->getGenericEnvironmentOfContext() != nullptr)
|
||||||
|
rawTy = ED->mapTypeIntoContext(rawTy);
|
||||||
|
|
||||||
|
// Swift enums require that the raw type is convertible from one of the
|
||||||
|
// primitive literal protocols.
|
||||||
|
auto conformsToProtocol = [&](KnownProtocolKind protoKind) {
|
||||||
|
ProtocolDecl *proto = TC.getProtocol(ED->getLoc(), protoKind);
|
||||||
|
return TypeChecker::conformsToProtocol(rawTy, proto,
|
||||||
|
ED->getDeclContext(), None);
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto otherLiteralProtocolKinds = {
|
||||||
|
KnownProtocolKind::ExpressibleByFloatLiteral,
|
||||||
|
KnownProtocolKind::ExpressibleByUnicodeScalarLiteral,
|
||||||
|
KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conformsToProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral)) {
|
||||||
|
return AutomaticEnumValueKind::Integer;
|
||||||
|
} else if (conformsToProtocol(KnownProtocolKind::ExpressibleByStringLiteral)){
|
||||||
|
return AutomaticEnumValueKind::String;
|
||||||
|
} else if (std::any_of(otherLiteralProtocolKinds.begin(),
|
||||||
|
otherLiteralProtocolKinds.end(),
|
||||||
|
conformsToProtocol)) {
|
||||||
|
return AutomaticEnumValueKind::None;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void checkEnumRawValues(TypeChecker &TC, EnumDecl *ED) {
|
static void checkEnumRawValues(TypeChecker &TC, EnumDecl *ED) {
|
||||||
Type rawTy = ED->getRawType();
|
Type rawTy = ED->getRawType();
|
||||||
if (!rawTy) {
|
if (!rawTy) {
|
||||||
@@ -1442,44 +1477,11 @@ static void checkEnumRawValues(TypeChecker &TC, EnumDecl *ED) {
|
|||||||
if (rawTy->hasError())
|
if (rawTy->hasError())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AutomaticEnumValueKind valueKind;
|
// If we don't have a value kind, the decl checker will provide a diagnostic.
|
||||||
// Swift enums require that the raw type is convertible from one of the
|
auto valueKind = computeAutomaticEnumValueKind(TC, ED);
|
||||||
// primitive literal protocols.
|
if (!valueKind.hasValue())
|
||||||
auto conformsToProtocol = [&](KnownProtocolKind protoKind) {
|
|
||||||
ProtocolDecl *proto = TC.getProtocol(ED->getLoc(), protoKind);
|
|
||||||
return TypeChecker::conformsToProtocol(rawTy, proto,
|
|
||||||
ED->getDeclContext(), None);
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto otherLiteralProtocolKinds = {
|
|
||||||
KnownProtocolKind::ExpressibleByFloatLiteral,
|
|
||||||
KnownProtocolKind::ExpressibleByUnicodeScalarLiteral,
|
|
||||||
KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (conformsToProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral)) {
|
|
||||||
valueKind = AutomaticEnumValueKind::Integer;
|
|
||||||
} else if (conformsToProtocol(KnownProtocolKind::ExpressibleByStringLiteral)){
|
|
||||||
valueKind = AutomaticEnumValueKind::String;
|
|
||||||
} else if (std::any_of(otherLiteralProtocolKinds.begin(),
|
|
||||||
otherLiteralProtocolKinds.end(),
|
|
||||||
conformsToProtocol)) {
|
|
||||||
valueKind = AutomaticEnumValueKind::None;
|
|
||||||
} else {
|
|
||||||
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
|
||||||
diag::raw_type_not_literal_convertible,
|
|
||||||
rawTy);
|
|
||||||
ED->getInherited().front().setInvalidType(TC.Context);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// We need at least one case to have a raw value.
|
|
||||||
if (ED->getAllElements().empty()) {
|
|
||||||
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
|
||||||
diag::empty_enum_raw_type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the raw values of the cases.
|
// Check the raw values of the cases.
|
||||||
LiteralExpr *prevValue = nullptr;
|
LiteralExpr *prevValue = nullptr;
|
||||||
EnumElementDecl *lastExplicitValueElt = nullptr;
|
EnumElementDecl *lastExplicitValueElt = nullptr;
|
||||||
@@ -1499,16 +1501,6 @@ static void checkEnumRawValues(TypeChecker &TC, EnumDecl *ED) {
|
|||||||
(void)elt->getInterfaceType();
|
(void)elt->getInterfaceType();
|
||||||
if (elt->isInvalid())
|
if (elt->isInvalid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// We don't yet support raw values on payload cases.
|
|
||||||
if (elt->hasAssociatedValues()) {
|
|
||||||
TC.diagnose(elt->getLoc(),
|
|
||||||
diag::enum_with_raw_type_case_with_argument);
|
|
||||||
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
|
||||||
diag::enum_raw_type_here, rawTy);
|
|
||||||
elt->setInvalid();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elt->hasRawValueExpr()) {
|
if (elt->hasRawValueExpr()) {
|
||||||
lastExplicitValueElt = elt;
|
lastExplicitValueElt = elt;
|
||||||
@@ -1516,7 +1508,7 @@ static void checkEnumRawValues(TypeChecker &TC, EnumDecl *ED) {
|
|||||||
// If the enum element has no explicit raw value, try to
|
// If the enum element has no explicit raw value, try to
|
||||||
// autoincrement from the previous value, or start from zero if this
|
// autoincrement from the previous value, or start from zero if this
|
||||||
// is the first element.
|
// is the first element.
|
||||||
auto nextValue = getAutomaticRawValueExpr(TC, valueKind, elt, prevValue);
|
auto nextValue = getAutomaticRawValueExpr(TC, *valueKind, elt, prevValue);
|
||||||
if (!nextValue) {
|
if (!nextValue) {
|
||||||
elt->setInvalid();
|
elt->setInvalid();
|
||||||
break;
|
break;
|
||||||
@@ -2611,11 +2603,26 @@ public:
|
|||||||
checkAccessControl(TC, ED);
|
checkAccessControl(TC, ED);
|
||||||
|
|
||||||
TC.checkPatternBindingCaptures(ED);
|
TC.checkPatternBindingCaptures(ED);
|
||||||
|
|
||||||
if (ED->hasRawType() && !ED->isObjC()) {
|
if (auto rawTy = ED->getRawType()) {
|
||||||
|
// The raw type must be one of the blessed literal convertible types.
|
||||||
|
if (!computeAutomaticEnumValueKind(TC, ED)) {
|
||||||
|
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
||||||
|
diag::raw_type_not_literal_convertible,
|
||||||
|
rawTy);
|
||||||
|
ED->getInherited().front().setInvalidType(TC.Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need at least one case to have a raw value.
|
||||||
|
if (ED->getAllElements().empty()) {
|
||||||
|
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
||||||
|
diag::empty_enum_raw_type);
|
||||||
|
}
|
||||||
|
|
||||||
// ObjC enums have already had their raw values checked, but pure Swift
|
// ObjC enums have already had their raw values checked, but pure Swift
|
||||||
// enums haven't.
|
// enums haven't.
|
||||||
checkEnumRawValues(TC, ED);
|
if (!ED->isObjC())
|
||||||
|
checkEnumRawValues(TC, ED);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkExplicitAvailability(ED);
|
checkExplicitAvailability(ED);
|
||||||
@@ -3089,6 +3096,7 @@ public:
|
|||||||
|
|
||||||
void visitEnumElementDecl(EnumElementDecl *EED) {
|
void visitEnumElementDecl(EnumElementDecl *EED) {
|
||||||
(void)EED->getInterfaceType();
|
(void)EED->getInterfaceType();
|
||||||
|
auto *ED = EED->getParentEnum();
|
||||||
|
|
||||||
TC.checkDeclAttributes(EED);
|
TC.checkDeclAttributes(EED);
|
||||||
|
|
||||||
@@ -3097,8 +3105,19 @@ public:
|
|||||||
checkDefaultArguments(TC, PL, EED);
|
checkDefaultArguments(TC, PL, EED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't yet support raw values on payload cases.
|
||||||
|
if (EED->hasAssociatedValues()) {
|
||||||
|
if (auto rawTy = ED->getRawType()) {
|
||||||
|
TC.diagnose(EED->getLoc(),
|
||||||
|
diag::enum_with_raw_type_case_with_argument);
|
||||||
|
TC.diagnose(ED->getInherited().front().getSourceRange().Start,
|
||||||
|
diag::enum_raw_type_here, rawTy);
|
||||||
|
EED->setInvalid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Yell if our parent doesn't have a raw type but we have a raw value.
|
// Yell if our parent doesn't have a raw type but we have a raw value.
|
||||||
if (!EED->getParentEnum()->hasRawType() && EED->hasRawValueExpr()) {
|
if (EED->hasRawValueExpr() && !ED->hasRawType()) {
|
||||||
TC.diagnose(EED->getRawValueExpr()->getLoc(),
|
TC.diagnose(EED->getRawValueExpr()->getLoc(),
|
||||||
diag::enum_raw_value_without_raw_type);
|
diag::enum_raw_value_without_raw_type);
|
||||||
EED->setInvalid();
|
EED->setInvalid();
|
||||||
|
|||||||
@@ -1372,6 +1372,11 @@ static bool isEnumObjC(EnumDecl *enumDecl) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need at least one case to have a raw value.
|
||||||
|
if (enumDecl->getAllElements().empty()) {
|
||||||
|
enumDecl->diagnose(diag::empty_enum_raw_type);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,11 +109,11 @@ class ClassWithInheritance9 : FooClass, BarClass, FooProtocol, BarProtocol, FooN
|
|||||||
//===--- Inheritance list in enums.
|
//===--- Inheritance list in enums.
|
||||||
//===---
|
//===---
|
||||||
|
|
||||||
enum EnumWithInheritance1 : FooNonExistentProtocol {} // expected-error {{use of undeclared type 'FooNonExistentProtocol'}}
|
enum EnumWithInheritance1 : FooNonExistentProtocol {} // expected-error {{use of undeclared type 'FooNonExistentProtocol'}} expected-error {{raw type}} expected-error {{an enum with no cases}}
|
||||||
// NO-TYREPR: {{^}}enum EnumWithInheritance1 : <<error type>> {{{$}}
|
// NO-TYREPR: {{^}}enum EnumWithInheritance1 : <<error type>> {{{$}}
|
||||||
// TYREPR: {{^}}enum EnumWithInheritance1 : FooNonExistentProtocol {{{$}}
|
// TYREPR: {{^}}enum EnumWithInheritance1 : FooNonExistentProtocol {{{$}}
|
||||||
|
|
||||||
enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {} // expected-error {{use of undeclared type 'FooNonExistentProtocol'}} expected-error {{use of undeclared type 'BarNonExistentProtocol'}}
|
enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {} // expected-error {{use of undeclared type 'FooNonExistentProtocol'}} expected-error {{use of undeclared type 'BarNonExistentProtocol'}} expected-error {{raw type}} expected-error {{an enum with no cases}}
|
||||||
// NO-TYREPR: {{^}}enum EnumWithInheritance2 : <<error type>>, <<error type>> {{{$}}
|
// NO-TYREPR: {{^}}enum EnumWithInheritance2 : <<error type>>, <<error type>> {{{$}}
|
||||||
// TYREPR: {{^}}enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {{{$}}
|
// TYREPR: {{^}}enum EnumWithInheritance2 : FooNonExistentProtocol, BarNonExistentProtocol {{{$}}
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ extension Container {
|
|||||||
// expected-error@-1 {{raw type 'Container.VeryPrivateStruct' is not expressible by a string, integer, or floating-point literal}}
|
// expected-error@-1 {{raw type 'Container.VeryPrivateStruct' is not expressible by a string, integer, or floating-point literal}}
|
||||||
// expected-error@-2 {{'Container.PrivateRawValue' declares raw type 'Container.VeryPrivateStruct', but does not conform to RawRepresentable and conformance could not be synthesized}}
|
// expected-error@-2 {{'Container.PrivateRawValue' declares raw type 'Container.VeryPrivateStruct', but does not conform to RawRepresentable and conformance could not be synthesized}}
|
||||||
// expected-error@-3 {{RawRepresentable conformance cannot be synthesized because raw type 'Container.VeryPrivateStruct' is not Equatable}}
|
// expected-error@-3 {{RawRepresentable conformance cannot be synthesized because raw type 'Container.VeryPrivateStruct' is not Equatable}}
|
||||||
|
// expected-error@-4 {{an enum with no cases cannot declare a raw type}}
|
||||||
fileprivate enum PrivatePayload {
|
fileprivate enum PrivatePayload {
|
||||||
case A(VeryPrivateStruct) // expected-error {{enum case in an internal enum uses a private type}} {{none}}
|
case A(VeryPrivateStruct) // expected-error {{enum case in an internal enum uses a private type}} {{none}}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,3 +189,4 @@ enum ArrayOfNewEquatable : Array<NotEquatable> { }
|
|||||||
// expected-error@-2{{'ArrayOfNewEquatable' declares raw type 'Array<NotEquatable>', but does not conform to RawRepresentable and conformance could not be synthesized}}
|
// expected-error@-2{{'ArrayOfNewEquatable' declares raw type 'Array<NotEquatable>', but does not conform to RawRepresentable and conformance could not be synthesized}}
|
||||||
// expected-error@-3{{RawRepresentable conformance cannot be synthesized because raw type 'Array<NotEquatable>' is not Equatable}}
|
// expected-error@-3{{RawRepresentable conformance cannot be synthesized because raw type 'Array<NotEquatable>' is not Equatable}}
|
||||||
// expected-note@-4{{do you want to add protocol stubs?}}
|
// expected-note@-4{{do you want to add protocol stubs?}}
|
||||||
|
// expected-error@-5 {{an enum with no cases cannot declare a raw type}}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#elseif NO_CASES
|
#elseif NO_CASES
|
||||||
// NO_CASES: :[[@LINE+1]]:22: error: an enum with no cases cannot declare a raw type
|
// NO_CASES: :[[@LINE+1]]:12: error: an enum with no cases cannot declare a raw type
|
||||||
@objc enum TheEnum : Int32 {
|
@objc enum TheEnum : Int32 {
|
||||||
static var A: TheEnum! { return nil }
|
static var A: TheEnum! { return nil }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ protocol Racoon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SillyRawEnum : SillyProtocol.InnerClass {}
|
enum SillyRawEnum : SillyProtocol.InnerClass {} // expected-error {{an enum with no cases cannot declare a raw type}}
|
||||||
|
// expected-error@-1 {{raw type}}
|
||||||
|
|
||||||
protocol SillyProtocol {
|
protocol SillyProtocol {
|
||||||
class InnerClass<T> {} // expected-error {{type 'InnerClass' cannot be nested in protocol 'SillyProtocol'}}
|
class InnerClass<T> {} // expected-error {{type 'InnerClass' cannot be nested in protocol 'SillyProtocol'}}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ class mixed_redecl3 {} // expected-note {{previously declared here}}
|
|||||||
enum mixed_redecl3 {} // expected-error {{invalid redeclaration}}
|
enum mixed_redecl3 {} // expected-error {{invalid redeclaration}}
|
||||||
// expected-note @-1 2{{found this candidate}}
|
// expected-note @-1 2{{found this candidate}}
|
||||||
enum mixed_redecl3a : mixed_redecl3 {} // expected-error {{'mixed_redecl3' is ambiguous for type lookup in this context}}
|
enum mixed_redecl3a : mixed_redecl3 {} // expected-error {{'mixed_redecl3' is ambiguous for type lookup in this context}}
|
||||||
|
// expected-error@-1 {{an enum with no cases cannot declare a raw type}}
|
||||||
|
// expected-error@-2 {{raw type}}
|
||||||
class mixed_redecl3b : mixed_redecl3 {} // expected-error {{'mixed_redecl3' is ambiguous for type lookup in this context}}
|
class mixed_redecl3b : mixed_redecl3 {} // expected-error {{'mixed_redecl3' is ambiguous for type lookup in this context}}
|
||||||
|
|
||||||
class mixed_redecl4 {} // expected-note {{previously declared here}}
|
class mixed_redecl4 {} // expected-note {{previously declared here}}
|
||||||
|
|||||||
Reference in New Issue
Block a user