Difference between revisions of "DIP83"

From D Wiki
Jump to: navigation, search
Line 29: Line 29:
 
== Rationale ==
 
== Rationale ==
  
Failing asserts in unittests currently given no hints about the reason why a test failed. For
+
Failing asserts in unittests currently given no hints about the reason why a test failed. To aid the developer in debugging the failing assert
  
* binary expressions such as '''assert(x == y)''' the developer typically wants to know the values of '''x''' and '''y''' and  
+
* binary expressions such as '''assert(x == y)''' should, for instance, print the values of '''x''' and '''y''' and  
* for unary expressions such as '''assert(!x)''' the value of '''x'''.
+
* unary expressions such as '''assert(!x)''' should, for instance, print the value of '''x'''.
  
Pretty printing is typically enabled by the developer by changing the D compilation flags ('''DFLAGS''') to calls to rdmd, dub, scons etc upon failure with existing behavior of '''-unittest''' for a specific failing module. This will be more convenient than explicitly adding the prints of the left-hand-side expression '''lhs''' and righ-hand-side expression '''rhs''' of assert expression in the failing unittest.
+
This extra, so called, pretty printing can be enable by developer by changing the D compilation flags ('''DFLAGS''') to calls to rdmd, dub, scons etc upon failure with existing behavior of '''-unittest''' for a specific failing module. This will be more convenient than explicitly adding the prints of the left-hand-side expression '''lhs''' and right-hand-side expression '''rhs''' directly before the call to the failing assert.
  
 
== Description ==
 
== Description ==
Line 42: Line 42:
 
This diagnostics is activated only when DMD is called with a specific command line flag, say '''-unittest=verbose'''.
 
This diagnostics is activated only when DMD is called with a specific command line flag, say '''-unittest=verbose'''.
  
If DMD is called with this flag it will then rewrite assert expression ('''AssertExpr''' in dmd source) such as
+
If DMD is called with this flag it will rewrite all assert expressions ('''AssertExpr''' in dmd source) such as
  
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
assert(lhs == rhs)
+
assert(lhs BINOP rhs)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 51: Line 51:
  
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
assertBinOp(A_FILE, A_LINE, A_COLUMN, "==")(lhs == rhs, lhs, rhs)
+
assertBinOp(__FILE__, __LINE__, __COLUMN__, "BINOP")(lhs, rhs)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 57: Line 57:
  
 
<syntaxhighlight lang="d">
 
<syntaxhighlight lang="d">
assertBinOp(string file, uint line, uint column, string op)(E, L, R)(lazy E expression, lazy L lhs, lazy R rhs)
+
assertBinOp(string file, uint line, uint column, string binop)(L, R)(lazy L lhs, lazy R rhs)
 +
</syntaxhighlight>
 +
 
 +
and similarly for unary expressions replace
 +
 
 +
<syntaxhighlight lang="d">
 +
assert(UOP lhs)
 +
</syntaxhighlight>
 +
 
 +
with
 +
 
 +
<syntaxhighlight lang="d">
 +
assertBinOp(__FILE__, __LINE__, __COLUMN__, UNOP)(lhs, rhs)
 +
</syntaxhighlight>
 +
 
 +
where
 +
 
 +
<syntaxhighlight lang="d">
 +
assertUnOp(string file, uint line, uint column, string unop)(L, R)(lazy L lhs, lazy R rhs)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
and similarly for unary expressions.
 
  
 
The default implementation of '''assertBinOp''' (preferrably defined somewhere in druntime) would be D code that mimics the current behaviour of '''assert(expression)''' by throwing an '''AssertExpression''' if '''cast(bool)expression''' is '''false''' (dont know about the behaviour of nothrow/@nogc discussed above though). The Phobos-developer can then do what he likes with the information he needs in the extra arguments in specific templated overloads of '''assertBinOp'''.
 
The default implementation of '''assertBinOp''' (preferrably defined somewhere in druntime) would be D code that mimics the current behaviour of '''assert(expression)''' by throwing an '''AssertExpression''' if '''cast(bool)expression''' is '''false''' (dont know about the behaviour of nothrow/@nogc discussed above though). The Phobos-developer can then do what he likes with the information he needs in the extra arguments in specific templated overloads of '''assertBinOp'''.

Revision as of 05:49, 2 October 2015

Title: Configurable Assert Diagnostics
DIP: 83
Version: 1
Status: Draft
Created: 2015-10-01
Last Modified: 2015-10-2
Author: Per Nordlöw
Links:

Abstract

Allow for assert's to do pretty printing of its failing expression when flagged for in call to compiler. Printing is configurable via specific sets of (template) function overloads.

Rationale

Failing asserts in unittests currently given no hints about the reason why a test failed. To aid the developer in debugging the failing assert

  • binary expressions such as assert(x == y) should, for instance, print the values of x and y and
  • unary expressions such as assert(!x) should, for instance, print the value of x.

This extra, so called, pretty printing can be enable by developer by changing the D compilation flags (DFLAGS) to calls to rdmd, dub, scons etc upon failure with existing behavior of -unittest for a specific failing module. This will be more convenient than explicitly adding the prints of the left-hand-side expression lhs and right-hand-side expression rhs directly before the call to the failing assert.

Description

This DIP proposes to add library-level-configurable diagnostics to failing calls to assert(expr) typically called from within unittest-blocks.

This diagnostics is activated only when DMD is called with a specific command line flag, say -unittest=verbose.

If DMD is called with this flag it will rewrite all assert expressions (AssertExpr in dmd source) such as

assert(lhs BINOP rhs)

with

assertBinOp(__FILE__, __LINE__, __COLUMN__, "BINOP")(lhs, rhs)

where

assertBinOp(string file, uint line, uint column, string binop)(L, R)(lazy L lhs, lazy R rhs)

and similarly for unary expressions replace

assert(UOP lhs)

with

assertBinOp(__FILE__, __LINE__, __COLUMN__, UNOP)(lhs, rhs)

where

assertUnOp(string file, uint line, uint column, string unop)(L, R)(lazy L lhs, lazy R rhs)


The default implementation of assertBinOp (preferrably defined somewhere in druntime) would be D code that mimics the current behaviour of assert(expression) by throwing an AssertExpression if cast(bool)expression is false (dont know about the behaviour of nothrow/@nogc discussed above though). The Phobos-developer can then do what he likes with the information he needs in the extra arguments in specific templated overloads of assertBinOp.

This specific behaviour could be extendable by adding (typically templated) overloads of assertBinOp for specific sets of types (concepts).

In this way D would get the extendability we want in testing-frameworks such as std.experimental.testing (https://github.com/D-Programming-Language/phobos/pull/3207) without adding a new assert-overload-set and without sacrifycing default memory usage in DMD/Phobos unittests.

Further, this solution enables the possibility to provide fancy diagnostics behaviour in assertBinOp for failing array/range or aggregate (struct or class) comparisons. This diagnostics could also have different pretty printing backends such as HTML.

For example a failing

assert([1,2,3] == [1,2,4]);

could pretty-print

([1,2,3][2] is 3) != ([1,2,4][2] is 4)

or for aggregates a failing

struct A { int x, y; }
auto a = A(1,2);
auto b = A(1,3);
assert(a == b);

could pretty-print

(a.y is 2) != (b.y is 3)

Parts of the solution list at

https://issues.dlang.org/show_bug.cgi?id=5547#c3

including rewriting/expansion of AssertExpr could probably reused.

Impact

Copyright

This document has been placed in the Public Domain.