Comment #0 by default_357-line — 2019-02-22T08:56:58Z
Consider this code:
import std.algorithm;
void foo(T)(int i) { this.does.not.compile; }
void main() {
[2, 3, 4].each!foo;
}
foo() is clearly not valid code. However, the error DMD returns is something about "each cannot deduce function from argument types."
each() checks if its lambda argument matches the range we're giving it. However, it does this check using __traits(compiles). __traits(compiles) however does not just check whether the argument is sensible on the face of it, ie. whether foo can be called with an int, but also suppresses errors *inside* foo.
As a result, the error DMD outputs is near completely useless.
A possible solution may be a new traits: __traits(shallowCompiles, ...), that would check whether the expression "itself" compiled, ie. whether function arguments matched, templates could instantiate, etc., but would *not* suppress errors inside function bodies instantiated incidentally underneath the expression.
See also https://issues.dlang.org/show_bug.cgi?id=19448 for a different approach.
Comment #1 by default_357-line — 2019-02-22T09:37:12Z
An alternate proposal: `__traits(compiles, ...)` returns an "error object" on failure that encapsulates the gagged error, and evaluates to false for if(), ||, etc, but is preserved through short-circuiting. Ie. error1 || error2 -> (error1 + error2); ie. it is an entirely valid symbol that happens to correspond to a compiler error. When a template instantiation failure is logged, the error is output to accompany the failure.
This should preserve the cause of instantiation failures without requiring significant changes to existing code.
Comment #2 by robert.schadek — 2024-12-13T19:02:29Z