diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 658b791e59f64..e1c4344f295d8 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1823,9 +1823,14 @@ static bool isDiscardableType(Type type) { if (auto *expansion = type->getAs()) 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) { @@ -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; diff --git a/test/Sema/diag_implicit_discardable.swift b/test/Sema/diag_implicit_discardable.swift new file mode 100644 index 0000000000000..27b272c5d6aa1 --- /dev/null +++ b/test/Sema/diag_implicit_discardable.swift @@ -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() + } +}