Skip to content

TypeCheckType: Rework IUO diagnostics using behavior limitation #78739

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
Jan 22, 2025
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
14 changes: 6 additions & 8 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6145,14 +6145,12 @@ ERROR(closure_unlabeled_parameter_following_variadic_parameter,none,
ERROR(parameter_vararg_default,none,
"variadic parameter cannot have a default value", ())

WARNING(implicitly_unwrapped_optional_in_illegal_position_interpreted_as_optional,none,
"using '!' is not allowed here; treating this as '?' instead", ())

WARNING(implicitly_unwrapped_optional_deprecated_in_this_position,Deprecation,
"using '!' here is deprecated and will be removed in a future release", ())

ERROR(implicitly_unwrapped_optional_in_illegal_position,none,
"using '!' is not allowed here; perhaps '?' was intended?", ())
ERROR(iuo_invalid_here, none,
"using '!' is not allowed here", ())
ERROR(iuo_deprecated_here,Deprecation,
"using '!' here is deprecated", ())
NOTE(iuo_use_optional_instead,none,
"use '?' instead", ())

// Ownership
ERROR(invalid_ownership_type,none,
Expand Down
56 changes: 32 additions & 24 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5405,32 +5405,40 @@ NeverNullType TypeResolver::resolveImplicitlyUnwrappedOptionalType(
}

if (doDiag && !options.contains(TypeResolutionFlags::SilenceErrors)) {
// Prior to Swift 5, we allow 'as T!' and turn it into a disjunction.
if (ctx.isSwiftVersionAtLeast(5)) {
// Mark this repr as invalid. This is the only way to indicate that
// something went wrong without supressing checking other reprs in
// the same type. For example:
//
// struct S<T, U> { ... }
//
// _ = S<Int!, String!>(...)
//
// Compiler should diagnose both `Int!` and `String!` as invalid,
// but returning `ErrorType` from here would stop type resolution
// after `Int!`.
// In language modes up to Swift 5, we allow `T!` in invalid position for
// compatibility and downgrade the error to a warning.
const unsigned swiftLangModeForError = 5;

// If we are about to error, mark this node as invalid.
// This is the only way to indicate that something went wrong without
// supressing checking of sibling nodes.
// For example:
//
// struct S<T, U> { ... }
//
// _ = S<Int!, String!>(...)
//
// Compiler should diagnose both `Int!` and `String!` as invalid,
// but returning `ErrorType` from here would stop type resolution
// after `Int!`.
if (ctx.isSwiftVersionAtLeast(swiftLangModeForError)) {
repr->setInvalid();
}

diagnose(repr->getStartLoc(),
diag::implicitly_unwrapped_optional_in_illegal_position)
.fixItReplace(repr->getExclamationLoc(), "?");
} else if (options.is(TypeResolverContext::ExplicitCastExpr)) {
diagnose(
repr->getStartLoc(),
diag::implicitly_unwrapped_optional_deprecated_in_this_position);
} else {
diagnose(
repr->getStartLoc(),
diag::implicitly_unwrapped_optional_in_illegal_position_interpreted_as_optional)
Diag<> diagID = diag::iuo_deprecated_here;
if (ctx.isSwiftVersionAtLeast(swiftLangModeForError)) {
diagID = diag::iuo_invalid_here;
}

diagnose(repr->getExclamationLoc(), diagID)
.warnUntilSwiftVersion(swiftLangModeForError);

// Suggest a regular optional, but not when `T!` is the right-hand side of
// a cast expression.
// In this case, the user is likely trying to cast an expression of optional
// type, and this fix-it is not generally helpful.
if (!options.is(TypeResolverContext::ExplicitCastExpr)) {
diagnose(repr->getExclamationLoc(), diag::iuo_use_optional_instead)
.fixItReplace(repr->getExclamationLoc(), "?");
}
}
Expand Down
37 changes: 0 additions & 37 deletions test/Constraints/iuo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,43 +179,6 @@ class rdar37241550 {
}
}

class B {}
class D : B {
var i: Int!
}

func coerceToIUO(d: D?) -> B {
return d as B! // expected-warning {{using '!' here is deprecated and will be removed in a future release}}
}

func forcedDowncastToOptional(b: B?) -> D? {
return b as! D! // expected-warning {{using '!' here is deprecated and will be removed in a future release}}
}

func forcedDowncastToObject(b: B?) -> D {
return b as! D! // expected-warning {{using '!' here is deprecated and will be removed in a future release}}
}

func forcedDowncastToObjectIUOMember(b: B?) -> Int {
return (b as! D!).i // expected-warning {{using '!' here is deprecated and will be removed in a future release}}
}

func forcedUnwrapViaForcedCast(b: B?) -> B {
return b as! B! // expected-warning {{forced cast from 'B?' to 'B' only unwraps optionals; did you mean to use '!'?}}
// expected-warning@-1 {{using '!' here is deprecated and will be removed in a future release}}
}

func conditionalDowncastToOptional(b: B?) -> D? {
return b as? D! // expected-warning {{using '!' here is deprecated and will be removed in a future release}}
}

func conditionalDowncastToObject(b: B?) -> D {
return b as? D! // expected-error {{value of optional type 'D?' must be unwrapped to a value of type 'D'}}
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
// expected-warning@-3 {{using '!' here is deprecated and will be removed in a future release}}
}

// https://github.com/apple/swift/issues/49536
// Ensure that we select the overload that does *not* involve forcing an IUO.
do {
Expand Down
Loading