Skip to content
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

Fix Issue 15613, 11529: Show parameter mismatch and rvalue/lvalue ref message #7554

Merged
merged 10 commits into from Jan 26, 2018

Conversation

ntrel
Copy link
Contributor

@ntrel ntrel commented Dec 30, 2017

When there's only one overload to consider:

  • Add a supplemental message showing the first argument that failed to match the corresponding function parameter.
  • Alter the messsage to show if an rvalue is being passed to a ref/out parameter.
  • When necessary for disambiguation, show the qualified type of the parameter as well.

@dlang-bot
Copy link
Contributor

dlang-bot commented Dec 30, 2017

Thanks for your pull request, @ntrel! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.

Some tips to help speed things up:

  • smaller, focused PRs are easier to review than big ones

  • try not to mix up refactoring or style changes with bug fixes or feature enhancements

  • provide helpful commit messages explaining the rationale behind each change

Bear in mind that large or tricky changes may require multiple rounds of review and revision.

Please see CONTRIBUTING.md for more information.

Bugzilla references

Auto-close Bugzilla Description
9631 Error message not using fully qualified name when appropriate.
11529 Unclear error message when rvalue is passed as `ref'
15613 Parameter type mismatch error message are not very helpful

@nordlow
Copy link
Contributor

nordlow commented Dec 31, 2017

Better diagnostics is exactly what D needs right now! Great work!

src/dmd/func.d Outdated
@@ -3618,3 +3620,21 @@ extern (C++) final class DeleteDeclaration : FuncDeclaration
v.visit(this);
}
}

void showArgMismatch(Loc loc, Expressions* fargs, TypeFunction tf, size_t failIndex)
Copy link
Member

Choose a reason for hiding this comment

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

Please add Ddoc style function comment.

src/dmd/func.d Outdated
auto msg = "cannot pass %sargument `%s` of type `%s` to parameter `%s`";
// don't print parameter type if it's already in the parameter string
if (strcmp(par.type.toChars(), ts[1]) != 0)
msg ~= " of type `%s`";
Copy link
Member

Choose a reason for hiding this comment

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

This isn't guaranteed to produce a 0 terminated array.

Copy link
Member

Choose a reason for hiding this comment

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

Just use two different msg strings.

Copy link
Member

Choose a reason for hiding this comment

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

i.e.:

msg = strcmp() ? "cannot pass ..." : "cannot pass ... of type ...";

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed by adding \0 to 2nd string, hope that's OK?

src/dmd/hdrgen.d Outdated
@@ -3433,3 +3433,17 @@ extern (C++) const(char)* parametersTypeToChars(Parameters* parameters, int vara
v.parametersToBuffer(parameters, varargs);
return buf.extractString();
}

extern (C++) const(char)* parameterToChars(Parameter parameter, int varargs)
Copy link
Member

Choose a reason for hiding this comment

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

Add Ddoc function comment.

src/dmd/mtype.d Outdated
@@ -5935,6 +5936,7 @@ extern (C++) final class TypeFunction : TypeNext
}
}

auto u = size_t.max; // used for failedIndex if no match
Copy link
Contributor

@JinShil JinShil Jan 1, 2018

Choose a reason for hiding this comment

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

I would prefer having a bool failed and a size_t failedIndex and use failed to determine whether or not failedIndex holds a valid value.

Copy link
Contributor Author

@ntrel ntrel Jan 2, 2018

Choose a reason for hiding this comment

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

We can just check the return value against MATCH.nomatch to determine if failedIndex is valid.

src/dmd/hdrgen.d Outdated
* parameter = parameter to print.
* varargs = Kind of varargs, see TypeFunction.varargs.
* Returns: NT string representing parameter. */
extern (C++) const(char)* parameterToChars(Parameter parameter, int varargs)
Copy link
Contributor

@JinShil JinShil Jan 1, 2018

Choose a reason for hiding this comment

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

DDoc also needs a summary, and what does "NT" mean? If "null-terminated", spell it out please.

src/dmd/hdrgen.d Outdated
/** Params:
* parameters = parameters to print, such as TypeFunction.parameters.
* varargs = Kind of varargs, see TypeFunction.varargs.
* Returns: NT string representing parameters. */
extern (C++) const(char)* parametersTypeToChars(Parameters* parameters, int varargs)
Copy link
Contributor

@JinShil JinShil Jan 1, 2018

Choose a reason for hiding this comment

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

DDoc also needs a summary, and what does "NT" mean? If "null-terminated", spell it out please.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't abbreviate null-terminated.

TEST_OUTPUT:
---
fail_compilation/bug9631.d(79): Error: function bug9631.arg.f `(int i, S s)` is not callable using argument types `(int, S)`
fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `S s` of type `bug9631.S`
Copy link
Contributor

Choose a reason for hiding this comment

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

function bug9631.arg.f `(int i, S s)` is not callable using argument types `(int, S)`

  1. Tick marks should surround the entire function declaration (e.g `bug9631.arg.f (int i, S s)`). That may be a difficult thing to do and I'd be willing to pass on the tick marks for now
  2. The function accepts (int, S) but the error message says it's not callable using (int, S). Does this need fully qualified names or something?

Copy link
Contributor

Choose a reason for hiding this comment

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

cannot pass argument y of type bug9631.tem!().S to parameter S s of type bug9631.S

I suspect this line is supposed the clarify the first, but it's awfully confusing. Can't we have just one line with fully qualified type names?

Copy link
Contributor Author

@ntrel ntrel Jan 2, 2018

Choose a reason for hiding this comment

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

bug9631.arg.f `(int i, S s)`

This was introduced by Walter before this PR. I tried to fix it but can't work out why exactly it happens.

fail_compilation/bug9631.d(80): Error: function literal `__lambda2(S s)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `S s` of type `bug9631.tem!().S`
fail_compilation/bug9631.d(86): Error: constructor bug9631.arg.A.this `(S _param_0)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `S _param_0` of type `bug9631.S`
Copy link
Contributor

@JinShil JinShil Jan 2, 2018

Choose a reason for hiding this comment

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

cannot pass argument `S(0)`

`S(0)` doesn't look like an argument; it looks like a function invocation OK, I get it now, though I'm not sure we're making much progress on clarifying these messages.

to parameter `S _param_0` of type `bug9631.S`

I think this should read "... to parameter `bug9631.S _param_0`" without redundantly specifying one unqualified type name `S` and one qualified type name `bug9631.S`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

fail_compilation/diag8101b.d(18): `diag8101b.S.foo(int _param_0)`
fail_compilation/diag8101b.d(19): `diag8101b.S.foo(int _param_0, int _param_1)`
fail_compilation/diag8101b.d(29): Error: function diag8101b.S.bar `(int _param_0)` is not callable using argument types `(double)`
fail_compilation/diag8101b.d(29): cannot pass argument `1.00000` of type `double` to parameter `int _param_0`
Copy link
Contributor

@JinShil JinShil Jan 2, 2018

Choose a reason for hiding this comment

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

Can we shoot for something like:

function diag8101b.S.bar (int _param_0) is not callable using argument 1.00000 of type double to parameter int _param_0

You might even be able to make it less verbose with...

function diag8101b.S.bar is not callable using argument 1.00000 of type double to parameter int _param_0

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 it's useful to see the full parameter list, so we know which function the compiler means. If we combine both messages I think there's too much information in one sentence.

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 it's useful to see all the argument types too.

fail_compilation/diag8101b.d(17): `diag8101b.S.foo(int _param_0)`
fail_compilation/diag8101b.d(18): `diag8101b.S.foo(int _param_0, int _param_1)`
fail_compilation/diag8101b.d(33): Error: mutable method `diag8101b.S.bar` is not callable using a const object
fail_compilation/diag8101b.d(27): Error: none of the overloads of `foo` are callable using argument types `(double)`, candidates are:
Copy link
Contributor

@JinShil JinShil Jan 2, 2018

Choose a reason for hiding this comment

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

The term "argument types" seems odd here. What about...

none of the overloads of foo are callable using parameter list (double), candidates are:

This also removes the plural "types" with the singular parameter type oddity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it's talking about arguments so "argument list" would work. But this is not part of the PR, I expect argument types is used elsewhere too.

@ntrel
Copy link
Contributor Author

ntrel commented Jan 2, 2018

BTW, I also have code to do the same for deduced template function calls (if there are no overloads), but I'll wait until this is merged to submit.

src/dmd/hdrgen.d Outdated
parameter.accept(v);
if (varargs)
{
buf.writestring("...");
Copy link
Member

Choose a reason for hiding this comment

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

need test case to cover this new line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added test, turns out it was wrong for variadic parameters :-/

src/dmd/hdrgen.d Outdated
* parameter = parameter to print.
* varargs = kind of varargs, see TypeFunction.varargs.
* fullQual = whether to fully qualify types.
* Returns: Null-terminated string representing parameters. */
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Please conform to the existing way Ddoc comments are formatted in the front end. To wit:

/****
 * blah blah
 */

not:

/** blah blah
   * blah blah */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done for this module.

src/dmd/mtype.d Outdated
@@ -9224,7 +9228,7 @@ const(char*)[2] toAutoQualChars(Type t1, Type t2)
{
auto s1 = t1.toChars();
auto s2 = t2.toChars();
if (strcmp(s1, s2) == 0)
if (!t1.equals(t2) && strcmp(s1, s2) == 0)
Copy link
Member

Choose a reason for hiding this comment

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

when would the type strings be the same but they are different types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the strings aren't the full qualified type, e.g. S vs tem!().S.

Copy link
Member

Choose a reason for hiding this comment

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

Please add comment to that effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

src/dmd/hdrgen.d Outdated
@@ -3433,3 +3438,24 @@ extern (C++) const(char)* parametersTypeToChars(Parameters* parameters, int vara
v.parametersToBuffer(parameters, varargs);
return buf.extractString();
}

/** Pretty print function parameters.
Copy link
Member

Choose a reason for hiding this comment

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

This has the same description and return value as the previous function - what does it do different?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typo, should be parameter. Fixed.


.error(exp.loc, "`%s%s` is not callable using argument types `%s`",
exp.e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());
showArgMismatch(exp.loc, exp.arguments, tf, failIndex);
Copy link
Member

Choose a reason for hiding this comment

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

It'd be nice to have test coverage for the new code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW, what does TOKvar mean? I'm trying to hit this case.

Copy link
Member

Choose a reason for hiding this comment

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

TOKvar distinguishes a VarExp from other Expression types.

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 don't know how to test for the case when TOKvar is a function. Function pointers are handled differently.

@@ -2325,8 +2325,10 @@ extern (C++) final class TypeDeduced : Type
* tiargs initial list of template arguments
* tthis if !NULL, the 'this' pointer argument
* fargs arguments to function
* failedIndex address to store argument index of first type mismatch
Copy link
Member

Choose a reason for hiding this comment

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

note that failedIndex can be null

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've noted this for pMessage (which replaced failedIndex).

Copy link
Member

Choose a reason for hiding this comment

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

This should be in Ddoc style:

  • Params:
  • use = - then Ddoc can would throw an error about the non-existing failedIndex

Copy link
Contributor

@JinShil JinShil left a comment

Choose a reason for hiding this comment

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

Definitely an improvement.

@wilzbach
Copy link
Member

Rebased to upstream/master

Copy link
Member

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

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

Really cool!

@wilzbach wilzbach added this to the 2.079.0 milestone Jan 18, 2018
@ntrel
Copy link
Contributor Author

ntrel commented Jan 19, 2018

@wilzbach Thanks for rebasing. I changed the ddoc comments to use Params: as suggested.

@wilzbach
Copy link
Member

Rebased again ;-)

@dlang-bot dlang-bot merged commit fcc69c1 into dlang:master Jan 26, 2018
@ntrel ntrel deleted the qual-arg branch January 30, 2018 16:30
@Geod24
Copy link
Member

Geod24 commented May 4, 2018

So yesterday I dusted off an old project and tried to compile. Sure thing, I ran out of memory. This project used to take 2, max 3 GBs of memory. Now it runs out of memory after reaching 7 GBs.

As a result I started to bisect, which led me to this commit after a bit of gymnastic (having -de in the Makefile is really annoying when doing that exercise).
Trying to bisect this PR was rather annoying: the first commit (48c6b49) segfault on this code:

class Bar { this(int) {} }
void main () { scope b = new Bar; }

but that was fixed by later refactoring. Second commit (ff99241) doesn't compile because it was blindly rebased after the STCref => STC.ref_ refactoring, and so everything before commit 6 (15b1b50) does not compile. That makes solving issues more complicated than it needs to be.

Now the cherry on the top: this pull request (or more precisely, commit 6) allocates an error message for every matching failure during call resolution. And that memory is allocated with mem.xmalloc and never freed. I added scope (exit) rmem.xfree here and there but it did not help (because of the bump the pointer I guess, or the very high fragmentation it creates?).

It comes down to commit 6, which eagerly generates error message. @ntrel what was the motivation for it ?

@ntrel
Copy link
Contributor Author

ntrel commented May 5, 2018

@Geod24 Thanks for finding this. failedIndex is not enough information to generate the (updated) error message on the caller side.

I think the problem is the call to functionResolve - it won't immediately necessarily error after that call so failMessage should not be passed there. Instead we can re-call the function where failMessage is actually needed (warning: not tested), please try:
https://github.com/ntrel/dmd/tree/resolve-msg

@Geod24
Copy link
Member

Geod24 commented May 6, 2018

@ntrel I had a go at reverting this commit and it passes the test suite: #8216

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants