ISO/IEC JTC1 SC22 WG21 P3071R0
Jens Maurer <Jens.Maurer@gmx.net>
Target audience: SG21
2023-12-10

P3071R0: Protection against modifications in contracts

Introduction

The expression that is checked for a true result in a contract annotation is supposed to observe the state of the program, but not change that state, exceptions such as logging notwithstanding. The current state of discussion of the contracts facility in SG21 offers little compile-time protections against accidental modifications. This paper proposes to add those by making id-expressions referring to local variables and parameters be const lvalues. Also, implicit or explicit references to this are as-if inside a const member function.

Example (only the lines marked "proposal" would change):

int global = 0;

int f(int x, int y, char *p, int& ref)
  pre(x = 0)                   // proposal: ill-formed assignment to const lvalue
  pre(*p = 5)                  // OK
  pre(ref = 5)                 // proposal: ill-formed assignment to const lvalue
  pre(std::same_as_v<decltype(ref), int&>)  // OK; yields true
  pre(global = 2)              // OK
  pre([x] { return x = 2; }())           // error: x is const
  pre([x] mutable { return x = 2; }())   // OK, modifies the copy of the parameter
  pre([&x] { return x = 2; }())          // proposal: ill-formed assignment to const lvalue
  pre([&x] mutable { return x = 2; }())  // proposal: ill-formed assignment to const lvalue
  post(r: y = r)               // error: y is not declared const
{
  contract_assert(x = 0);      // proposal: ill-formed assignment to const lvalue
  int var = 42;
  contract_assert(var = 42);   // proposal: ill-formed assignment to const lvalue

  static int svar = 1;
  contract_assert(svar = 1);   // OK
  return y;
}

Proposal

Specifically, this paper proposes:

A contract context is the conditional-expression of a contract-condition, where the grammar non-terminals are as defined in P2961R1 (A natural syntax for Contracts).

  1. An id-expression that is a subexpression of a contract context and names a variable with automatic storage duration of object type T, or a structured binding of type T whose corresponding variable has automatic storage duration, is an lvalue of type const T.
  2. An id-expression that is a subexpression of a contract context and names a variable with automatic storage duration of type "reference to T" is an lvalue of type const T.
  3. When a lambda-expression that is a subexpression of a contract context captures a non-function entity by copy, the type of the implicitly declared data member is T, but (as usual) naming such a data member in the compound-statement of the lambda-expression yields a const lvalue unless the lambda is declared mutable. When a lambda-expression captures such an entity by reference, the type of an expression naming the reference is const T.
  4. The primary-expression this, when appearing as a subexpression of a contract context (including as the result of the implicit transformation in the body of a non-static member function), is a prvalue of type "pointer to cv X", where cv is the combination of const and the cv-qualifier-seq of the enclosing member function (if any).

Discussion

The following lists arguments in favor of the proposed change and those against, as a summary of earlier e-mail exchanges.