I propose adding compile-time boolean assertions to Go.
[I don't feel strongly about this proposal, but it seems pretty minimal; easy to implement; and to make some real world code somewhat easier to read/write. I've also not found any past discussion of this idea, so it seemed worth at least writing down even if rejected.]
Proposal
Concretely, I propose making these changes:
-
Introduce a new "assert" package like:
package assert
type True bool
-
Add a language rule that it's an error to have a constant of type assert.True but value false.
-
(Optional) Add a language rule that it's an error to use assert.True except as the type of a constant.
Uses
There are somewhat common idioms of writing:
const _ = -uint(x - y) // assert x == y
const _ = uint(x - y) // assert x >= y
But I at least find these awkward to reason about, even being very familiar with the details of how they work.
With this proposal, they could instead be written more clearly as:
import "assert"
const _ assert.True = x == y
const _ = assert.True(x >= y)
(Showing off two different ways to write const declarations using assert.True.)
Further, generalizing to boolean expressions allows us to easily use boolean operators to combine multiple tests. It also potentially allows static assertions involving non-integer constants (i.e., floats, complex, bools, and strings).
For example, package gc's sizeof_test.go could be rewritten as compile time asserts like:
const (
ptrSize = unsafe.Sizeof((*int)(nil))
funcSize = unsafe.Sizeof(Func{})
_ = assert.True((ptrSize == 4 && funcSize == 116) || (ptrSize == 8 && funcSize == 208))
)
Backwards compatibility
assert.True doesn't exist today, so there's no code using it that we have to worry about.
Old tools unaware of the special semantics for assert.True (e.g., old compilers or tools using go/types) will continue working for old code. They'll also continue working correctly for new code that correctly use assert.True. The tools will, however, fail to detect failing assertions.
Related proposals
#9367 proposed allowing bool->int conversions, which be an alternative way of extending the current integer static assertions idiom to arbitrary boolean static assertions. However, it would still be somewhat awkward to read/write.
#30582 proposes an assertion to indicate unreachable code paths. Technically orthogonal to this one, but it might be worth ensuring they expose a consistent API to users.
C++11 added static_assert: https://en.cppreference.com/w/cpp/language/static_assert (Counter argument: C++11 has templates and constexpr, which make static_assert more broadly useful than assert.True would be.)
I propose adding compile-time boolean assertions to Go.
[I don't feel strongly about this proposal, but it seems pretty minimal; easy to implement; and to make some real world code somewhat easier to read/write. I've also not found any past discussion of this idea, so it seemed worth at least writing down even if rejected.]
Proposal
Concretely, I propose making these changes:
Introduce a new "assert" package like:
Add a language rule that it's an error to have a constant of type assert.True but value false.
(Optional) Add a language rule that it's an error to use assert.True except as the type of a constant.
Uses
There are somewhat common idioms of writing:
But I at least find these awkward to reason about, even being very familiar with the details of how they work.
With this proposal, they could instead be written more clearly as:
(Showing off two different ways to write const declarations using assert.True.)
Further, generalizing to boolean expressions allows us to easily use boolean operators to combine multiple tests. It also potentially allows static assertions involving non-integer constants (i.e., floats, complex, bools, and strings).
For example, package gc's sizeof_test.go could be rewritten as compile time asserts like:
Backwards compatibility
assert.True doesn't exist today, so there's no code using it that we have to worry about.
Old tools unaware of the special semantics for assert.True (e.g., old compilers or tools using go/types) will continue working for old code. They'll also continue working correctly for new code that correctly use assert.True. The tools will, however, fail to detect failing assertions.
Related proposals
#9367 proposed allowing bool->int conversions, which be an alternative way of extending the current integer static assertions idiom to arbitrary boolean static assertions. However, it would still be somewhat awkward to read/write.
#30582 proposes an assertion to indicate unreachable code paths. Technically orthogonal to this one, but it might be worth ensuring they expose a consistent API to users.
C++11 added static_assert: https://en.cppreference.com/w/cpp/language/static_assert (Counter argument: C++11 has templates and constexpr, which make static_assert more broadly useful than assert.True would be.)