Skip to content
22 changes: 12 additions & 10 deletions src/dmd/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -2321,16 +2321,18 @@ extern (C++) final class TypeDeduced : Type
/*************************************************
* Given function arguments, figure out which template function
* to expand, and return matching result.
* Input:
* m matching result
* dstart the root of overloaded function templates
* loc instantiation location
* sc instantiation scope
* tiargs initial list of template arguments
* tthis if !NULL, the 'this' pointer argument
* fargs arguments to function
* Params:
* m = matching result
* dstart = the root of overloaded function templates
* loc = instantiation location
* sc = instantiation scope
* tiargs = initial list of template arguments
* tthis = if !NULL, the 'this' pointer argument
* fargs = arguments to function
* pMessage = address to store error message, or null
*/
void functionResolve(Match* m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs, Type tthis, Expressions* fargs)
void functionResolve(Match* m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
Type tthis, Expressions* fargs, const(char)** pMessage = null)
{
version (none)
{
Expand Down Expand Up @@ -2416,7 +2418,7 @@ void functionResolve(Match* m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiar
else
return 0; // MATCH.nomatch
}
MATCH mfa = tf.callMatch(tthis_fd, fargs);
MATCH mfa = tf.callMatch(tthis_fd, fargs, 0, pMessage);
//printf("test1: mfa = %d\n", mfa);
if (mfa > MATCH.nomatch)
{
Expand Down
19 changes: 13 additions & 6 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -3430,7 +3430,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return setError();
}

if (!tf.callMatch(null, exp.arguments))
const(char)* failMessage;
if (!tf.callMatch(null, exp.arguments, 0, &failMessage))
{
OutBuffer buf;
buf.writeByte('(');
Expand All @@ -3440,7 +3441,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
tthis.modToBuffer(&buf);

//printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
.error(exp.loc, "%s `%s%s` is not callable using argument types `%s`", p, exp.e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());
.error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
p, exp.e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());
if (failMessage)
errorSupplemental(exp.loc, failMessage);
return setError();
}
// Purity and safety check should run after testing arguments matching
Expand Down Expand Up @@ -3494,22 +3498,25 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
assert(exp.f);
tiargs = null;

if (ve.hasOverloads)
if (exp.f.overnext)
exp.f = resolveFuncCall(exp.loc, sc, exp.f, tiargs, null, exp.arguments, 2);
else
{
exp.f = exp.f.toAliasFunc();
TypeFunction tf = cast(TypeFunction)exp.f.type;
if (!tf.callMatch(null, exp.arguments))
const(char)* failMessage;
if (!tf.callMatch(null, exp.arguments, 0, &failMessage))
{
OutBuffer buf;
buf.writeByte('(');
argExpTypesToCBuffer(&buf, exp.arguments);
buf.writeByte(')');

//printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
.error(exp.loc, "`%s%s` is not callable using argument types `%s`", exp.e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());

.error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
exp.f.kind(), exp.f.toPrettyChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());
if (failMessage)
errorSupplemental(exp.loc, failMessage);
exp.f = null;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/dmd/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -2481,7 +2481,8 @@ extern (C++) FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s,
Match m;
m.last = MATCH.nomatch;

functionResolve(&m, s, loc, sc, tiargs, tthis, fargs);
const(char)* failMessage;
functionResolve(&m, s, loc, sc, tiargs, tthis, fargs, &failMessage);

if (m.last > MATCH.nomatch && m.lastf)
{
Expand Down Expand Up @@ -2599,6 +2600,8 @@ extern (C++) FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s,
.error(loc, "%s `%s%s%s` is not callable using argument types `%s`",
fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameters, tf.varargs),
tf.modToChars(), fargsBuf.peekString());
if (failMessage)
errorSupplemental(loc, failMessage);
}
}

Expand Down Expand Up @@ -3632,3 +3635,4 @@ extern (C++) final class DeleteDeclaration : FuncDeclaration
v.visit(this);
}
}

30 changes: 30 additions & 0 deletions src/dmd/hdrgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -3421,6 +3421,13 @@ extern (C++) void arrayObjectsToBuffer(OutBuffer* buf, Objects* objects)
}
}

/*************************************************************
* Pretty print function parameters.
* Params:
* parameters = parameters to print, such as TypeFunction.parameters.
* varargs = kind of varargs, see TypeFunction.varargs.
* Returns: Null-terminated string representing parameters.
*/
extern (C++) const(char)* parametersTypeToChars(Parameters* parameters, int varargs)
{
OutBuffer buf;
Expand All @@ -3429,3 +3436,26 @@ extern (C++) const(char)* parametersTypeToChars(Parameters* parameters, int vara
v.parametersToBuffer(parameters, varargs);
return buf.extractString();
}

/*************************************************************
* Pretty print function parameter.
* Params:
* parameter = parameter to print.
* tf = TypeFunction which holds parameter.
* fullQual = whether to fully qualify types.
* Returns: Null-terminated string representing parameters.
*/
extern (C++) const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQual)
{
OutBuffer buf;
HdrGenState hgs;
hgs.fullQual = fullQual;
scope PrettyPrintVisitor v = new PrettyPrintVisitor(&buf, &hgs);

parameter.accept(v);
if (tf.varargs == 2 && parameter == Parameter.getNth(tf.parameters, tf.parameters.dim - 1))
{
buf.writestring("...");
}
return buf.extractString();
}
43 changes: 39 additions & 4 deletions src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -5885,15 +5885,33 @@ extern (C++) final class TypeFunction : TypeNext
return t.merge();
}

// arguments get specially formatted
private const(char)* getParamError(const(char)* format, Expression arg, Parameter par)
{
// show qualification when toChars() is the same but types are different
auto at = arg.type.toChars();
bool qual = !arg.type.equals(par.type) && strcmp(at, par.type.toChars()) == 0;
if (qual)
at = arg.type.toPrettyChars(true);
OutBuffer as;
as.printf("`%s` of type `%s`", arg.toChars(), at);
OutBuffer ps;
ps.printf("`%s`", parameterToChars(par, this, qual));
OutBuffer buf;
buf.printf(format, as.peekString(), ps.peekString());
return buf.extractString();
}

/********************************
* 'args' are being matched to function 'this'
* Determine match level.
* Input:
* flag 1 performing a partial ordering match
* pMessage address to store error message, or null
* Returns:
* MATCHxxxx
*/
MATCH callMatch(Type tthis, Expressions* args, int flag = 0)
MATCH callMatch(Type tthis, Expressions* args, int flag = 0, const(char)** pMessage = null)
{
//printf("TypeFunction::callMatch() %s\n", toChars());
MATCH match = MATCH.exact; // assume exact match
Expand Down Expand Up @@ -6022,7 +6040,10 @@ extern (C++) final class TypeFunction : TypeNext
if (m && !arg.isLvalue())
{
if (p.storageClass & STC.out_)
{
if (pMessage) *pMessage = getParamError("cannot pass rvalue argument %s to parameter %s", arg, p);
goto Nomatch;
}

if (arg.op == TOK.string_ && tp.ty == Tsarray)
{
Expand All @@ -6044,7 +6065,10 @@ extern (C++) final class TypeFunction : TypeNext
}
}
else
{
if (pMessage) *pMessage = getParamError("cannot pass rvalue argument %s to parameter %s", arg, p);
goto Nomatch;
}
}

/* Find most derived alias this type being matched.
Expand All @@ -6068,7 +6092,12 @@ extern (C++) final class TypeFunction : TypeNext
* ref T[dim] <- an lvalue of const(T[dim]) argument
*/
if (!ta.constConv(tp))
{
if (pMessage) *pMessage = getParamError(
arg.isLvalue() ? "cannot pass argument %s to parameter %s" :
"cannot pass rvalue argument %s to parameter %s", arg, p);
goto Nomatch;
}
}
}

Expand Down Expand Up @@ -6125,7 +6154,10 @@ extern (C++) final class TypeFunction : TypeNext
m = arg.implicitConvTo(ta.next);

if (m == MATCH.nomatch)
{
if (pMessage) *pMessage = getParamError("cannot pass argument %s to parameter %s", arg, p);
goto Nomatch;
}
if (m < match)
match = m;
}
Expand All @@ -6137,9 +6169,11 @@ extern (C++) final class TypeFunction : TypeNext
goto Ldone;

default:
goto Nomatch;
break;
}
}
if (pMessage && u < nargs)
*pMessage = getParamError("cannot pass argument %s to parameter %s", (*args)[u], p);
goto Nomatch;
}
if (m < match)
Expand Down Expand Up @@ -9224,7 +9258,7 @@ extern (C++) final class Parameter : RootObject
extern (D) static immutable bool[SR.max + 1][SR.max + 1] covariant = covariantInit();
}

/**
/*************************************************************
* For printing two types with qualification when necessary.
* Params:
* t1 = The first type to receive the type name for
Expand All @@ -9237,7 +9271,8 @@ const(char*)[2] toAutoQualChars(Type t1, Type t2)
{
auto s1 = t1.toChars();
auto s2 = t2.toChars();
if (strcmp(s1, s2) == 0)
// show qualification only if it's different
if (!t1.equals(t2) && strcmp(s1, s2) == 0)
{
s1 = t1.toPrettyChars(true);
s2 = t2.toPrettyChars(true);
Expand Down
18 changes: 18 additions & 0 deletions test/fail_compilation/bug15613.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
TEST_OUTPUT:
---
fail_compilation/bug15613.d(16): Error: function `bug15613.f(int...)` is not callable using argument types `(typeof(null))`
fail_compilation/bug15613.d(16): cannot pass argument `null` of type `typeof(null)` to parameter `int...`
Copy link
Contributor

@JinShil JinShil Jan 12, 2018

Choose a reason for hiding this comment

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

I suggest special casing null so it reads: "cannot pass null to parameter int...". "null of type typeof(null)" is quite an odd thing to say. I'll still approve this PR without this change, as this PR is a huge improvement over the current implementation, so it's your call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally, maybe. This is a general issue though, e.g. int x = null; gives:

funcvar.d(5): Error: cannot implicitly convert expression `null` of type `typeof(null)` to `int`

fail_compilation/bug15613.d(17): Error: function `bug15613.g(Object, ...)` is not callable using argument types `(int)`
fail_compilation/bug15613.d(17): cannot pass argument `8` of type `int` to parameter `Object`
---
*/

void f(int...);
void g(Object, ...);

void main()
{
f(null);
g(8);
}
51 changes: 51 additions & 0 deletions test/fail_compilation/bug9631.d
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,54 @@ void test3()
S[1] sa = cast(S[1])ta;
auto t2 = cast(tem!().S[])sa;
}

/*
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 `bug9631.S s`
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 line 66 calls it bug9631.S but line 65 calls it just S. Perhaps it would be better to just call it S on line 66, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point is to disambiguate between S and tem!().S. You're right that the module name isn't needed, but it makes it clear that the compiler has fully qualified the type. In the first line, there's no qualification so that it's easier to read the function parameters. Given this, if I remove the module name, it might not be so clear.

Copy link
Member

Choose a reason for hiding this comment

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

ok

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 `bug9631.tem!().S 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 `bug9631.S _param_0`
---
*/
void arg()
{
S x;
tem!().S y;

void f(int i, S s);
f(4, y);
(tem!().S s){}(x);

struct A
{
this(S){}
}
A(tem!().S());
}

/*
TEST_OUTPUT:
---
fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0`
fail_compilation/bug9631.d(107): Error: template `bug9631.targ.ft` cannot deduce function from argument types `!()(S)`, candidates are:
fail_compilation/bug9631.d(105): `bug9631.targ.ft()(tem!().S)`
fail_compilation/bug9631.d(109): Error: template `bug9631.targ.ft2` cannot deduce function from argument types `!()(S, int)`, candidates are:
fail_compilation/bug9631.d(108): `bug9631.targ.ft2(T)(S, T)`
---
*/
void targ()
{
S x;
tem!().S y;

void ft()(tem!().S){}
ft!()(x);
ft(x);
void ft2(T)(S, T){}
ft2(y, 1);
}

6 changes: 4 additions & 2 deletions test/fail_compilation/diag13082.d
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/*
TEST_OUTPUT:
---
fail_compilation/diag13082.d(22): Error: constructor `diag13082.C.this(int a)` is not callable using argument types `(string)`
fail_compilation/diag13082.d(23): Error: constructor `diag13082.S.this(int a)` is not callable using argument types `(string)`
fail_compilation/diag13082.d(24): Error: constructor `diag13082.C.this(int a)` is not callable using argument types `(string)`
fail_compilation/diag13082.d(24): cannot pass argument `b` of type `string` to parameter `int a`
fail_compilation/diag13082.d(25): Error: constructor `diag13082.S.this(int a)` is not callable using argument types `(string)`
fail_compilation/diag13082.d(25): cannot pass argument `b` of type `string` to parameter `int a`
---
*/

Expand Down
17 changes: 9 additions & 8 deletions test/fail_compilation/diag8101b.d
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
/*
TEST_OUTPUT:
---
fail_compilation/diag8101b.d(26): Error: none of the overloads of `foo` are callable using argument types `(double)`, candidates are:
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(28): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)`
fail_compilation/diag8101b.d(31): Error: none of the overloads of `foo` are callable using a `const` object, candidates are:
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:
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`
fail_compilation/diag8101b.d(32): Error: none of the overloads of `foo` are callable using a `const` object, candidates are:
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(34): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object
---
*/

Expand Down
3 changes: 2 additions & 1 deletion test/fail_compilation/fail263.d
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail263.d(18): Error: function `fail263.f(byte* p)` is not callable using argument types `(const(byte)*)`
fail_compilation/fail263.d(19): Error: function `fail263.f(byte* p)` is not callable using argument types `(const(byte)*)`
fail_compilation/fail263.d(19): cannot pass argument `cast(const(byte)*)A` of type `const(byte)*` to parameter `byte* p`
---
*/

Expand Down
Loading