Skip to content

Sema: Look through inout when mapping IUOs to Optionals. #13680

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 3 commits into from
Jan 3, 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
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,12 @@ ERROR(invalid_redecl,none,"invalid redeclaration of %0", (DeclName))
NOTE(invalid_redecl_prev,none,
"%0 previously declared here", (DeclName))

WARNING(deprecated_redecl_by_optionality, none,
"invalid redeclaration of %0 which differs only by the kind of optional passed as an inout argument (%1 vs. %2)",
(DeclName, Type, Type))
NOTE(deprecated_redecl_by_optionality_note, none,
"overloading by kind of optional is deprecated and will be removed in a future release", ())

ERROR(ambiguous_type_base,none,
"%0 is ambiguous for type lookup in this context", (Identifier))
ERROR(invalid_member_type,none,
Expand Down
26 changes: 17 additions & 9 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1696,8 +1696,15 @@ static Type mapSignatureType(ASTContext &ctx, Type type) {

/// Map a signature type for a parameter.
static Type mapSignatureParamType(ASTContext &ctx, Type type) {
/// Translate implicitly unwrapped optionals into strict optionals.
if (auto uncheckedOptOf = type->getImplicitlyUnwrappedOptionalObjectType()) {
// Translate implicitly unwrapped optionals into strict optionals.
if (auto inOutTy = type->getAs<InOutType>()) {
if (auto uncheckedOptOf =
inOutTy->getObjectType()
->getImplicitlyUnwrappedOptionalObjectType()) {
type = InOutType::get(OptionalType::get(uncheckedOptOf));
}
} else if (auto uncheckedOptOf =
type->getImplicitlyUnwrappedOptionalObjectType()) {
type = OptionalType::get(uncheckedOptOf);
}

Expand Down Expand Up @@ -1739,16 +1746,17 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type,
if (curryLevels == 0) {
// In an initializer, ignore optionality.
if (isInitializer) {
if (auto objectType = type->getAnyOptionalObjectType())
if (auto inOutTy = type->getAs<InOutType>()) {
if (auto objectType =
inOutTy->getObjectType()->getAnyOptionalObjectType()) {
type = InOutType::get(objectType);
}
} else if (auto objectType = type->getAnyOptionalObjectType()) {
type = objectType;
}
}

// Translate implicitly unwrapped optionals into strict optionals.
if (auto uncheckedOptOf = type->getImplicitlyUnwrappedOptionalObjectType()) {
type = OptionalType::get(uncheckedOptOf);
}

return mapSignatureType(ctx, type);
return mapSignatureParamType(ctx, type);
}

auto funcTy = type->castTo<AnyFunctionType>();
Expand Down
57 changes: 57 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,63 @@ static void checkRedeclaration(TypeChecker &tc, ValueDecl *current) {
continue;
}

// Signatures are the same, but interface types are not. We must
// have a type that we've massaged as part of signature
// interface type generation. If it's a result of remapping a
// function parameter from 'inout T!' to 'inout T?', emit a
// warning that these overloads are deprecated and will no
// longer be supported in the future.
if (!current->getInterfaceType()->isEqual(other->getInterfaceType())) {
if (currentDC->isTypeContext() == other->getDeclContext()->isTypeContext()) {
auto currFnTy = current->getInterfaceType()->getAs<AnyFunctionType>();
auto otherFnTy = other->getInterfaceType()->getAs<AnyFunctionType>();
if (currFnTy && otherFnTy && currentDC->isTypeContext()) {
currFnTy = currFnTy->getResult()->getAs<AnyFunctionType>();
otherFnTy = otherFnTy->getResult()->getAs<AnyFunctionType>();
}

if (currFnTy && otherFnTy) {
ArrayRef<AnyFunctionType::Param> currParams = currFnTy->getParams();
ArrayRef<AnyFunctionType::Param> otherParams = otherFnTy->getParams();

if (currParams.size() == otherParams.size()) {
auto diagnosed = false;
for (unsigned i : indices(currParams)) {
if (currParams[i].isInOut() && otherParams[i].isInOut()) {
auto currParamTy = currParams[i]
.getType()
->getAs<InOutType>()
->getObjectType();
auto otherParamTy = otherParams[i]
.getType()
->getAs<InOutType>()
->getObjectType();
OptionalTypeKind currOTK;
OptionalTypeKind otherOTK;
(void)currParamTy->getAnyOptionalObjectType(currOTK);
(void)otherParamTy->getAnyOptionalObjectType(otherOTK);
if (currOTK != OTK_None && otherOTK != OTK_None &&
currOTK != otherOTK) {
tc.diagnose(current, diag::deprecated_redecl_by_optionality,
current->getFullName(), currParamTy,
otherParamTy);
tc.diagnose(other, diag::invalid_redecl_prev,
other->getFullName());
tc.diagnose(current,
diag::deprecated_redecl_by_optionality_note);
diagnosed = true;
break;
}
}
}

if (diagnosed)
break;
}
}
}
}

// If the conflicting declarations have non-overlapping availability and,
// we allow the redeclaration to proceed if...
//
Expand Down
23 changes: 23 additions & 0 deletions test/decl/overload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,29 @@ func inout2(x: inout Int) { }
func optional(x: Int?) { } // expected-note{{previously declared}}
func optional(x: Int!) { } // expected-error{{invalid redeclaration of 'optional(x:)'}}

func optionalInOut(x: inout Int?) { } // expected-note{{previously declared}}
// expected-note@-1 {{previously declared}}
func optionalInOut(x: inout Int!) { } // expected-warning{{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-1 {{overloading by kind of optional is deprecated and will be removed in a future release}}
// expected-warning@-2 {{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-3 {{overloading by kind of optional is deprecated and will be removed in a future release}}

class optionalOverloads {
class func optionalInOut(x: inout Int?) { } // expected-note{{previously declared}}
// expected-note@-1 {{previously declared}}
class func optionalInOut(x: inout Int!) { } // expected-warning{{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-1 {{overloading by kind of optional is deprecated and will be removed in a future release}}
// expected-warning@-2 {{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-3 {{overloading by kind of optional is deprecated and will be removed in a future release}}

func optionalInOut(x: inout Int?) { } // expected-note{{previously declared}}
// expected-note@-1 {{previously declared}}
func optionalInOut(x: inout Int!) { } // expected-warning{{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-1 {{overloading by kind of optional is deprecated and will be removed in a future release}}
// expected-warning@-2 {{invalid redeclaration of 'optionalInOut(x:)' which differs only by the kind of optional passed as an inout argument ('Int!' vs. 'Int?')}}
// expected-note@-3 {{overloading by kind of optional is deprecated and will be removed in a future release}}
}

func optional_3() -> Int? { } // expected-note{{previously declared}}
func optional_3() -> Int! { } // expected-error{{invalid redeclaration of 'optional_3()'}}

Expand Down