Skip to content

[AST][SILOptimizer]: unify missing return diagnostics in some cases #75574

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 1 commit into from
Aug 9, 2024
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
5 changes: 0 additions & 5 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,6 @@ ERROR(enum_case_dot_prefix,none,
ERROR(static_var_decl_global_scope,none,
"%select{%error|static properties|class properties}0 may only be declared on a type",
(StaticSpellingKind))
ERROR(unexpected_curly_braces_in_decl, none,
"unexpected '{' in declaration", ())
ERROR(missing_accessor_return_decl,none,
"missing return in %select{accessor|subscript}0 expected to return %1",
(bool, TypeRepr*))
ERROR(expected_getset_in_protocol,none,
"expected get or set in a protocol property", ())
ERROR(unexpected_getset_implementation_in_protocol,none,
Expand Down
46 changes: 17 additions & 29 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8013,35 +8013,6 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
bool parsingLimitedSyntax = Flags.contains(PD_InProtocol) ||
SF.Kind == SourceFileKind::SIL;

// If the body is completely empty, preserve it. This is at best a getter with
// an implicit fallthrough off the end.
if (peekToken().is(tok::r_brace)) {
accessors.LBLoc = consumeToken(tok::l_brace);
accessors.RBLoc = consumeToken(tok::r_brace);

// In the limited syntax, fall out and let the caller handle it.
if (parsingLimitedSyntax)
return makeParserSuccess();

if (ResultType != nullptr) {
// An error type at this point means we couldn't parse
// the result type for subscript correctly which will be
// already diagnosed as missing result type in declaration.
if (ResultType->getKind() == TypeReprKind::Error)
return makeParserError();

diagnose(accessors.RBLoc, diag::missing_accessor_return_decl,
/*subscript*/ Indices != nullptr, ResultType);
} else {
// This is supposed to be a computed property, but we don't
// have a result type representation which indicates this is probably not
// a well-formed computed property. So we can assume that empty braces
// are unexpected at this position for this declaration.
diagnose(accessors.LBLoc, diag::unexpected_curly_braces_in_decl);
}
return makeParserError();
}

auto parseImplicitGetter = [&]() {
assert(Tok.is(tok::l_brace));
accessors.LBLoc = Tok.getLoc();
Expand All @@ -8055,6 +8026,23 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
accessors.RBLoc = getter->getEndLoc();
};

// If the body is completely empty, preserve it. This is at best a getter with
// an implicit fallthrough off the end.
if (peekToken().is(tok::r_brace)) {
if (parsingLimitedSyntax) {
// In the limited syntax, fall out and let the caller handle it.
accessors.LBLoc = consumeToken(tok::l_brace);
accessors.RBLoc = consumeToken(tok::r_brace);
} else {
// Otherwise, treat the empty braces as a valid implicit getter. We need
// more information to determine whether missing return diagnostics are
// necessary.
parseImplicitGetter();
}

return makeParserSuccess();
}

// Prepare backtracking for implicit getter.
std::optional<CancellableBacktrackingScope> backtrack;
backtrack.emplace(*this);
Expand Down
9 changes: 6 additions & 3 deletions test/Constraints/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,12 @@ do {
}
inSubcall = false

// This is a problem, but isn't clear what was intended.
var somethingElse = true { // expected-error {{unexpected '{' in declaration}}
}
// These are a problems, but it's not clear what was intended.
var somethingElse = true {
// expected-error@-1 {{computed property must have an explicit type}}
// expected-error@-2 {{variable with getter/setter cannot have an initial value}}
}
var somethingElseWithTypeAnno: Bool = true {} // expected-error {{variable with getter/setter cannot have an initial value}}
inSubcall = false

var v2 : Bool = false
Expand Down
8 changes: 4 additions & 4 deletions test/Parse/omit_return.swift
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,10 @@ func ff_implicitMemberAccessEnumCase() -> Unit {


var fv_nop: () {
} // expected-error {{missing return in accessor expected to return '()'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`

var fv_missing: String {
} // expected-error {{missing return in accessor expected to return 'String'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`

var fv_implicit: String {
"hello"
Expand Down Expand Up @@ -1054,12 +1054,12 @@ var fvs_optionalTryImplicit: String? {

enum S_nop {
subscript() -> () {
} // expected-error {{missing return in subscript expected to return '()'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`
}

enum S_missing {
subscript() -> String {
} // expected-error {{missing return in subscript expected to return 'String'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`
}

enum S_implicit {
Expand Down
135 changes: 135 additions & 0 deletions test/SILOptimizer/missing_returns.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// RUN: %target-swift-frontend %s -emit-sil -verify

// MARK: Relocated Test Cases
// Missing return diagnostics used to also be implemented during parsing/AST
// construction in addition to the SIL passes. Some existing test cases have
// been moved here after removing the earlier phases' diagnostics in favor of
// those implemented via the SIL passes.

// MARK: `decl/subscript/subscripting`

struct MissingGetterSubscript1 {
subscript (i : Int) -> Int {
} // expected-error {{missing return in getter expected to return 'Int'}}
}

// MARK: `decl/var/properties`

struct X {}

var x13: X {} // expected-error {{missing return in getter expected to return 'X'}}

struct X14 {}
extension X14 {
var x14: X {
} // expected-error {{missing return in getter expected to return 'X'}}
}

// https://github.com/apple/swift/issues/57936

enum E1_57936 {
var foo: Int {} // expected-error{{missing return in getter expected to return 'Int'}}
}

enum E2_57936<T> {
var foo: T {} // expected-error{{missing return in getter expected to return 'T'}}
}

// MARK: `decl/var/result_builders`

@resultBuilder
struct Maker {
static func buildBlock() -> Int { 42 }
}

@Maker
var globalWithEmptyImplicitGetter: Int {}

// MARK: `Parse/omit_return`

var fv_nop: () {
}

var fv_missing: String {
} // expected-error {{missing return in getter expected to return 'String'}}

enum S_nop {
subscript() -> () {
}
}

enum S_missing {
subscript() -> String {
} // expected-error {{missing return in getter expected to return 'String'}}
}

// MARK: `Sema/generic-subscript`

struct S_generic_subscript_missing_return {
subscript<Value>(x: Int) -> Value {
} // expected-error {{missing return in getter expected to return 'Value'}}
}

// MARK: New Test Cases

enum MyEmptyType {}
extension MyEmptyType {
var i: Int {} // expected-error{{missing return in getter expected to return 'Int'}}
var n: MyEmptyType {} // expected-error{{getter with uninhabited return type 'MyEmptyType' is missing call to another never-returning function on all paths}}

static subscript<A>(root: MyEmptyType) -> A {}

subscript(_ e: MyEmptyType) -> Int {}
subscript<T>(_ e: MyEmptyType) -> T {}
subscript(_ i: Int) -> Int {} // expected-error{{missing return in getter expected to return 'Int'}}
subscript<T>(_ p: Int) -> T {} // expected-error{{missing return in getter expected to return 'T'}}
subscript(_ i: Int) -> Self {} // expected-error{{getter with uninhabited return type 'MyEmptyType' is missing call to another never-returning function on all paths}}
subscript(_ s: Self) -> Self {}

static func unreachable_static_implicit_return(_ e: MyEmptyType) -> Int {}
func unreachable(_ e: MyEmptyType) -> Int { // expected-note{{'e' is of type 'MyEmptyType' which cannot be constructed because it is an enum with no cases}}
42 // expected-warning{{will never be executed}}
}

// FIXME: should these produce warnings since they implicity take an uninhabited 'self' param?
func implicitly_unreachable() { _ = 42 }
func implicitly_unreachable_implicit_return() -> Int { 42 }
}

extension Never {
var i: Int {} // expected-error{{missing return in getter expected to return 'Int'}}
var n: Never {} // expected-error{{getter with uninhabited return type 'Never' is missing call to another never-returning function on all paths}}

static subscript<A>(root: Never) -> A {}

subscript(_ n: Never) -> Int {}
subscript<T>(_ e: Never) -> T {}
subscript(_ i: Int) -> Int {} // expected-error{{missing return in getter expected to return 'Int'}}
subscript<T>(_ p: Int) -> T {} // expected-error{{missing return in getter expected to return 'T'}}
subscript(_ i: Int) -> Self {} // expected-error{{getter with uninhabited return type 'Never' is missing call to another never-returning function on all paths}}
subscript(_ s: Self) -> Self {}

static func unreachable_static_implicit_return(_ n: Never) -> Int {}
func unreachable(_ n: Never) -> Int { // expected-note{{'n' is of type 'Never' which cannot be constructed because it is an enum with no cases}}
42 // expected-warning{{will never be executed}}
}

// FIXME: should these produce unreachable code warnings since they implicity take an uninhabited 'self' param?
func implicitly_unreachable() { _ = 42 }
func implicitly_unreachable_implicit_return() -> Int { 42 }
}

enum InhabitedType {
case inhabitant

// Uninhabited params
subscript(_ n: Never) -> Int {}
subscript<T>(_ e: Never) -> T {}
subscript(_ v: MyEmptyType, e: Int) -> Never {}

// Inhabited params
subscript(_ i: Int) -> Int {} // expected-error{{missing return in getter expected to return 'Int'}}
subscript(_ j: Int) -> Void {}
subscript(_ k: Int) -> Never {} // expected-error{{getter with uninhabited return type 'Never' is missing call to another never-returning function on all paths}}
// FIXME: ^ this diagnostic should probably use the word 'subscript' rather than 'getter'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

noticed this wording change when moving to the SIL-based diagnostics. i have yet to determine how to special case the relevant logic to improve this. let me know if this seems like a blocker.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it's a blocker, it's the same diagnostic we give today for e.g:

struct S {
  subscript(_ x: Int) -> Never {
    let x = 0
  }
}

}
2 changes: 1 addition & 1 deletion test/Sema/generic-subscript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ protocol P {

struct S : P { // expected-error {{type 'S' does not conform to protocol 'P'}}
subscript<Value>(x: Int) -> Value { // expected-note {{candidate has non-matching type '<Value> (Int) -> Value'}}
} // expected-error {{missing return in subscript expected to return 'Value'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`
}

struct S2: P {
Expand Down
2 changes: 1 addition & 1 deletion test/decl/subscript/subscripting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ struct RetOverloadedSubscript {

struct MissingGetterSubscript1 {
subscript (i : Int) -> Int {
} // expected-error {{missing return in subscript expected to return 'Int'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`
}
struct MissingGetterSubscript2 {
subscript (i : Int, j : Int) -> Int {
Expand Down
14 changes: 2 additions & 12 deletions test/decl/var/properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,12 @@ var x12: X {
}
}

var x13: X {} // expected-error {{missing return in accessor expected to return 'X'}}
var x13: X {} // missing return expectations moved to `SILOptimizer/missing_returns`

struct X14 {}
extension X14 {
var x14: X {
} // expected-error {{missing return in accessor expected to return 'X'}}
} // missing return expectations moved to `SILOptimizer/missing_returns`
}

// Type checking problems
Expand Down Expand Up @@ -1343,13 +1343,3 @@ class LazyPropInClass {
lazy var foo: Int = { return 0 } // expected-error {{function produces expected type 'Int'; did you mean to call it with '()'?}}
// expected-note@-1 {{Remove '=' to make 'foo' a computed property}}{{21-23=}}{{3-8=}}
}

// https://github.com/apple/swift/issues/57936

enum E1_57936 {
var foo: Int {} // expected-error{{missing return in accessor expected to return 'Int'}}
}

enum E2_57936<T> {
var foo: T {} // expected-error{{missing return in accessor expected to return 'T'}}
}
5 changes: 2 additions & 3 deletions test/decl/var/result_builders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ typealias typename = Inventor
@Maker // expected-error {{result builder attribute 'Maker' can only be applied to a variable if it defines a getter}}
var global: Int

// FIXME: should this be allowed?
@Maker
var globalWithEmptyImplicitGetter: Int {}
// expected-error@-1 {{missing return in accessor expected to return 'Int'}}
// expected-error@-3 {{result builder attribute 'Maker' can only be applied to a variable if it defines a getter}}
// expected-error@-1{{result builder 'Maker' does not implement any 'buildBlock' or a combination of 'buildPartialBlock(first:)' and 'buildPartialBlock(accumulated:next:)' with sufficient availability for this call site}}
// Note: no missing return error is expected in this case. Similar test added to `SILOptimizer/missing_returns` to verify SIL diagnostic behavior.

@Maker
var globalWithEmptyExplicitGetter: Int { get {} } // expected-error{{result builder 'Maker' does not implement any 'buildBlock' or a combination of 'buildPartialBlock(first:)' and 'buildPartialBlock(accumulated:next:)' with sufficient availability for this call site}}
Expand Down