|
| 1 | +# Providing more selective control over contracts |
| 2 | + |
| 3 | +| Field | Value | |
| 4 | +|-----------------|-----------------------------------------------------------------| |
| 5 | +| DIP: | DIP1006 | |
| 6 | +| RC# | 0 | |
| 7 | +| Author: | Mathias Lang - [email protected] | |
| 8 | +| Implementation: | None | |
| 9 | +| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | |
| 10 | + |
| 11 | + |
| 12 | +## Abstract |
| 13 | + |
| 14 | +A proposal to expose a way to selectively disable some of D's contract features |
| 15 | +(invariant, `in` / `out` and `assert`). |
| 16 | + |
| 17 | + |
| 18 | +### Links |
| 19 | + |
| 20 | +Limited D1 implementation, used by Sociomantic: https://github.com/dlang/dmd/pull/6347 |
| 21 | + |
| 22 | + |
| 23 | +## Description |
| 24 | + |
| 25 | +Add a command-line switch, named `-contracts` in this proposal, which takes 4 possible values: |
| 26 | + |
| 27 | +- `all`: the default, present for completeness and scripts, so one can just append it to a generated command line to enable all contracts checks. |
| 28 | +- `noinvariant`: Disable implicit `invariant` calls, but leaves function's `in` / `out` contracts and `assert` in. |
| 29 | +- `assert`: Disable `invariant` blocks and `in` / `out` contracts, but leaves `assert` in. |
| 30 | +- `none`: Disable all `invariant`, `in` and `out` contracts, and `assert`. |
| 31 | + |
| 32 | +Note that the `noinvariant` value only disables implicit calls to invariant. Explicit calls can only disabled by disabling `assert`. |
| 33 | + |
| 34 | + |
| 35 | +### Rationale |
| 36 | + |
| 37 | +#### Prelude on contracts |
| 38 | + |
| 39 | +Under the broad name "Contracts", we regroup 4 of D features: `assert`, `in` contracts, `out` contracts and `invariant`. |
| 40 | +Contracts in D are a mean to do sanity checking. A contract that fails is a sign of a faulty logic in the code. |
| 41 | +Since contracts should never fail, the currently advertised approach for D programers is to use them during development, |
| 42 | +then deploy a `-release` build of the application for maximum performances. |
| 43 | + |
| 44 | +Contracts naturally fit in a hierarchy, where `assert` is the most basic block (being used by others), |
| 45 | +and `invariant` is meant for expensive sanity checking. `in` and `out` contracts are located in between. |
| 46 | + |
| 47 | +#### Cost of contracts |
| 48 | + |
| 49 | +`assert` and `in` / `out` contracts follow a pay-for-what-you-use approach: an `assert` which is never executed is free, |
| 50 | +and a `in` or `out` contract on a function that is never called is also free of runtime overhead. |
| 51 | + |
| 52 | +`invariant` stand out in that regard: they can be used on a pay-for-what-you-use basis, by using `assert(object)` (or `assert(this)`), |
| 53 | +but they are also implicitly called twice on every `public` and `protected` class method call. |
| 54 | +The call itself is not direct: the compiler inserts a call to `_d_invariant`, [which can be found in druntime](https://github.com/dlang/druntime/blob/v2.072.0/src/rt/invariant.d). |
| 55 | +The code being very simple results in the class hierarchy to be traversed completely twice (no caching is done) |
| 56 | +for every call, even for classes which do not define any invariant. |
| 57 | + |
| 58 | +Profiling some real-time applications within Sociomantic showed that `_d_invariant` was the most expensive call in the application. |
| 59 | +Affected applications were using little or no invariant, but since Sociomantic code is mostly writen in an OOP style, |
| 60 | +simply disabling invariants (with no other optimization) led to 20% more throughput. |
| 61 | + |
| 62 | +#### Issues with the current approach |
| 63 | + |
| 64 | +Testing can be complete and it is not rare to miss a scenario that can show up any time in a production setting. |
| 65 | +In such a case, having a contract not failing where it should can lead to costs higher than the one induced by a crash of the application. |
| 66 | +Example of such case include critical data corruption and unexpected monetary spending. |
| 67 | + |
| 68 | +For this reason, developers are often wary of disabling such safety features, even in production, as long as the performance cost is acceptable. |
| 69 | +What can be viewed as an acceptable performance cost is up to the end user and the code base. |
| 70 | +Any OOP-intensive code will want to get rid of `invariant` as a first step, while code more function-oriented might be more interested in disabling `in` / `out`. |
| 71 | + |
| 72 | +#### Considered alternatives |
| 73 | + |
| 74 | +The 4 values available to the user are voluntarily simple and hierarchical. It makes little sense to allow any contracts without enabling asserts. |
| 75 | +It would be feasible to allow `invariant` without `in` and `out` contracts, or only `out` contracts, only `in` contracts, or some other combination. |
| 76 | +Since providing all combinations would increase complexity, but doesn't yet provide an obvious advantage, it was left out of this proposal (but can be subject to another one). |
| 77 | + |
| 78 | +Since this proposal was heavily motivated by the cost of invariant, an obvious alternative is to reduce said cost. |
| 79 | +However, the cost of having `invariant` enabled will never be null for builds that do not use invariants at all, which is the real motivation for this feature. |
| 80 | + |
| 81 | +Finally, this functionality is already implemented in LDC via `-enable-invariants={0,1}`. |
| 82 | +Standardizing it would simplify user's life and allow tooling that deals with multiple compilers (e.g. `dub`, IDEs...) to provide this option. |
| 83 | + |
| 84 | +### Breaking changes / deprecation process |
| 85 | + |
| 86 | +Since this behavior is entirely opt-in, no breaking change is expected. |
| 87 | + |
| 88 | +### Implementation difficulty |
| 89 | + |
| 90 | +The reference front-end already segments those functionality into separate flags, hence the implementation should be trivial. |
| 91 | + |
| 92 | +## Copyright & License |
| 93 | + |
| 94 | +Copyright (c) 2016 by the D Language Foundation |
| 95 | + |
| 96 | +Licensed under [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) |
| 97 | + |
| 98 | +## Review |
| 99 | + |
| 100 | +Will contain comments / requests from language authors once review is complete, |
| 101 | +filled out by the DIP manager - can be both inline and linking to external |
| 102 | +document. |
0 commit comments