Skip to content

Commit

Permalink
Fix <rdar://problem/23272739> Poor diagnostic due to contextual const…
Browse files Browse the repository at this point in the history
…raint

Previously we erroneously complained:
  error: cannot invoke 'contains' with an argument list of type '(String)'
now we correctly complain:
  error: unexpected non-void return value in void function

This enhances CSDiags to use "getTypeOfMember" when analyzing method
candidates that are applied to a known base type.  Using it allows us to
substitute information about the base, resolving archetypes that exist in
subsequent argument positions.  In the testcase, this means that we use
information about Set<String> to know that the argument to "contains" is a
String.

This allows us to generate much better diagnostics in some cases, and works
around some limitations in the existing stuff for handling unresolved
archetypes.  One unfortunate change is the notes in Misc/misc_diagnostics.swift.
Because we don't track argument lists very well, we are flattening an argument
list that is actually ((Int,Int)) into (Int, Int) so we get a bogus looking
diagnostic.  This was possible before this patch though, it is just one
more case that triggers the issue.
  • Loading branch information
lattner committed Dec 2, 2015
1 parent a35699b commit 6636baf
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 10 deletions.
32 changes: 30 additions & 2 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,12 +1104,22 @@ namespace {
/// func f(a:Int)(b:Double) -> Int
/// Uncurry level of 0 indicates that we're looking at the "a" argument, an
/// uncurry level of 1 indicates that we're looking at the "b" argument.
///
/// The declType specifies a specific type to use for this decl that may be
/// more resolved than the decls type. For example, it may have generic
/// arguments substituted in.
struct UncurriedCandidate {
ValueDecl *decl;
unsigned level;
Type declType;

UncurriedCandidate(ValueDecl *decl, unsigned level)
: decl(decl), level(level), declType(decl->getType()) {
}

AnyFunctionType *getUncurriedFunctionType() const {
auto type = decl->getType();
// Start with the known type of the decl.
auto type = declType;

// If this is an operator func decl in a type context, the 'self' isn't
// actually going to be applied.
Expand Down Expand Up @@ -1491,11 +1501,29 @@ void CalleeCandidateInfo::collectCalleeCandidates(Expr *fn) {
if (auto AE = dyn_cast<ApplyExpr>(fn)) {
collectCalleeCandidates(AE->getFn());

// If this is a DotSyntaxCallExpr, then the callee is a method, and the
// argument list of this apply is the base being applied to the method.
// If we have a type for that, capture it so that we can calculate a
// substituted type, which resolves many generic arguments.
Type baseType;

// TODO: What about ConstructorRefCallExpr?
if (isa<DotSyntaxCallExpr>(AE) &&
!isUnresolvedOrTypeVarType(AE->getArg()->getType()))
baseType = AE->getArg()->getType()->getLValueOrInOutObjectType();

// If we found a candidate list with a recursive walk, try adjust the curry
// level for the applied subexpression in this call.
if (!candidates.empty()) {
for (auto &C : candidates)
for (auto &C : candidates) {
C.level += 1;

// Compute a new substituted type if we have a base type to apply.
if (baseType && C.level == 1)
C.declType = baseType->getTypeOfMember(CS->DC->getParentModule(),
C.decl, nullptr);
}

return;
}
}
Expand Down
10 changes: 9 additions & 1 deletion test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ func f20371273() {
// FIXME: Should complain about not having a return type annotation in the closure.
[0].map { _ in let r = (1,2).0; return r }
// expected-error @-1 {{cannot invoke 'map' with an argument list of type '(@noescape (Int) throws -> _)'}}
// expected-note @-2 {{expected an argument list of type '(@noescape (Self.Generator.Element) throws -> T)'}}
// expected-error @-2 {{cannot convert return expression of type 'Int' to return type 'T'}}
// expected-note @-3 {{expected an argument list of type '(@noescape Int throws -> T)'}}

// <rdar://problem/21078316> Less than useful error message when using map on optional dictionary type
func rdar21078316() {
Expand Down Expand Up @@ -649,3 +650,10 @@ func r22058555() {
}
}

// <rdar://problem/23272739> Poor diagnostic due to contextual constraint
func r23272739(contentType: String) {
let actualAcceptableContentTypes: Set<String> = []
return actualAcceptableContentTypes.contains(contentType) // expected-error {{unexpected non-void return value in void function}}
}


2 changes: 1 addition & 1 deletion test/Constraints/subscript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ let _ = 1["1"] // expected-error {{ambiguous use of 'subscript'}}

// rdar://17687826 - QoI: error message when reducing to an untyped dictionary isn't helpful
let squares = [ 1, 2, 3 ].reduce([:]) { (dict, n) in // expected-error {{cannot invoke 'reduce' with an argument list of type '([_ : _], @noescape (_, Int) throws -> _)'}}
// expected-note @-1 {{expected an argument list of type '(T, combine: @noescape (T, Self.Generator.Element) throws -> T)'}}
// expected-note @-1 {{expected an argument list of type '(T, combine: @noescape (T, Int) throws -> T)'}}
var dict = dict // expected-error {{type of expression is ambiguous without more context}}

dict[n] = n * n
Expand Down
6 changes: 3 additions & 3 deletions test/Generics/function_defs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func doCompare<T : EqualComparable, U : EqualComparable>(t1: T, t2: T, u: U) ->
}

return t1.isEqual(u) // expected-error {{cannot invoke 'isEqual' with an argument list of type '(U)'}}
// expected-note @-1 {{expected an argument list of type '(Self)'}}
// expected-note @-1 {{expected an argument list of type '(T)'}}
}

protocol MethodLessComparable {
Expand All @@ -38,7 +38,7 @@ func existential<T : EqualComparable, U : EqualComparable>(t1: T, t2: T, u: U) {
var eqComp : EqualComparable = t1 // expected-error{{protocol 'EqualComparable' can only be used as a generic constraint}}
eqComp = u
if t1.isEqual(eqComp) {} // expected-error{{cannot invoke 'isEqual' with an argument list of type '(EqualComparable)'}}
// expected-note @-1 {{expected an argument list of type '(Self)'}}
// expected-note @-1 {{expected an argument list of type '(T)'}}
if eqComp.isEqual(t2) {} // expected-error{{member 'isEqual' cannot be used on value of protocol type 'EqualComparable'; use a generic constraint instead}}
}

Expand Down Expand Up @@ -172,7 +172,7 @@ func staticEqCheck<T : StaticEq, U : StaticEq>(t: T, u: U) {
if T.isEqual(t, y: t) { return }
if U.isEqual(u, y: u) { return }
T.isEqual(t, y: u) // expected-error{{cannot invoke 'isEqual' with an argument list of type '(T, y: U)'}}
// expected-note @-1 {{expected an argument list of type '(Self, y: Self)'}}
// expected-note @-1 {{expected an argument list of type '(T, y: T)'}}
}

//===----------------------------------------------------------------------===//
Expand Down
6 changes: 3 additions & 3 deletions test/Misc/misc_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ let _: String = testIS1() // expected-error {{cannot convert value of type 'Int'

func insertA<T>(inout array : [T], elt : T) {
array.append(T); // expected-error {{cannot invoke 'append' with an argument list of type '((T).Type)'}}
// expected-note @-1 {{expected an argument list of type '(Element)'}}
// expected-note @-1 {{expected an argument list of type '(T)'}}
}

// <rdar://problem/17875634> can't append to array of tuples
Expand All @@ -112,10 +112,10 @@ func test17875634() {
// expected-note @-1 {{overloads for '+=' exist with these partially matching parameter lists:}}

match.append(row, col) // expected-error{{cannot invoke 'append' with an argument list of type '(Int, Int)'}}
// expected-note @-1 {{expected an argument list of type '(Element)'}}
// expected-note @-1 {{expected an argument list of type '(Int, Int)'}}

match.append(1, 2) // expected-error{{cannot invoke 'append' with an argument list of type '(Int, Int)'}}
// expected-note @-1 {{expected an argument list of type '(Element)'}}
// expected-note @-1 {{expected an argument list of type '(Int, Int)'}}

match.append(coord)
match.append((1, 2))
Expand Down

0 comments on commit 6636baf

Please sign in to comment.