Skip to content

Commit 0a54806

Browse files
authored
Merge pull request #9147 from CodaFi/mutatis-mutandis
Refine parameter type 'inout' diagnostics
2 parents 4a12ab5 + 1020954 commit 0a54806

File tree

7 files changed

+71
-22
lines changed

7 files changed

+71
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2089,7 +2089,8 @@ ERROR(attr_only_on_parameters,none,
20892089
"%0 may only be used on parameters", (StringRef))
20902090
ERROR(attr_not_on_variadic_parameters,none,
20912091
"%0 may not be used on variadic parameters", (StringRef))
2092-
2092+
ERROR(attr_not_on_subscript_parameters,none,
2093+
"%0 may not be used on subscript parameters", (StringRef))
20932094

20942095
ERROR(override_final,none,
20952096
"%0 overrides a 'final' %1", (DescriptiveDeclKind, DescriptiveDeclKind))

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4184,7 +4184,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
41844184
TypeResolutionOptions(),
41854185
&resolver);
41864186
isInvalid |= TC.typeCheckParameterList(SD->getIndices(), SD,
4187-
TypeResolutionOptions(),
4187+
TR_SubscriptParameters,
41884188
resolver);
41894189

41904190
if (isInvalid || SD->isInvalid()) {

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ static bool checkGenericSubscriptSignature(TypeChecker &tc,
990990
auto params = subscript->getIndices();
991991

992992
badType |= tc.typeCheckParameterList(params, subscript,
993-
TypeResolutionOptions(),
993+
TR_SubscriptParameters,
994994
resolver);
995995

996996
// Infer requirements from the pattern.

lib/Sema/TypeCheckPattern.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -766,17 +766,22 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
766766
}
767767
decl->getTypeLoc().setType(Ty);
768768
}
769-
// If the param is not a 'let' and it is not an 'inout'.
770-
// It must be a 'var'. Provide helpful diagnostics like a shadow copy
771-
// in the function body to fix the 'var' attribute.
772-
if (!decl->isLet() &&
773-
!decl->isImplicit() &&
774-
(Ty.isNull() || !Ty->is<InOutType>()) &&
775-
!hadError) {
776-
auto func = dyn_cast_or_null<AbstractFunctionDecl>(DC);
777-
diagnoseAndMigrateVarParameterToBody(decl, func, TC);
778-
decl->setInvalid();
779-
hadError = true;
769+
770+
// If the user did not explicitly write 'let', 'var', or 'inout', we'll let
771+
// type inference figure out what went wrong in detail.
772+
if (decl->getLetVarInOutLoc().isValid()) {
773+
// If the param is not a 'let' and it is not an 'inout'.
774+
// It must be a 'var'. Provide helpful diagnostics like a shadow copy
775+
// in the function body to fix the 'var' attribute.
776+
if (!decl->isLet() &&
777+
!decl->isImplicit() &&
778+
(Ty.isNull() || !Ty->is<InOutType>()) &&
779+
!hadError) {
780+
auto func = dyn_cast_or_null<AbstractFunctionDecl>(DC);
781+
diagnoseAndMigrateVarParameterToBody(decl, func, TC);
782+
decl->setInvalid();
783+
hadError = true;
784+
}
780785
}
781786

782787
if (hadError)

lib/Sema/TypeCheckType.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,14 +2786,19 @@ bool TypeResolver::resolveSILResults(TypeRepr *repr,
27862786

27872787
Type TypeResolver::resolveInOutType(InOutTypeRepr *repr,
27882788
TypeResolutionOptions options) {
2789-
// inout is only valid for function parameters.
2790-
if (!(options & TR_FunctionInput) &&
2791-
!(options & TR_ImmediateFunctionInput)) {
2792-
TC.diagnose(repr->getInOutLoc(),
2793-
(options & TR_VariadicFunctionInput)
2794-
? diag::attr_not_on_variadic_parameters
2795-
: diag::attr_only_on_parameters,
2796-
"'inout'");
2789+
// inout is only valid for (non-subscript) function parameters.
2790+
if ((options & TR_SubscriptParameters) ||
2791+
(!(options & TR_FunctionInput) &&
2792+
!(options & TR_ImmediateFunctionInput))) {
2793+
decltype(diag::attr_only_on_parameters) diagID;
2794+
if (options & TR_SubscriptParameters) {
2795+
diagID = diag::attr_not_on_subscript_parameters;
2796+
} else if (options & TR_VariadicFunctionInput) {
2797+
diagID = diag::attr_not_on_variadic_parameters;
2798+
} else {
2799+
diagID = diag::attr_only_on_parameters;
2800+
}
2801+
TC.diagnose(repr->getInOutLoc(), diagID, "'inout'");
27972802
repr->setInvalid();
27982803
return ErrorType::get(Context);
27992804
}

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,9 @@ enum TypeResolutionFlags : unsigned {
493493

494494
/// Whether we are checking the underlying type of a typealias.
495495
TR_TypeAliasUnderlyingType = 0x4000000,
496+
497+
/// Whether we are checking the parameter list of a subscript.
498+
TR_SubscriptParameters = 0x8000000,
496499
};
497500

498501
/// Option set describing how type resolution should work.

test/Sema/immutability.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,38 @@ func f(a : FooClass, b : LetStructMembers) {
573573
a.baz = 1 // expected-error {{cannot assign to property: 'baz' is a method}}
574574
b.f = 42 // expected-error {{cannot assign to property: 'f' is a method}}
575575
}
576+
577+
// SR-2354: Reject subscript declarations with mutable parameters.
578+
class MutableSubscripts {
579+
var x : Int = 0
580+
581+
subscript(x: inout Int) -> () { x += 1 } // expected-error {{'inout' may not be used on subscript parameters}}
582+
subscript<T>(x: inout T) -> () { // expected-error {{'inout' may not be used on subscript parameters}}
583+
fatalError()
584+
}
585+
586+
static func initialize(from state: inout MutableSubscripts) -> MutableSubscripts {
587+
return state
588+
}
589+
}
590+
591+
592+
// SR-4214: Misleading location-less diagnostic when closure parameter type
593+
// is inferred to be inout.
594+
func sr4214() {
595+
func sequence<T>(_ x : T, _ f : (T) -> T) -> T {
596+
return f(x)
597+
}
598+
599+
// expected-error@+1 {{expression type '(inout MutableSubscripts) -> ()' is ambiguous without more context}}
600+
let closure = { val in val.x = 7 } as (inout MutableSubscripts) -> ()
601+
var v = MutableSubscripts()
602+
closure(&v)
603+
// FIXME: This diagnostic isn't really all that much better
604+
// expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(_) -> _'}}
605+
sequence(v) { (state : inout MutableSubscripts) -> () in
606+
_ = MutableSubscripts.initialize(from: &state)
607+
return ()
608+
}
609+
}
610+

0 commit comments

Comments
 (0)