Establish toolchain and language versioning (#4105)

Proposal for how Carbon version numbers work:

- A single version across language, standard library, compiler, linker,
etc.
-   Semantic Versioning (SemVer) based
- Details of how SemVer criteria for major, minor, and patch should
apply to
    Carbon
- Details of how we will operate before 1.0 and how this connects to
Carbon's
    milestones
- Directional guidance for future work including post-1.0 versions, LTS
    versions, and standardization

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Chandler Carruth
2024-07-14 06:28:46 +00:00
committed by GitHub
co-authored by Richard Smith
parent 99696b9812
commit 2fcff24100
2 changed files with 575 additions and 0 deletions
+270
View File
@@ -0,0 +1,270 @@
# Toolchain and language versioning
<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
[Pull request](https://github.com/carbon-language/carbon-lang/pull/4105)
<!-- toc -->
## Table of contents
- [Overview](#overview)
- [Major version increments](#major-version-increments)
- [Breaking changes](#breaking-changes)
- [Exclusions to what constitutes a breaking change](#exclusions-to-what-constitutes-a-breaking-change)
- [Minor version increments](#minor-version-increments)
- [Patch version increments](#patch-version-increments)
- [Examples:](#examples)
- [Pre-release versions](#pre-release-versions)
- [Release qualification pre-releases: `MAJOR.MINOR.PATCH-rc.N`](#release-qualification-pre-releases-majorminorpatch-rcn)
- [Incremental development versions: `MAJOR.MINOR.PATCH-0.{nightly,dev}.N`](#incremental-development-versions-majorminorpatch-0nightlydevn)
- [Nightly pre-release versions](#nightly-pre-release-versions)
- [Development pre-release versions](#development-pre-release-versions)
- [Relevant proposal](#relevant-proposal)
<!-- tocstop -->
## Overview
Carbon uses a single versioning scheme across both the language itself and
toolchain, including the standard library, compiler, linker, and all development
tools released by the main Carbon project.
The scheme conforms to and is closely based on Semantic Versioning
(https://semver.org/ -- version 2.0.0):
- Carbon versions: `MAJOR.MINOR.PATCH`
- Releases with backwards incompatible changes, including a deprecation that
might trigger a build-breaking warning, increment `MAJOR` after reaching the
`1.0` milestone, and increment `MINOR` before then.
- Releases with only backwards compatible changes are not expected to happen.
- Releases containing only bug fixes increment `PATCH`.
- Pre-release suffixes:
- `MAJOR.MINOR.PATCH-rc.N`: The N-th potentially viable candidate for a
release.
- `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD`: A nightly incremental
development build on a particular day during development of that
version.
- `MAJOR.MINOR.PATCH-0.dev`: An interactive, incremental development build
on a particular day by some developer during development of that
version.
See the sections below for the details of each aspect of these versions.
## Major version increments
Aligned with SemVer, the major version must increment for any _breaking change_
to the language or any part of the toolchain.
The first increment from 0 to 1 is expected to be based on achieving a desired
milestone of feature completeness and quality to declare the language to have
reached a stable version.
Subsequent increments are expected to be done with a time-based release
strategy. Features (or breaking changes) ready to ship will do so, and others
will wait for the next major release. The exact cadence used is future work and
should be determined based on discussions with Carbon's users in the ramp up to
and after reaching the 1.0 milestone.
However, just because we increment the major version for a major release and
_can_ make breaking changes doesn't mean we _will_ or _should_. Breaking
language changes are extraordinarily expensive for users due to the inherent
scale of churn they impose. It is tempting to try to make non-breaking releases
instead, but our experience with C++ language, compiler, and standard library
updates is that truly making no breaking changes is extremely difficult and
overly constraining. We expect most releases, especially in the early phases of
the language to involve _some_ amount of breaking change and will simply work to
make these as cheap to upgrade through and minimal as we can.
At some future point, it is possible that Carbon will become so stable that it
makes sense to consider using minor version increments for some releases. If and
when this happens, we should revisit our versioning and release policies to
establish a predictable and unsurprising structure.
### Breaking changes
Beyond traditional breaking API changes in either standard library APIs or tool
APIs, Carbon also includes breaking changes in the language or toolchain.
Language and toolchain breaking changes are any that cause correct, functioning,
and non-reflective code to become invalid, rejected, incorrect, or silently
change behavior.
### Exclusions to what constitutes a breaking change
Carbon excludes "reflective code" which is in some way detecting the Carbon
version, or the presence or absence of features and as a consequence can be
"broken" by detecting changes that are correctly designed to otherwise be
non-breaking. We don't want adding features to be considered a breaking change
and so exclude code that specifically detects such additions.
Carbon also excludes breaking changes to incorrect code unless it was accepted
and functioning in some useful, and typically widespread, way despite its bugs.
## Minor version increments
Currently, Carbon plans to primarily use the minor version increments with a 0
major version to track our progress towards our 1.0 milestone of a feature
complete initial language. As a consequence we have defined 0.1 and 0.2
milestones and may define more steps as needed.
Beyond this and in a post-1.0 language world, we expect most significant
features to also accompany some small and manageable breaking changes from
deprecations. We may choose to revisit this in the future, but our current plan
is not to make minor version releases post-1.0 and instead focus on our
commitment to making those updates both easy and scalable for language users.
## Patch version increments
The patch version will increment when the change is fundamentally a bug fix to a
previously released version. We expect the vast majority of these to be strictly
backwards compatible bug fixes.
Patch releases are expected to be driven by demand, and not necessarily present
if unnecessary. However, the exact schedule and process will be determined as
part of getting ready for the 1.0 milestone. Before that milestone we don't
commit to any particular process or cadence for patch releases as no release
before that point should be considered stable.
Note that we still consider restoring the _intended_ "public API" of a release
to be a bug fix. When these bug fixes theoretically break new code in a way that
would typically require a major version increment, they may be made with merely
a patch version increment when they are in fact restoring our intended behavior
for that release. However, we take the SemVer guarantees very seriously as these
fixes can still be disruptive and so we work to hold a high bar for them:
- They must be in some way fixing a _regressions_ for users from a previous
release, not merely a missing feature.
- Can even be a regression in the overall cohesion or reliability of the
language or tools, which a bug in a new feature might erode.
- Key is that we motivate any patch fix through the lens of a regression
fix and stabilization rather than fixing forward.
- The scope of disruption from the fix is demonstrably small, due to some
combination of:
- The short time duration of the release containing the regression.
- The narrow scope of code constructs potentially impacted.
- The impact of the regression is large and cannot be easily worked around,
for example:
- Undermining a core priority of the Carbon Language for a significant set
of users.
- Making the engineering cost of adopting the release uneconomical for any
significant body of users.
### Examples:
- We add a new feature that includes a bug which creates unsoundness and
allows incorrect code to be compiled that will crash or exhibit UB when
executed.
- While this is a new feature and not a bug in an existing feature, it
would be a _serious_ regression to the reliability of the language as a
whole and the ability of users to reason about the correctness of
programs.
- This would be a good candidate for a patch release to address unless it
is found very late (months) after the release _and_ the only ways to
address would have similarly bad effects on code written since the
initial release.
- Even that level of user disruption could potentially be overcome if for
example the bug led to security vulnerabilities.
- We add a narrowly used new feature that includes a bug where some code
patterns that _should_ work with the feature are rejected at compile time or
crash reliably.
- Unlikely this is worth a patch release to make an invasive change given
the narrow use case.
- A good candidate to introduce a warning or error on using the feature in
the way that might lead to a crash, and possibly on using the feature at
all.
- We add a compiler opt-in feature behind a flag that doesn't work reliably.
- Good candidate to have the flag disabled or trigger a warning message.
- Not a good candidate to try to fix the feature forward.
- We add a compiler opt-out feature that doesn't work reliably.
- What to do likely depends on the scope of users impacted. If _very_ few
users impacted, possibly just document how to opt-out.
- If enough are impacted to be a nuisance and a regression in experience
in general, likely worth attempting a patch release that narrowly fixes
or mitigates the issue.
## Pre-release versions
Beyond the major, minor, and patch versions of an actual release, SemVer
provides a foundation for pre-release version management. However, it is a very
open-ended foundation with a wide range of possibilities and no particular
semantic model provided. Some of this is to support the wide variety of
different needs across different projects. It also reflects the fundamentally
less crisp and well defined criteria needed for pre-releases.
That said, Carbon should try to have a small and well articulated scheme of
pre-release versions to help communicate what these releases do and don't
constitute and how they should be interpreted. These are selected to provide a
framework that allows pre-release versions to order in a reasonably cohesive way
when compared using SemVer.
In descending order, Carbon pre-releases may use:
- `MAJOR.MINOR.PATCH-rc.N`
- `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD`
- `MAJOR.MINOR.PATCH-0.dev`
We expand on each of these below to provide criteria and details.
### Release qualification pre-releases: `MAJOR.MINOR.PATCH-rc.N`
We create release candidate or "rc" pre-releases when we believe that version to
be complete and ready to release and want to collect feedback. There should not
be interesting or significant gaps, even known ones, from the intended release.
The expectation should always be that unless some feedback arrives to the
contrary, the release candidate could simply become the release.
For each pre-release category, we always suffix with a sequential count `N` of
the pre-releases at that version. We must start with a `.0` in order to have
subsequent iterations of the same version and category of pre-release to sort
after the first.
### Incremental development versions: `MAJOR.MINOR.PATCH-0.{nightly,dev}.N`
These are versions that are not in any way associated with the actual expected
release, but are periodically produced as an incremental tracking of development
progress. Because they are not expected to be part of qualifying a specific
release, they're not defined by any particular criteria of completeness or
readiness. In practice, these will occur at every stage between one release and
the next.
#### Nightly pre-release versions
These are automated incremental development versions built each night when the
automated testing passes. There is _no_ attempt to provide any higher quality
bar or refinement than whatever was in the tree at the time the automation runs,
and whatever automated tests are present pass.
It is important to emphasize that the primary use case of these pre-release
versions is not to evaluate a potential release but for the developers and
contributors to Carbon itself to track incremental development progress. That
development-oriented goal drives how they are built and what they do and don't
provide.
Mechanically, we prefix the `nightly` pre-release version component with a `0`
component to ensure these versions sort before any and all release qualification
pre-release versions. We also add a date-derived suffix to provide a rough
chronological ordering of nightly builds with otherwise identical versions.
#### Development pre-release versions
During development, interactive builds of Carbon need to be versioned in an
unambiguous way, and we do that with the `dev` pre-release version. Much like
nightly versions, these are only produced as artifacts of development activities
and are never part of any actual release process. These versions are further not
built expected to be automated or necessarily repeatable. They may contain
in-flight edits and all manner of other variations.
Mechanically, we prefix the `dev` pre-release version component with `0` in the
same way as `nightly` is prefixed to ensure effective ordering. We don't add any
additional information to keep the mechanics of development builds simple -- for
example, there is no easy way to extract the date of the build. The exact
timestamp of the build may be available but doesn't participate for simplicity
and minimizing cache impact.
## Relevant proposal
- [Proposal p4105](/proposals/p4105.md)
+305
View File
@@ -0,0 +1,305 @@
# Establish toolchain and language versioning
<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
[Pull request](https://github.com/carbon-language/carbon-lang/pull/4105)
<!-- toc -->
## Table of contents
- [Abstract](#abstract)
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Directional sketch for the future](#directional-sketch-for-the-future)
- [Language evolution and breaking changes](#language-evolution-and-breaking-changes)
- [Long-Term Stable (LTS) versions and standardization](#long-term-stable-lts-versions-and-standardization)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)
- [Do nothing, or just talk about a minimal nightly version.](#do-nothing-or-just-talk-about-a-minimal-nightly-version)
- [Make no breaking changes past 1.0.](#make-no-breaking-changes-past-10)
- [Version different parts of the language separately.](#version-different-parts-of-the-language-separately)
- [Use a custom versioning scheme rather than SemVer.](#use-a-custom-versioning-scheme-rather-than-semver)
- [Include more pre-release variations](#include-more-pre-release-variations)
<!-- tocstop -->
## Abstract
Proposal for how Carbon version numbers work:
- A single version across language, standard library, compiler, linker, etc.
- Semantic Versioning (SemVer) based
- Details of how SemVer criteria for major, minor, and patch should apply to
Carbon
- Details of how we will operate before 1.0 and how this connects to Carbon's
milestones
- Directional guidance for future work including post-1.0 versions, LTS
versions, and standardization
## Problem
We need a versioning scheme for Carbon both for the language and the reference
toolchain implementing that language. This is important even before we reach any
specific milestone, as we want to define the schema and implement it _before_ it
becomes useful for marking specific milestones.
## Background
- [Semantic Versioning](https://semver.org/)
- [Rust Editions](https://doc.rust-lang.org/edition-guide/editions/)
## Proposal
First, Carbon should have a single versioning scheme across both the language
itself and toolchain, including the standard library, compiler, linker, and all
development tools released by the main Carbon project.
Second, the Carbon versioning scheme should conform to and be closely based on
Semantic Versioning (SemVer), which is the de-facto standard for versioning
schemes in software today. Beyond that, it needs to clarify how the standards
laid out in SemVer map into a programming language context as programming
languages and standard libraries have an extraordinarily broad and tightly
coupled "API" to their users -- all of the source code written in the language.
Carbon needs to provide extra clarity around what constitutes our "public API"
for SemVer purposes and the criteria for changes.
Third, SemVer provides a schema for pre-release versions, but is largely
open-ended on their semantics. Carbon should have a specific set of well defined
pre-release versions with clearly communicated purpose, nature, and meaning to
avoid confusion.
Fourth, language versioning is an especially important area for the long-term
evolution and so Carbon should have some directional guidance around the future
work expected in the versioning front. This should speak to specific use cases
and needs that may be left seemingly unaddressed otherwise.
Summarizing the proposed outcome of these together:
- Carbon versions: `MAJOR.MINOR.PATCH`
- `MAJOR` increments on backwards incompatible changes, including a
deprecation that might trigger a build-breaking warning.
- Doesn't make it free to make such changes. Each change must pay for its
adoption and churn cost. But when a change is needed and well justified,
this signifies its introduction.
- Used to establish our milestones for the language.
- Some explicit carve-outs of things designated to not be in the "public
API" of the language.
- `MINOR` increments only expected during early development with a major
version of `0`.
- If Carbon some day stabilizes sufficiently to motivate it, we may
revisit this and begin to use the minor version number to signal
backwards compatible releases.
- `PATCH` increments represent bug fixes only.
- Goal is always fully backwards compatible.
- When fixing the bug makes code written against the release with the bug
break, may be unavoidable as the intent was never the buggy behavior.
But this is rare and we hold a very high bar for such bug fixes due to
their disruptive nature.
- Pre-release suffixes:
- `MAJOR.MINOR.PATCH-rc.N`: The N-th potentially viable candidate for a
release.
- `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD`: A nightly incremental
development build on a particular day during development of that
version.
- `MAJOR.MINOR.PATCH-0.dev`: An interactive, incremental development build
on a particular day by some developer during development of that
version.
## Details
See the added [versioning document](/docs/project/versioning.md).
## Directional sketch for the future
The mechanics outlined above provide a good basis for the initial versions of
the language (up to 1.0) and any necessary mechanics and tooling around those
versions. However, beyond 1.0 we expect the needs of the language and project to
expand and more detailed versioning and evolution tools to become critical. We
lay out directional sketches here for where Carbon should go in the future to
address these needs, but these are just directional guidance and will need their
own carefully considered proposals when the time comes.
### Language evolution and breaking changes
We don't expect a simple version number to be sufficient long-term for the
evolution needs of the Carbon language. We should plan to at least map these
major versions into Rust-edition-like controls within source code itself to
allow incremental adoption across a codebase of fixes for breaking changes or
adoption of new language features with a single toolchain version. That is, some
code will want to compile using previous major version semantics even with the
new compiler.
The approach taken in Rust and proposed for C++ to address this are "editions"
that source code opts into in order to allow the compiler to support a mixture
of code in a codebase during incremental adoption. Carbon will need at least
something equivalent to this, and may want to explore a more fine-grained system
of opting into specific functionality sets similar to how pragma-based extension
usage or Circle works.
Regardless of the specifics, a key is that breaking changes are not forcibly
coupled in their roll-out to updates to the Carbon toolchain. Each step needs to
be incrementally tackled.
### Long-Term Stable (LTS) versions and standardization
SemVer alone isn't sufficient to address some user needs for language stability.
It is enough to _detect_ the issues when they arise, but Carbon should also plan
for how to _address_ these issues.
The suggested direction here is towards designated LTS versions based on a
particular level of completeness and quality and the user demand. These versions
will likely need even longer time horizons of support than Linux distro LTS
releases. The direction should be to embrace this and the potential for
multi-decade support windows to support users' needs. As the windows of LTSes
expand, their frequency should reduce to avoid supporting an unsustainable
diversity of versions.
Exactly how a version is designated as LTS is left to the future work here, but
it should not be expected to change the schema and structure of the versioning,
just the support policy applied to the specific release version in question.
Some users may even require standardization of a programming language to make it
usable in their environment. Carbon should again embrace this need and see the
standardization as an analogous process to promoting a normal release into an
LTS. Some relevant and effective LTS should be selected and taken through
whatever process is identified to create a standard reflecting that LTS version
of Carbon. Updates to the standard should in turn track as updates to a newer
LTS. The specifics of how to do this are left to the future work, and they may
change exactly how this works.
Note that the goal of this future direction isn't to constrain how Carbon can
arrive effectively at either an LTS release or a standard. Instead, the goal is
to make it clear that we _should_ be open and planning to achieve these in order
to meet the needs of candidate Carbon users.
## Rationale
- [Language tools and ecosystem](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#language-tools-and-ecosystem)
- Carbon needs a coherent versioning scheme across the language itself as
well as its ecosystem. Especially as the language is developing rapidly,
being able to sync across all of these with a single, simple versioning
scheme is especially important to have the tooling and ecosystem agree
about features of the language.
- [Software and language evolution](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#software-and-language-evolution)
- Users of Carbon need a clear model for understanding the language's
evolution, tracking it, and responding to significant aspects of it.
- The versioning scheme needs to support a wide variety of versions built
in the process of evolving the language without creating confusion.
## Alternatives considered
### Do nothing, or just talk about a minimal nightly version.
Advantages:
- Carbon is at a very early stage and is a long way from needing release
candidates for stable releases or incrementing its major version numbers.
- No need to imagine distant future scenarios.
Disadvantages:
- We already discuss version numbers in various places in the Carbon project,
including our [milestones](/docs/project/milestones.md) and
[roadmap](/docs/project/roadmap.md).
- Establishing how the Carbon project will communicate its updates with
version number changes in advance of those updates makes that communication
more effective.
- Lets us telegraph our intentions and how we are thinking about releasing and
cadence to the community and get earlier feedback.
A key to adopting the more detailed versioning plan is that we can change any
and all of this if and when we need to. This does not lock Carbon into using
this exact versioning scheme. We will listen to any feedback from potential
users and can adapt our approach if needed.
### Make no breaking changes past 1.0.
Advantages:
- Stable languages have the lowest churn costs for users, and are easier to
learn at scale.
Disadvantages:
- This would be in direct opposition to our language goal of supporting
software and language evolution.
- This relieves pressure on adding things to the language by avoiding an
unreasonably high bar. The pressure is still large due to the very real
churn costs, but we avoid amplifying that further with an absolute
restriction on fixing issues.
- We expect Carbon to be a reasonably complex language in order to succeed
at its goals, ranging from C++ interop to incremental memory safety.
This complexity inherently comes with an increased risk and importance
of being able to improve and fix issues.
### Version different parts of the language separately.
Advantages:
- There will be eventually be many differences between changes to the
toolchain and changes to the language itself. We might be able to capitalize
on those to have a better cadence or versioning scheme for these
independently.
Disadvantages:
- We would have to carefully define and maintain a compatibility matrix
between the different components. Increasingly in modern languages and
development, the compiler, language, standard library, and tooling are all
deeply interdependent.
- Experience rolling out major updates to Clang and GCC in large codebases and
software ecosystems show even compiler-only or toolchain-only changes easily
become as disruptive as smaller updates to the C++ language itself have been
over the years. As a consequence, while it is tempting to hope for a sharp
difference here we don't in practice anticipate one.
### Use a custom versioning scheme rather than SemVer.
Advantages:
- We don't actually use all parts of SemVer, which results in awkward unused
component of our version number.
- SemVer doesn't actually provide an opinionated versioning scheme, merely a
relaxed schema that many versioning schemes can fit into.
Disadvantages:
- Fitting into SemVer ends up needing only very cosmetic changes to a scheme
that is meaningful for Carbon. And we can easily specify the open-ended
parts of the scheme.
- Allows us to easily fit into scripts and people's understanding using SemVer
as a baseline model.
- Avoids confusion when people from outside the community first encounter
Carbon versions as their most likely intuition about the meaning of various
aspects of the number will be a reasonable starting point.
### Include more pre-release variations
Initially there was a discussion of potentially defining `alpha` and `beta`
pre-release versions along with release candidate versions, nightly, and
development versions. The specific idea was to document versioning we could use
for longer-lived releases we want to make when not yet ready to call it a
release candidate.
Advantages:
- If we end up wanting longer-lived releases prior to arriving at a numbered
milestone such as `0.1`, this would provide a ready-made solution.
- Specifically, communicating the option for this early might help others
avoid being confused by what exactly the state of such a pre-release would
be.
Disadvantages:
- There's no real indication we would ever want to make such a release. It
seems easy to imagine the combination of nightly builds and pre-releases
completely covering all of the use cases we end up with in practice.
- Keeping the infrastructure and documentation for such improbable use cases
is drag and friction that doesn't buy us enough to be worthwhile.