From 29928df5a01119616e906b6d5ee3067c02344a47 Mon Sep 17 00:00:00 2001 From: Mathias Lang Date: Tue, 3 Jan 2017 23:47:21 +0100 Subject: [PATCH] DIP1006: Providing more selective control over contracts The aim of this DIP is to provide a common approach to handle deployment of applications in non-release mode. --- DIPs/DIP1006.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 DIPs/DIP1006.md diff --git a/DIPs/DIP1006.md b/DIPs/DIP1006.md new file mode 100644 index 000000000..dd60f30b9 --- /dev/null +++ b/DIPs/DIP1006.md @@ -0,0 +1,102 @@ +# Providing more selective control over contracts + +| Field | Value | +|-----------------|-----------------------------------------------------------------| +| DIP: | DIP1006 | +| RC# | 0 | +| Author: | Mathias Lang - mathias.lang@sociomantic.com | +| Implementation: | None | +| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | + + +## Abstract + +A proposal to expose a way to selectively disable some of D's contract features +(invariant, `in` / `out` and `assert`). + + +### Links + +Limited D1 implementation, used by Sociomantic: https://github.com/dlang/dmd/pull/6347 + + +## Description + +Add a command-line switch, named `-contracts` in this proposal, which takes 4 possible values: + +- `all`: the default, present for completeness and scripts, so one can just append it to a generated command line to enable all contracts checks. +- `noinvariant`: Disable implicit `invariant` calls, but leaves function's `in` / `out` contracts and `assert` in. +- `assert`: Disable `invariant` blocks and `in` / `out` contracts, but leaves `assert` in. +- `none`: Disable all `invariant`, `in` and `out` contracts, and `assert`. + +Note that the `noinvariant` value only disables implicit calls to invariant. Explicit calls can only disabled by disabling `assert`. + + +### Rationale + +#### Prelude on contracts + +Under the broad name "Contracts", we regroup 4 of D features: `assert`, `in` contracts, `out` contracts and `invariant`. +Contracts in D are a mean to do sanity checking. A contract that fails is a sign of a faulty logic in the code. +Since contracts should never fail, the currently advertised approach for D programers is to use them during development, +then deploy a `-release` build of the application for maximum performances. + +Contracts naturally fit in a hierarchy, where `assert` is the most basic block (being used by others), +and `invariant` is meant for expensive sanity checking. `in` and `out` contracts are located in between. + +#### Cost of contracts + +`assert` and `in` / `out` contracts follow a pay-for-what-you-use approach: an `assert` which is never executed is free, +and a `in` or `out` contract on a function that is never called is also free of runtime overhead. + +`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)`), +but they are also implicitly called twice on every `public` and `protected` class method call. +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). +The code being very simple results in the class hierarchy to be traversed completely twice (no caching is done) +for every call, even for classes which do not define any invariant. + +Profiling some real-time applications within Sociomantic showed that `_d_invariant` was the most expensive call in the application. +Affected applications were using little or no invariant, but since Sociomantic code is mostly writen in an OOP style, +simply disabling invariants (with no other optimization) led to 20% more throughput. + +#### Issues with the current approach + +Testing can be complete and it is not rare to miss a scenario that can show up any time in a production setting. +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. +Example of such case include critical data corruption and unexpected monetary spending. + +For this reason, developers are often wary of disabling such safety features, even in production, as long as the performance cost is acceptable. +What can be viewed as an acceptable performance cost is up to the end user and the code base. +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`. + +#### Considered alternatives + +The 4 values available to the user are voluntarily simple and hierarchical. It makes little sense to allow any contracts without enabling asserts. +It would be feasible to allow `invariant` without `in` and `out` contracts, or only `out` contracts, only `in` contracts, or some other combination. +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). + +Since this proposal was heavily motivated by the cost of invariant, an obvious alternative is to reduce said cost. +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. + +Finally, this functionality is already implemented in LDC via `-enable-invariants={0,1}`. +Standardizing it would simplify user's life and allow tooling that deals with multiple compilers (e.g. `dub`, IDEs...) to provide this option. + +### Breaking changes / deprecation process + +Since this behavior is entirely opt-in, no breaking change is expected. + +### Implementation difficulty + +The reference front-end already segments those functionality into separate flags, hence the implementation should be trivial. + +## Copyright & License + +Copyright (c) 2016 by the D Language Foundation + +Licensed under [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) + +## Review + +Will contain comments / requests from language authors once review is complete, +filled out by the DIP manager - can be both inline and linking to external +document.