Skip to content
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
16 changes: 10 additions & 6 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1823,9 +1823,14 @@ static bool isDiscardableType(Type type) {
if (auto *expansion = type->getAs<PackExpansionType>())
return isDiscardableType(expansion->getPatternType());

return (type->hasError() ||
type->isUninhabited() ||
type->lookThroughAllOptionalTypes()->isVoid());
if (type->hasError())
return true;

// Look through optionality and check if the type is either `Void` or
// 'structurally uninhabited'. Treat either as discardable.
auto nonOptionalType = type->lookThroughAllOptionalTypes();
return (nonOptionalType->isStructurallyUninhabited() ||
nonOptionalType->isVoid());
}

static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) {
Expand Down Expand Up @@ -1972,9 +1977,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) {
}
}

// If the result of this expression is of type "Never" or "()"
// (the latter potentially wrapped in optionals) then it is
// safe to ignore.
// If the result of this expression is either "structurally uninhabited" or
// `Void`, (potentially wrapped in optionals) then it is safe to ignore.
if (isDiscardableType(valueE->getType()))
return;

Expand Down
73 changes: 73 additions & 0 deletions test/Sema/diag_implicit_discardable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// RUN: %target-typecheck-verify-swift

struct MyError: Error {}
enum MyNever {}

func never_throws() throws -> Never { throw MyError() }
func uninhabited_throws() throws -> (Int, MyNever) { throw MyError () }
func almost_uninhabited_throws() throws -> (Int, Never?) { throw MyError () }
func int_throws() throws -> Int { throw MyError() }
func void_throws() throws {}

func maybe_never() -> Never? { nil }
func maybe_uninhabited() -> (Int, MyNever)? { nil }
func maybe_maybe_uninhabited() -> (Int, Never)?? { nil }
func maybe_void() -> Void? { nil }
func maybe_maybe_void() -> Void?? { nil }
func looks_uninhabited_if_you_squint() -> (Int, Never?)? { nil }

// MARK: - Tests

func test_try() throws {
try never_throws()
try uninhabited_throws()
try almost_uninhabited_throws()
// expected-warning @-1 {{result of call to 'almost_uninhabited_throws()' is unused}}
try int_throws()
// expected-warning @-1 {{result of call to 'int_throws()' is unused}}
try void_throws()
}

func test_force_try() throws {
try! never_throws()
try! uninhabited_throws()
try! almost_uninhabited_throws()
// expected-warning @-1 {{result of call to 'almost_uninhabited_throws()' is unused}}
try! int_throws()
// expected-warning @-1 {{result of call to 'int_throws()' is unused}}
try! void_throws()
}

func test_optional_try() throws {
try? never_throws()
try? uninhabited_throws()
try? almost_uninhabited_throws()
// expected-warning @-1 {{result of 'try?' is unused}}
try? int_throws()
// expected-warning @-1 {{result of 'try?' is unused}}
try? void_throws()
}

func test_implicitly_discardable() {
maybe_never()
maybe_uninhabited()
maybe_maybe_uninhabited()
maybe_void()
maybe_maybe_void()
looks_uninhabited_if_you_squint()
// expected-warning @-1 {{result of call to 'looks_uninhabited_if_you_squint()' is unused}}
}

// https://github.com/swiftlang/swift/issues/85092

func test_85092() {
struct MyError: Error {}

func f() throws -> Never {
throw MyError()
}

func g() {
try? f()
}
}