Skip to content

[Diagnostics] When building a subscript don't assume that overload is always present #5819

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
Nov 29, 2016
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: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ ERROR(cannot_subscript_base,none,
"cannot subscript a value of type %0",
(Type))

ERROR(cannot_subscript_ambiguous_base,none,
"cannot subscript a value of incorrect or ambiguous type", ())

ERROR(cannot_subscript_nil_literal,none,
"cannot subscript a nil literal value", ())

Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1341,7 +1341,7 @@ struct ASTNodeBase {};
void verifyChecked(SubscriptExpr *E) {
PrettyStackTraceExpr debugStack(Ctx, "verifying SubscriptExpr", E);

if (!E->getDecl()) {
if (!E->hasDecl()) {
Out << "Subscript expression is missing subscript declaration";
abort();
}
Expand Down
33 changes: 27 additions & 6 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1233,11 +1233,32 @@ namespace {
ConstraintLocatorBuilder locator,
bool isImplicit, AccessSemantics semantics) {
// Determine the declaration selected for this subscript operation.
auto selected = getOverloadChoice(
auto selected = getOverloadChoiceIfAvailable(
Copy link
Member

Choose a reason for hiding this comment

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

Huh, this makes me wonder: are all direct uses of getOverloadChoice within CSApply.cpp suspect because of this issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They might be, I saw couple of valid ones too, do you want to be to double-check and maybe create separate PR to address any issues I find?

Copy link
Member

Choose a reason for hiding this comment

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

That would be greatly appreciated!

cs.getConstraintLocator(
locator.withPathElement(
ConstraintLocator::SubscriptMember)));
auto choice = selected.choice;

// Handles situation where there was a solution available but it didn't
// have a proper overload selected from subscript call, might be because
// solver was allowed to return free or unresolved types, which can
// happen while running diagnostics on one of the expressions.
if (!selected.hasValue()) {
auto &tc = cs.TC;
auto baseType = base->getType();

if (auto errorType = baseType->getAs<ErrorType>()) {
tc.diagnose(base->getLoc(), diag::cannot_subscript_base,
errorType->getOriginalType())
.highlight(base->getSourceRange());
} else {
tc.diagnose(base->getLoc(), diag::cannot_subscript_ambiguous_base)
Copy link
Member

Choose a reason for hiding this comment

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

Should we assert that baseType->hasError() or is an UnresolvedType? Are there other cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think depending if free type variables are allowed it might be a type variable as well, not sure how much sense would it make it add assertion here...

Copy link
Member

@DougGregor DougGregor Nov 29, 2016

Choose a reason for hiding this comment

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

I brought it up because I saw similar assertions in other places that used getOverloadChoiceIfAvailable, and I do think it's useful to have this assertion: it's fairly easy to make a mistake when trying to line up locators in CSGen, CSSimplify, and CSApply. In such cases, it's programmer error on our part, and we would much rather have it assert to fail fast ("cannot find a locator when we found a complete solution!") than produce a mysterious "ambiguous" error for a constraint system that actually succeeded.

It'd be fine to do this as a follow-up.

.highlight(base->getSourceRange());
}

return nullptr;
}

auto choice = selected->choice;
auto subscript = cast<SubscriptDecl>(choice.getDecl());

auto &tc = cs.getTypeChecker();
Expand All @@ -1255,7 +1276,7 @@ namespace {
// Figure out the index and result types.
auto containerTy
= subscript->getDeclContext()->getDeclaredTypeOfContext();
auto subscriptTy = simplifyType(selected.openedType);
auto subscriptTy = simplifyType(selected->openedType);
auto indexTy = subscriptTy->castTo<AnyFunctionType>()->getInput();
auto resultTy = subscriptTy->castTo<AnyFunctionType>()->getResult();

Expand All @@ -1282,7 +1303,7 @@ namespace {
// Form the subscript expression.

// Handle dynamic lookup.
if (selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic ||
if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic ||
subscript->getAttrs().hasAttribute<OptionalAttr>()) {
base = coerceObjectArgumentToType(base, baseTy, subscript,
AccessSemantics::Ordinary, locator);
Expand All @@ -1308,13 +1329,13 @@ namespace {
solution.computeSubstitutions(
subscript->getInterfaceType(),
dc,
selected.openedFullType,
selected->openedFullType,
getConstraintSystem().getConstraintLocator(
locator.withPathElement(ConstraintLocator::SubscriptMember)),
substitutions);

// Convert the base.
auto openedFullFnType = selected.openedFullType->castTo<FunctionType>();
auto openedFullFnType = selected->openedFullType->castTo<FunctionType>();
auto openedBaseType = openedFullFnType->getInput();
containerTy = solution.simplifyType(tc, openedBaseType);
base = coerceObjectArgumentToType(
Expand Down
2 changes: 2 additions & 0 deletions test/Constraints/closures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,5 @@ func g_2994(arg: Int) -> Double {
return 2
}
C_2994<S_2994>(arg: { (r: S_2994) in f_2994(arg: g_2994(arg: r.dataOffset)) }) // expected-error {{cannot convert value of type 'Double' to expected argument type 'String'}}

let _ = { $0[$1] }(1, 1) // expected-error {{cannot subscript a value of incorrect or ambiguous type}}
3 changes: 0 additions & 3 deletions validation-test/Sema/type_checker_crashers/rdar27329076.swift

This file was deleted.

3 changes: 0 additions & 3 deletions validation-test/Sema/type_checker_crashers/rdar27787341.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// RUN: not %target-swift-frontend %s -typecheck

_ = try [ { return .D($0[0]) } ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// RUN: not %target-swift-frontend %s -typecheck

_ = [1].reduce([:]) { $0[$1] }
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: not --crash %target-swift-frontend %s -typecheck
// RUN: not %target-swift-frontend %s -typecheck

_ = [1].reduce( [Int:Int]() ) {
(dict, num) in dict[num] = num * num
Expand Down
7 changes: 0 additions & 7 deletions validation-test/compiler_crashers_2/0044-enum-dot-crash.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: not %target-swift-frontend %s -emit-silgen

enum X {
init?(a: Int) {
.p[a]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
// REQUIRES: asserts
.A[]