Skip to content
173 changes: 170 additions & 3 deletions src/dmd/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ struct TemplatePrevious
*/
extern (C++) final class TemplateDeclaration : ScopeDsymbol
{
import dmd.root.array : Array;

TemplateParameters* parameters; // array of TemplateParameter's
TemplateParameters* origParameters; // originals for Ddoc

Expand All @@ -538,6 +540,10 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
// threaded list of previous instantiation attempts on stack
TemplatePrevious* previous;

private Expression lastConstraint; /// the constraint after the last failed evaluation
private Array!Expression lastConstraintNegs; /// its negative parts
private Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint`

extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false)
{
super(loc, ident);
Expand Down Expand Up @@ -680,6 +686,39 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
return buf.extractChars();
}

/****************************
* Similar to `toChars`, but does not print the template constraints
*/
const(char)* toCharsNoConstraints()
{
if (literal)
return Dsymbol.toChars();

OutBuffer buf;
HdrGenState hgs;

buf.writestring(ident.toChars());
buf.writeByte('(');
foreach (i, tp; *parameters)
{
if (i > 0)
buf.writestring(", ");
.toCBuffer(tp, &buf, &hgs);
}
buf.writeByte(')');

if (onemember)
{
FuncDeclaration fd = onemember.isFuncDeclaration();
if (fd && fd.type)
{
TypeFunction tf = fd.type.isTypeFunction();
buf.writestring(parametersTypeToChars(tf.parameterList));
}
}
return buf.extractChars();
}

override Prot prot() pure nothrow @nogc @safe
{
return protection;
Expand Down Expand Up @@ -782,15 +821,23 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
fd.selectorParameter = hiddenParams.selectorParameter;
}

Expression e = constraint.syntaxCopy();
lastConstraint = constraint.syntaxCopy();
lastConstraintTiargs = ti.tiargs;
lastConstraintNegs.setDim(0);

import dmd.staticcond;

assert(ti.inst is null);
ti.inst = ti; // temporary instantiation to enable genIdent()
scx.flags |= SCOPE.constraint;
bool errors;
bool result = evalStaticCondition(scx, constraint, e, errors);
const bool result = evalStaticCondition(scx, constraint, lastConstraint, errors, &lastConstraintNegs);
if (result || errors)
{
lastConstraint = null;
lastConstraintTiargs = null;
lastConstraintNegs.setDim(0);
}
ti.inst = null;
ti.symtab = null;
scx = scx.pop();
Expand All @@ -800,6 +847,115 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
return result;
}

/****************************
* Destructively get the error message from the last constraint evaluation
* Params:
* tip = tip to show after printing all overloads
*/
const(char)* getConstraintEvalError(ref const(char)* tip)
{
import dmd.staticcond;

// there will be a full tree view in verbose mode, and more compact list in the usual
const full = global.params.verbose;
uint count;
const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count);
scope (exit)
{
lastConstraint = null;
lastConstraintTiargs = null;
lastConstraintNegs.setDim(0);
}
if (msg)
{
OutBuffer buf;

assert(parameters && lastConstraintTiargs);
if (parameters.length > 0)
{
formatParamsWithTiargs(*lastConstraintTiargs, buf);
buf.writenl();
}
if (!full)
{
// choosing singular/plural
const s = (count == 1) ?
" must satisfy the following constraint:" :
" must satisfy one of the following constraints:";
buf.writestring(s);
buf.writenl();
// the constraints
buf.writeByte('`');
buf.writestring(msg);
buf.writeByte('`');
}
else
{
buf.writestring(" whose parameters have the following constraints:");
buf.writenl();
const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
buf.writestring(sep);
buf.writenl();
// the constraints
buf.writeByte('`');
buf.writestring(msg);
buf.writeByte('`');
buf.writestring(sep);
tip = "not satisfied constraints are marked with `>`";
}
return buf.extractChars();
}
else
return null;
}

private void formatParamsWithTiargs(ref Objects tiargs, ref OutBuffer buf)
{
buf.writestring(" with `");

// write usual arguments line-by-line
// skips trailing default ones - they are not present in `tiargs`
const bool variadic = isVariadic() !is null;
const end = cast(int)parameters.length - (variadic ? 1 : 0);
uint i;
for (; i < tiargs.length && i < end; i++)
{
if (i > 0)
{
buf.writeByte(',');
buf.writenl();
buf.writestring(" ");
}
buf.write((*parameters)[i]);
buf.writestring(" = ");
buf.write(tiargs[i]);
}
// write remaining variadic arguments on the last line
if (variadic)
{
if (i > 0)
{
buf.writeByte(',');
buf.writenl();
buf.writestring(" ");
}
buf.write((*parameters)[end]);
buf.writestring(" = ");
buf.writeByte('(');
if (cast(int)tiargs.length - end > 0)
{
buf.write(tiargs[end]);
foreach (j; parameters.length .. tiargs.length)
{
buf.writestring(", ");
buf.write(tiargs[j]);
}
}
buf.writeByte(')');
}
buf.writeByte('`');
}

/******************************
* Create a scope for the parameters of the TemplateInstance
* `ti` in the parent scope sc from the ScopeDsymbol paramsym.
Expand Down Expand Up @@ -7073,7 +7229,18 @@ extern (C++) class TemplateInstance : ScopeDsymbol
else if (tdecl && !tdecl.overnext)
{
// Only one template, so we can give better error message
error("does not match template declaration `%s`", tdecl.toChars());
const(char)* msg = "does not match template declaration";
const(char)* tip;
const tmsg = tdecl.toCharsNoConstraints();
const cmsg = tdecl.getConstraintEvalError(tip);
if (cmsg)
{
error("%s `%s`\n%s", msg, tmsg, cmsg);
if (tip)
.tip(tip);
}
else
error("%s `%s`", msg, tmsg);
}
else
.error(loc, "%s `%s.%s` does not match any template declaration", tempdecl.kind(), tempdecl.parent.toPrettyChars(), tempdecl.ident.toChars());
Expand Down
30 changes: 30 additions & 0 deletions src/dmd/errors.d
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ enum Classification
gagged = Color.brightBlue, /// for gagged errors
warning = Color.brightYellow, /// for warnings
deprecation = Color.brightCyan, /// for deprecations
tip = Color.brightGreen, /// for tip messages
}

/**
Expand Down Expand Up @@ -400,6 +401,20 @@ extern (C++) void message(const(char)* format, ...)
va_end(ap);
}

/**
* Print a tip message with the prefix and highlighting.
* Params:
* format = printf-style format specification
* ... = printf-style variadic arguments
*/
extern (C++) void tip(const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
vtip(format, ap);
va_end(ap);
}

/**
* Just print to stderr, doesn't care about gagging.
* (format,ap) text within backticks gets syntax highlighted.
Expand Down Expand Up @@ -610,6 +625,21 @@ extern (C++) void vmessage(const ref Loc loc, const(char)* format, va_list ap)
fflush(stdout); // ensure it gets written out in case of compiler aborts
}

/**
* Same as $(D tip), but takes a va_list parameter.
* Params:
* format = printf-style format specification
* ap = printf-style variadic arguments
*/
extern (C++) void vtip(const(char)* format, va_list ap)
{
if (!global.gag)
{
Loc loc = Loc.init;
verrorPrint(loc, Classification.tip, " Tip: ", format, ap);
}
}

/**
* Same as $(D deprecationSupplemental), but takes a va_list parameter.
* Params:
Expand Down
13 changes: 12 additions & 1 deletion src/dmd/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -2939,6 +2939,7 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
{
// max num of overloads to print (-v overrides this).
int numToDisplay = 5;
const(char)* constraintsTip;

overloadApply(declaration, (Dsymbol s)
{
Expand All @@ -2956,7 +2957,14 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
}
else if (auto td = s.isTemplateDeclaration())
{
.errorSupplemental(td.loc, "`%s`", td.toPrettyChars());
import dmd.staticcond;

const tmsg = td.toCharsNoConstraints();
const cmsg = td.getConstraintEvalError(constraintsTip);
if (cmsg)
.errorSupplemental(td.loc, "`%s`\n%s", tmsg, cmsg);
else
.errorSupplemental(td.loc, "`%s`", tmsg);
nextOverload = td.overnext;
}

Expand All @@ -2972,6 +2980,9 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
.errorSupplemental(loc, "... (%d more, -v to show) ...", num);
return 1; // stop iterating
});
// should be only in verbose mode
if (constraintsTip)
.tip(constraintsTip);
}

/**************************************
Expand Down
Loading