Skip to content

[Sema] Disallow stored properties to have uninhabited types #19992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,15 @@ ERROR(pattern_binds_no_variables,none,
"variables",
(unsigned))

ERROR(pattern_no_uninhabited_type,none,
"%select{%select{variable|constant}0|stored property}1 %2 cannot have "
"enum type %3 with no cases",
(bool, bool, Identifier, Type))
ERROR(pattern_no_uninhabited_tuple_type,none,
"%select{%select{variable|constant}0|stored property}1 %2 cannot have "
"tuple type %3 containing enum with no cases",
(bool, bool, Identifier, Type))

ERROR(nscoding_unstable_mangled_name,none,
"%select{private|fileprivate|nested|local}0 class %1 has an "
"unstable name when archiving via 'NSCoding'",
Expand Down
19 changes: 19 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2461,6 +2461,25 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}
}

// Reject variable if it is a stored property with an uninhabited type
if (VD->hasStorage() &&
VD->getInterfaceType()->isStructurallyUninhabited()) {
auto uninhabitedTypeDiag = diag::pattern_no_uninhabited_type;

if (VD->getInterfaceType()->is<TupleType>()) {
uninhabitedTypeDiag = diag::pattern_no_uninhabited_tuple_type;
} else {
assert((VD->getInterfaceType()->is<EnumType>() ||
VD->getInterfaceType()->is<BoundGenericEnumType>()) &&
"unknown structurally uninhabited type");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an assertion that the type is an enum otherwise? That way we'll know if we ever add any new ways for something to be structurally uninhabited.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh. It could be a BoundGenericEnumType too…

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops 😅


TC.diagnose(VD->getLoc(), uninhabitedTypeDiag, VD->isLet(),
VD->isInstanceMember(), VD->getName(),
VD->getInterfaceType());
VD->markInvalid();
}

if (!checkOverrides(VD)) {
// If a property has an override attribute but does not override
// anything, complain.
Expand Down
1 change: 1 addition & 0 deletions test/IRGen/generic_enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ struct Bar<A1, A2> {
}

enum Foo<A>{
case bar
}
8 changes: 7 additions & 1 deletion test/Reflection/Inputs/TypeLowering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,14 @@ public struct MetatypeStruct {
public let abstractMetatype: MetadataHolder<BasicStruct.Type, BasicStruct>
}

// We don't allow stored properties to have uninhabited types now, but make a
// wrapper over one to continue testing this
public enum EmptyEnum {}

public struct EmptyEnumWrapper<T> {
public var value: T
}

public enum NoPayloadEnum {
case A
case B
Expand Down Expand Up @@ -194,7 +200,7 @@ public enum MultiPayloadGenericDynamic<T, U> {
}

public struct EnumStruct {
public let empty: EmptyEnum
public let empty: EmptyEnumWrapper<EmptyEnum>
public let noPayload: NoPayloadEnum
public let sillyNoPayload: SillyNoPayloadEnum
public let singleton: SingletonEnum
Expand Down
4 changes: 3 additions & 1 deletion test/Reflection/typeref_lowering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,9 @@
// CHECK-64: (struct TypeLowering.EnumStruct)
// CHECK-64-NEXT: (struct size=81 alignment=8 stride=88 num_extra_inhabitants=[[PTR_XI]]
// CHECK-64-NEXT: (field name=empty offset=0
// CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0
// CHECK-64-NEXT: (field name=value offset=0
// CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0))))
// CHECK-64-NEXT: (field name=noPayload offset=0
// CHECK-64-NEXT: (no_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (field name=sillyNoPayload offset=1
Expand Down
3 changes: 2 additions & 1 deletion test/attr/attr_noreturn.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func noReturn3(_: Int)
// expected-error@-2 {{'@noreturn' has been removed; functions that never return should have a return type of 'Never' instead}}{{1-11=}}{{53-56=Never}}

// Test that error recovery gives us the 'Never' return type
let x: Never = noReturn1(0) // No error
let x: Never = noReturn1(0)
// expected-error@-1 {{constant 'x' cannot have enum type 'Never' with no cases}}

// @noreturn in function type declarations
let valueNoReturn: @noreturn () -> ()
Expand Down
4 changes: 3 additions & 1 deletion test/attr/attr_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -941,11 +941,12 @@ class infer_instanceVar1 {
// expected-note@-2 {{Swift structs cannot be represented in Objective-C}}

var var_PlainEnum: PlainEnum
// CHECK-LABEL: {{^}} var var_PlainEnum: PlainEnum
// expected-error@-1 {{stored property 'var_PlainEnum' cannot have enum type 'PlainEnum' with no cases}}

@objc var var_PlainEnum_: PlainEnum
// expected-error@-1 {{property cannot be marked @objc because its type cannot be represented in Objective-C}}
// expected-note@-2 {{non-'@objc' enums cannot be represented in Objective-C}}
// expected-error@-3 {{stored property 'var_PlainEnum_' cannot have enum type 'PlainEnum' with no cases}}

var var_PlainProtocol: PlainProtocol
// CHECK-LABEL: {{^}} var var_PlainProtocol: PlainProtocol
Expand Down Expand Up @@ -1271,6 +1272,7 @@ class infer_instanceVar1 {
// expected-error@-1 {{'unowned' may only be applied to class and class-bound protocol types, not 'PlainStruct'}}
unowned var var_Unowned_bad3: PlainEnum
// expected-error@-1 {{'unowned' may only be applied to class and class-bound protocol types, not 'PlainEnum'}}
// expected-error@-2 {{stored property 'var_Unowned_bad3' cannot have enum type 'PlainEnum' with no cases}}
unowned var var_Unowned_bad4: String
// expected-error@-1 {{'unowned' may only be applied to class and class-bound protocol types, not 'String'}}
// CHECK-NOT: @objc{{.*}}Unowned_fail
Expand Down
22 changes: 22 additions & 0 deletions test/decl/var/properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1267,3 +1267,25 @@ class WeakFixItTest {
// expected-error @+1 {{'weak' variable should have optional type '(WFI_P1 & WFI_P2)?'}} {{18-18=(}} {{33-33=)?}}
weak var bar : WFI_P1 & WFI_P2
}

// SR-8811
// Stored properties cannot have uninhabited types

struct SR8811 {
var x: Never // expected-error {{stored property 'x' cannot have enum type 'Never' with no cases}}

var y: (Int, Never, Bool) // expected-error {{stored property 'y' cannot have tuple type '(Int, Never, Bool)' containing enum with no cases}}
}

let sr8811x: Never // expected-error {{constant 'sr8811x' cannot have enum type 'Never' with no cases}}

var sr8811y: (Int, Never) // expected-error {{variable 'sr8811y' cannot have tuple type '(Int, Never)' containing enum with no cases}}

// Ok
var sr8811z: Never {
return fatalError()
}

enum SR8811EmptyGenericEnum<A> {}

let sr8811z: SR8811EmptyGenericEnum<Int> // expected-error {{constant 'sr8811z' cannot have enum type 'SR8811EmptyGenericEnum<Int>' with no cases}}
4 changes: 2 additions & 2 deletions test/expr/expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -584,9 +584,9 @@ func conversionTest(_ a: inout Double, b: inout Int) {
var pi_f3 = float.init(getPi()) // expected-error {{ambiguous use of 'init(_:)'}}
var pi_f4 = float.init(pi_f)

var e = Empty(f)
var e = Empty(f) // expected-error {{variable 'e' cannot have enum type 'Empty' with no cases}}
var e2 = Empty(d) // expected-error{{cannot convert value of type 'Double' to expected argument type 'Float'}}
var e3 = Empty(Float(d))
var e3 = Empty(Float(d)) // expected-error {{variable 'e3' cannot have enum type 'Empty' with no cases}}
}

struct Rule { // expected-note {{'init(target:dependencies:)' declared here}}
Expand Down