Skip to content

Commit

Permalink
Merge pull request #13680 from rudkx/map-signature-looking-through-inout
Browse files Browse the repository at this point in the history
Sema: Look through inout when mapping IUOs to Optionals.
  • Loading branch information
rudkx committed Jan 3, 2018
2 parents 1ebc8e9 + e5a0c96 commit 1385964
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
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 @@ -1703,8 +1703,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 @@ -1746,16 +1753,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 @@ -982,6 +982,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

0 comments on commit 1385964

Please sign in to comment.