DIP 1028---Make @safe the Default---Community Review Round 1
Joseph Rushton Wakeling
joseph.wakeling at webdrake.net
Mon Jan 13 15:57:38 UTC 2020
On Thursday, 2 January 2020 at 09:47:48 UTC, Mike Parker wrote:
> This is the feedback thread for the first round of Community
> Review for DIP 1028, "Make @safe the Default":
>
> https://github.com/dlang/DIPs/blob/1b705f8d4faa095d6d9e3a1b81d6cfa6d688554b/DIPs/DIP1028.md
First, @Walter, thanks for writing this, and @Mike, thanks for
taking care of the review process :-)
Since I'm co-author of some key bits of the current reviewer
guidelines, I'll try to follow the questions there fairly
precisely and see if it makes for some nice structure in my
feedback.
(1) Is the proposal acceptable as a language change in principle?
Clearly yes: @safe-by-default fits within an observable trend of
languages getting more and more concerned with provable memory
safety, and with a related trend of requiring the user to write
provably safe code. Rust in particular has significantly raised
the bar in this space, and proven that such constraints are often
welcomed by developers.
The should we/shouldn't we questions around this are likely to
come down to practicalities.
However, this does seem like a fairly major philosophical shift
in the language design. Up until now D has (possibly only
implicitly) followed what amounts to a "permissive by default"
design philosophy where (apart from really obvious stupidities)
the default setting is that one can do what one likes, and
stronger design constraints (including memory safety but also
e.g. constness and immutability, purity, nogc, nothrow, final
methods, ...) are readily available but all opt-in.
The existence of a parallel DIP for nothrow-by-default seems to
confirm this push for a more restrictive-by-default language.
The two DIPs should be considered on their own merits, so I won't
try to couple them, beyond noticing the design trend they both
contribute to. However, the impact of that trend should be
considered in terms of the impact on the D development
experience. Some thoughts on pros and cons will be covered below.
As a separate issue, it's probably a good idea to take discussion
of keyword choices off the table for the purposes of this DIP.
@safe, @trusted and @system have
(2) Is the proposal workable in practice?
-- "Is the proposed feature specified in sufficient detail?" --
There is sufficient spec to make the change happen, per se. What
is missing is a sufficiently detailed overview of the practical
problems that will arise from the migration, and explicit
proposals for how to deal with them. This brings us to ...
-- "Are edge cases, flaws, and risks identified and addressed?" --
The identified breaking changes and risks are discussed only at a
high level. The proposed solutions (explicit annotation of all
non-templated functions) are correct per se but don't really
capture the complexity of the likely reality. Some examples:
* @system-by-default means that if a @safe attribute is
overlooked,
that bug can be fixed without breaking change: by contrast,
with
@safe-by-default, an overlooked @system attribute can't be
fixed
without breaking change to the API concerned.
(Yes, I can hear you now saying that if the function isn't
@safe
then the compiler will object and force the user to add
@system,
so this oversight won't happen. But this is about intent: the
function might be safe in practice, as initially implemented,
but with no intention to preserve that guarantee. One
advantage
of opt-in constraints is that generally one can be sure that
the
developer means them to be there.)
* As noted already in others' feedback, the use of `@system:`
to tag
multiple functions has an asymmetric impact compared to
`@safe:`.
In the latter case one likely _wants_ that attribute to apply
to
all subsequent functions, including templated ones. However,
we
probably do not want catch-all `@system:` to override the
inferred
safety of templated function instantiations. This edge case
should
be discussed, with suggestions for how to address it.
* The DIP contains no advice or impact assessment for the case
of
3rd-party libraries that are no longer actively maintained,
and
the consequent risks for obsoleting a large amount of existing
D code. Ideally the DIP should contain a robust estimate of
the
numbers of projects this might impact, and some discussion of
the
pros and cons of that impact, and mitigation strategies.
The migration plan should include clear steps for how to
regularly
remeasure the expected impact on 3rd-party library usability
over
time (e.g. as more and more libraries are adapted to support
the
new feature). There should also be explicit criteria for
deciding
on what level of impact is (un)acceptable in order to
transition
from `--preview=safedefault` to the feature being on by
default.
* Issues related to taking the address of local variables
(mentioned
several times in this discussion thread) should be discussed,
with
reference to other DIPs that address that concern. It should
be
made clear whether finalization of those other features is
needed
(or strongly desirable) to finalize @safe-by-default.
* The impact of @safe-by-default on `extern` APIs should be
discussed.
We have no reasonable grounds to assume these functions are
safe by
default: the DIP should address how to deal with this (which
should
ideally not rely on developer virtue).
* The impact of @safe-by-default on the ability to write
`@nogc` code
should be covered by the DIP, including appropriate
references to
other relevant DIPs.
* The advice in the current draft of the DIP to "annotate
functions
that aren't safe with `@trusted` or `@system` should include
clear
guidance as to _when_ to use `@trusted` and when to use
`@system`.
We don't want to "fix" migration problems by blindly slapping
`@trusted` onto code that hasn't been properly validated.
We have already had cases of people trying to do that just to
get
stuff to compile with `@safe`
<https://github.com/msoucy/dproto/pull/79>
so we should try to avoid the risk of spreading that kind of
code
around. (Rust has to deal with similar concerns, of too many
devs
just adding `unsafe` blocks willy-nilly to get the compiler
off
their backs, and I've seen similarly problematic uses of
@trusted
in even some quite prominent D libraries.)
* It seems likely that @safe-by-default will increase the
number of
occasions that developers have to use @trusted. The DIP
should
try to make some estimate of the amount of impact, and should
address whether it is necessary (or at least very desirable)
to
add support for @trusted code blocks as well as functions (cf.
what Rust allows with `unsafe`, and feedback by Manu and
others
on the problems of needing to define local lambdas to apply
the
@trusted attribute to).
These last few examples touch on another missing risk: there is
no assessment of the expected impact on developer experience.
Arguably a very nice productivity feature of D is the ability to
hack readily and only worry about introducing strict constraints
when one actively wants them. This is part of what makes D so
readily usable for everything from small casual scripts to
large-scale libraries and applications.
Those of us who tend to apply `@safe` willingly and regularly may
underestimate the impact on users who prefer fast iteration over
strictly enforced constraints. I'm particularly concerned that
it may get too many developers into the habit of unthinkingly
slapping down `@trusted` on code that doesn't deserve it, rather
as some Rust developers just `unsafe` lots of things without
really thinking it through.
It's easy to dismiss those people as architects of their own
pain, but the problem is how such users can spread bad habits by
example. We should perhaps not underestimate the importance of
consent in submitting to constraints ... :-)
OTOH the positive flipside of imposing constraints by default is
that it means that the combination of different constraints gets
much better battle tested: there is a much lower barrier to
discovering (and hopefully fixing) tricky edge cases.
-- "Are there any platform or architecture pitfalls to be aware
of?" --
None that I can think of.
-- "Is there an implementation that proves the proposed feature
works in practice?" --
The basic implementation is likely trivial. Questions of proof
need to apply more to the migration path (see below).
-- "Does the DIP consider prior work from other languages?" --
Yes, but the consideration is merely of their existence. It
would be good to have a more detailed comparison, discussing how
they achieve those outcomes, and what the resulting constraints
and developer experiences are.
-- "If the proposed feature is a breaking change, is there a
well-defined migration path?" --
This is the crux of the matter. There are many small finnicky
impacts that this change will have.
The basic migration path of using `--preview=safedefault` and
ironing out kinks is sound. However, what needs to be
established (which is currently missing) is a clear statement of
the criteria that will be used to determine when (and if!) it is
appropriate to transition from the `--preview` feature to having
@safe-by-default ... by default.
In short, acceptance of moving to the `--preview=safedefault`
stage should NOT be taken as acceptance of the @safe-by-default
transition in its entirety. The DIP should define the definite
blockers to that transition, and should outline a robust review
process for the decision to finalize (or abandon) the change.
The core code migration step (adding `@system` to non-templated
functions without an existing `@safe`, `@trusted` or `@system`
attribute) ought to be possible to automate: the DIP might
mandate the creation of such a tool as a requirement before the
transition can be finalized.
(3) Summary
The proposed feature is a significant breaking change. If we are
lucky, the practical impact may be much smaller than one might
anticipate, but that needs to be robustly established before
approval can be given to the DIP (or at least, to transitioning
away from `--preview=safedefault`).
The DIP needs to provide much more detail on the anticipated
impacts, the migration paths, and the risks for the existing and
future D ecosystem (with particular attention to how many
existing codebases may be obsoleted). It should also clarify if
any other DIPs or experimental features need to be finalized
before this DIP can be.
The anticipated impact on both developer and maintainer
experience should be carefully outlined, with clearly written
mitigation strategies for the worst pain points.
In short: since nothing stops anyone who cares from having a
`@safe` codebase right now through the existing opt-in features,
show us in detail how the pain of transitioning to opt-out is
really worth it ;-)
More information about the Digitalmars-d
mailing list