Skip to content

Refine parameter type 'inout' diagnostics #9147

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
May 5, 2017
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
3 changes: 2 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2089,7 +2089,8 @@ ERROR(attr_only_on_parameters,none,
"%0 may only be used on parameters", (StringRef))
ERROR(attr_not_on_variadic_parameters,none,
"%0 may not be used on variadic parameters", (StringRef))

ERROR(attr_not_on_subscript_parameters,none,
"%0 may not be used on subscript parameters", (StringRef))

ERROR(override_final,none,
"%0 overrides a 'final' %1", (DescriptiveDeclKind, DescriptiveDeclKind))
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4184,7 +4184,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
TypeResolutionOptions(),
&resolver);
isInvalid |= TC.typeCheckParameterList(SD->getIndices(), SD,
TypeResolutionOptions(),
TR_SubscriptParameters,
resolver);

if (isInvalid || SD->isInvalid()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,7 @@ static bool checkGenericSubscriptSignature(TypeChecker &tc,
auto params = subscript->getIndices();

badType |= tc.typeCheckParameterList(params, subscript,
TypeResolutionOptions(),
TR_SubscriptParameters,
resolver);

// Infer requirements from the pattern.
Expand Down
27 changes: 16 additions & 11 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,17 +766,22 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
}
decl->getTypeLoc().setType(Ty);
}
// If the param is not a 'let' and it is not an 'inout'.
// It must be a 'var'. Provide helpful diagnostics like a shadow copy
// in the function body to fix the 'var' attribute.
if (!decl->isLet() &&
!decl->isImplicit() &&
(Ty.isNull() || !Ty->is<InOutType>()) &&
!hadError) {
auto func = dyn_cast_or_null<AbstractFunctionDecl>(DC);
diagnoseAndMigrateVarParameterToBody(decl, func, TC);
decl->setInvalid();
hadError = true;

// If the user did not explicitly write 'let', 'var', or 'inout', we'll let
// type inference figure out what went wrong in detail.
if (decl->getLetVarInOutLoc().isValid()) {
// If the param is not a 'let' and it is not an 'inout'.
// It must be a 'var'. Provide helpful diagnostics like a shadow copy
// in the function body to fix the 'var' attribute.
if (!decl->isLet() &&
!decl->isImplicit() &&
(Ty.isNull() || !Ty->is<InOutType>()) &&
!hadError) {
auto func = dyn_cast_or_null<AbstractFunctionDecl>(DC);
diagnoseAndMigrateVarParameterToBody(decl, func, TC);
decl->setInvalid();
hadError = true;
}
}

if (hadError)
Expand Down
21 changes: 13 additions & 8 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2783,14 +2783,19 @@ bool TypeResolver::resolveSILResults(TypeRepr *repr,

Type TypeResolver::resolveInOutType(InOutTypeRepr *repr,
TypeResolutionOptions options) {
// inout is only valid for function parameters.
if (!(options & TR_FunctionInput) &&
!(options & TR_ImmediateFunctionInput)) {
TC.diagnose(repr->getInOutLoc(),
(options & TR_VariadicFunctionInput)
? diag::attr_not_on_variadic_parameters
: diag::attr_only_on_parameters,
"'inout'");
// inout is only valid for (non-subscript) function parameters.
if ((options & TR_SubscriptParameters) ||
(!(options & TR_FunctionInput) &&
!(options & TR_ImmediateFunctionInput))) {
decltype(diag::attr_only_on_parameters) diagID;
if (options & TR_SubscriptParameters) {
diagID = diag::attr_not_on_subscript_parameters;
} else if (options & TR_VariadicFunctionInput) {
diagID = diag::attr_not_on_variadic_parameters;
} else {
diagID = diag::attr_only_on_parameters;
}
TC.diagnose(repr->getInOutLoc(), diagID, "'inout'");
repr->setInvalid();
return ErrorType::get(Context);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,9 @@ enum TypeResolutionFlags : unsigned {

/// Whether we are checking the underlying type of a typealias.
TR_TypeAliasUnderlyingType = 0x4000000,

/// Whether we are checking the parameter list of a subscript.
TR_SubscriptParameters = 0x8000000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use this to resolve property types also, and remove the non-materializable check there.

};

/// Option set describing how type resolution should work.
Expand Down
35 changes: 35 additions & 0 deletions test/Sema/immutability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -573,3 +573,38 @@ func f(a : FooClass, b : LetStructMembers) {
a.baz = 1 // expected-error {{cannot assign to property: 'baz' is a method}}
b.f = 42 // expected-error {{cannot assign to property: 'f' is a method}}
}

// SR-2354: Reject subscript declarations with mutable parameters.
class MutableSubscripts {
var x : Int = 0

subscript(x: inout Int) -> () { x += 1 } // expected-error {{'inout' may not be used on subscript parameters}}
subscript<T>(x: inout T) -> () { // expected-error {{'inout' may not be used on subscript parameters}}
fatalError()
}

static func initialize(from state: inout MutableSubscripts) -> MutableSubscripts {
return state
}
}


// SR-4214: Misleading location-less diagnostic when closure parameter type
// is inferred to be inout.
func sr4214() {
func sequence<T>(_ x : T, _ f : (T) -> T) -> T {
return f(x)
}

// expected-error@+1 {{expression type '(inout MutableSubscripts) -> ()' is ambiguous without more context}}
let closure = { val in val.x = 7 } as (inout MutableSubscripts) -> ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the parameter is assumed immutable unless the user actually writes inout or var - supposedly (see FIXME below). This one usually manifests itself with anonymous arguments that need to be converted to named arguments because we don't like inferring inout on closure parameters. @xedin was looking into this at one point IIRC

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we infer it inconsistently now, which is terrible, and then give a bad diagnostic in the non-inferred cases, which @CodaFi has improved. There are a handful of bugs about this.

var v = MutableSubscripts()
closure(&v)
// FIXME: This diagnostic isn't really all that much better
// expected-error@+1 {{cannot convert value of type '(inout MutableSubscripts) -> ()' to expected argument type '(_) -> _'}}
sequence(v) { (state : inout MutableSubscripts) -> () in
_ = MutableSubscripts.initialize(from: &state)
return ()
}
}