The goal here is to (significantly) reduce the boilerplate needed when defining classes that wrap enums, especially those managed with the `.def`-file style X-macros that are common in the toolchain.
This should also provide both better and more consistent functionality to those classes once ported over to it.
Initially, only `ParserState`, `SemanticsNodeKind`, and `SemanticsBuiltinKind` are ported as these were also the three that JonMeow ported in his original pull/2453 "option 5". This is heavily based on that version of the code.
Goals I was considering that influenced the design:
- Keep the individual enum-wrapping classes as simple and easy to read as possible. Especially important is keeping the `.def` files that are often filled with really important documentation clean and easy to maintain over time.
- Don't rely on computed `#include`s as that is an especially dark corner of the preprocessor and breaks some build systems.
- Have a really good API of the enum-wrapping class, including nice constant names for the values, easy printing, and even easy debugger-callable methods to get the name (as opposed to the integer value).
- Keep the API that users interact with in the base class as clean and easy to read as possible.
- Reduce the boiler plate for each instance of these as much as possible.
- Avoid excessive inline generated code or constants that would result in steady growth in object file sizes and linker effort doing deduplication.
These goals aren't always compatible, so we end up needing to pick a compromise between them when in tension. I think this version is a pretty good compromise.
The original version I started with already pull most of the API into a CRTP-style base class. This version pulls *all* of the common API. This is the main tool for getting consistency and avoiding duplication. However, connecting this base class to the individual enum wrappers is still difficult. Some specific changes here that try to do as much as possible there:
- Use a slightly fancier macro pattern to reduce the boilerplate of defining the raw `enum class` prior to the wrapper class.
- Use a macro to simplify naming the base class.
- Move the name table to a `.cpp` file to avoid every inclusion generating a complete copy of the strings (that the linker has to deduplicate). This is done with some care to sharply reduce the boilerplate needed in that `.cpp` file.
- Sink the name _API_ fully into the CRTP base class. This requires some significant complexity in the implementation, but all of that is hidden behind a single implementation detail macro, and the API itself is simple and readable. This also makes it much more reasonable to test the entire system a single time next to the base class.
This version also moves from constant factory functions to normal constants. This requires two batches -- first a declaration, and then a definition -- but the API result is significantly better and similar to the original option, the macro structure reduces the cost of these. Unfortunately that makes the adoption a bit noisy, but I think its worth the churn.
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Without this, some compile setups can get a missing symbol for llvm::raw_ostream; this is relying on ostream.h through other code paths at present, so should be explicit about it.
Features:
* Add basic support for `impl` virtual methods (override virtual method)
* Error on invalid declaration for `impl` and `virtual`, covering simple use cases
* Add `abstract` fn parser-only support
Changes:
* Modify parser to handle new function specifiers, resolve conflicts
* Group `virtual_override` and `FN`, and group `impl_kind` and `IMPL` to avoid ambiguities with around `impl` token parsing
* Add new `VirtualOverride` enum for function declarations, and matching `virt_override() -> VirtualOverride` getter
* Update function declaration logic
Depends on #2462
Features:
* Add `virtual` virtual override keyword for functions
* Support `virtual` class methods using dynamic dispatch
Changes:
* Add `vtable` in `NominalClassType`,
* Add `NominalClassValue**` in class values pointing to descendant-most class
* Resolve virtual methods during member lookup
Limitations:
* Does not include yet `impl`, `abstract` virtual override keywords, or the complete logic for virtual function declaration
Depends on #2460
Relates to #1881
Relates to #2493
Protobufs code hits a warning with the latest system headers on macOS.
I figured this may have been fixed so I updated protobufs and Bazel to
the latest releases. This generally cleaned things up.
However, it actually added *more* warnings. This clearly isn't a really
well tested path. In fact, we already have a disabled warning that we'd
like for Carbon code because LLVM isn't clean for that warning.
So I've switched our warning strategy to a more durable approach of
suppressing all warnings for external repository headers and source
files. This lets us re-enable the missing warning and should fix the
protobuf warning that started me down this twisty path.
Sadly, we *have* to update to Bazel 6 in order to have the necessary
flag to use this approach to suppressing warnings, so I couldn't do this
as two PRs cleanly. =/ That's why I've bundled both the Bazel (and
protobuf) updates with the warning strategy change.
Last but not least, I've fixed several unused parameters in Carbon's
code that our warnings now catch.
Followup for #2490
The switch of DiagnosticMessage to have DiagnosticMessage means we don't need to use unique_ptr. This means that copy constructors are implicit again and don't need to be avoided, but per discussion still keeping with moves. Comments on HandleDiagnostic try to capture the use of moves there.
I'm keeping DiagnosticLevel at the top-level, and adding some checking that notes are actually Notes.
Also, adding MakeMessage to unify some of the logic (this has particularly been bugging me around format_fn, and I ran into it here because of the Diagnostic -> DiagnosticMessage change). The addition of NoTypeDeduction is intended to avoid some duplicative comments that'd been piling up.
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
The problem I'm trying to solve is: `var x: i32 = x;`. This change makes it so that name lookup fails, by removing `x` from name lookup between the `=` and `;`.
`var x: i32` still adds to name lookup to handle future situations like `var (x: i32, x: i32);` which is still a redefinition of `x`; if we don't add `x` to name lookup, it gets harder to catch that example.
The VariableDeclaration/VariableInitializer refactor in ParseTree supports this by given a bracketing-like structure for semantics to cue that it's entering an initialization expression. With this, VariableInitializer can remove the name lookup and queue it to be restored. VariableDeclaration doesn't need to change too much since it's still bracketed by VariableIntroducer, and so we just traverse slightly differently.
Note this also incidentally changes a little about NameReference, that it's returning the storage consistently instead of the name. You can see this e.g. in global_lookup.carbon, `Assign(node8, node4): node2;` using node4 (VarStorage) instead of Node5 (BindName). Really either _could_ work, since from a BindName we can get to the VarStorage, and that may be reason to switch later if we find it preferable to have the BindName for whatever reason.
But the *actual* value in NameLookup is a BindName so that errors can associate with the _name_ instead of the "storage" parse node, which is currently the `:`. This is mainly for fail_duplicate_decl.carbon, which has a "Previous definition" note that points at the storage's parse node.
This is currently used once for PreviousDefinition in semantics.
This PR doesn't just add a builder, it also adds support to the emitter itself to collect notes attached to a diagnostic, and to the consumers and emitters to print all of them.
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
In theory we're doing a central registry so that we can ensure there's at least one test for each. This isn't doing that, but I'm trying to validate that the central registry isn't leading to duplicates or abandoned checks (and catching a couple of each).
While we have a generic 'long term' label, it seems redundant for some
issues that are already labeled with something that clearly is
open-ended and not something we should expect to have a bounded
timeline. For example, we want to actively curate a backlog of design
ideas and good first issues for folks to browse and pick up, so we
shouldn't be marking them as inactive after any fixed time frame.
Define a "type" to be a value of type `type`. Contexts expecting a type perform an implicit conversion to `type`. Values like `()` and `(i32, i32)` and `{}` are no longer types, but instead implicitly convert to `type`. Values of interface or constraint type (now called "facets") are similarly not formally types but implicitly convert to `type`.
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: josh11b <josh11b@users.noreply.github.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Addresses comments from this discussion: https://github.com/carbon-language/carbon-lang/pull/2460#discussion_r1046444433
Features:
* Move subtyping logic from Interpreter to TypeChecker, exposing subtyping as a series of access to `.base`.
* Excludes function parameter conversion, which is still done in ::Convert due to parameters conversion being handled differently.
Changes:
* Add new `class BaseAccessExpression : public MemberAccessExpression`, allowing rewrites
* Handle `BaseAccessExpression` expression type in Interpreter
* Move subtyping logic to `TypeChecker::ImplicitlyConvert`
The doc is more up-to-date than the readme right now, and I'm not ready to migrate it back for the moment, but this should at least make it clear what the status is.
This switches to single list storage of SemanticsNode. The driving motivation behind this is to simplify cross-references within a given IR. Types of nodes will frequently refer to other blocks. This causes a significant increase in the number of cross-references, which can become difficult to manage (and reason about). By reducing to a single list of nodes, cross-references are only needed when crossing IR boundaries.
Because cross-references now only have 2 things to track (IR and index), they can be a regular SemanticsNode and don't need further indirection. This wasn't motivating, but feels like it reinforces the simplification.
Note this isn't being used to deduplicate nodes, at least right now. That could lead to difficult-to-update situations, but also most nodes are associated with the underlying ParseTree::Node in order to track sources for diagnostics; as a consequence, nodes representing equal text in different source locations wouldn't be the same node. There may be future opportunities here, discussed with @zygoloid, but no action is taken at present.
We may eventually want to switch the storage of NodeBlocks to have `[start, end)` ranges instead of individual numbers, but I'm leaving that alone for now.
As an aside, I noticed I was accidentally overloading the copy constructor on SemanticsIR. I've added some disambiguation on that, but am not deleting the copy constructor per style advice (even though the type should never be copied due to storage size).
codespell tries to change `CrossReference -> cross-reference` so disabling it there.
When binding a name, add it to name lookup. On NameReference nodes, use name lookup.
- Switches from "identifiers" to the more generic "strings". Not strictly necessary here, but it's the overall direction I think we've agreed upon and wanted to do it while building more support out.
- Starts doing deduplication of strings.
- On BindName, registers names with name lookup.
- Does name lookup based on the deduplicated string.
- Per discussion with zygoloid, design is intended to be constant-time lookup regardless of the number of parent scopes.
- Adds scopes so that we can track names which will be deregistered from lookup.
Doing some sorting of functions / enums (generally speaking, I've been trying to keep these loosely lexically sorted for lack of a better ordering).
Also removes some code that seems to be dead, and a minor TODO comment fix.
Per #2463 we're looking at adding more patches, this updates .pre-commit-config.yaml and sets up the directory structure to be more accommodating to more patches.
Follow-on to change #2410 which created the `expressions/literals.md` home for literal expressions. Incorporates the decision in #2113 to address a TODO.
Add a generic mechanism to decompose a `Value` and rebuild it, and use that to implement `Substitute`'s recursive transformation of values instead of a hand-rolled decomposition. This means `Substitute` now covers all kinds of values, whereas previously it used to be unable to transform some values, and should be less work to add new kinds of value.
We can use the same mechanism for various other things: structural dumping of values, equality comparisons, and value instantiation in the interpreter would all benefit from this. But in this change I'm just switching `Substitute` to this as a first step.
Depends on #2361, #2421
Add support for destructors of base classes.
Features:
- Call destructors from derived to base class
- Support addressing `TupleValue` using a new `Member` variant
Changes:
- Update `StepDestroy()` to recursively call destructors from derived to base class
- Add new `Member` variant and `IndexedValue` struct to be usable with `TupleValue`
Update the design documentation to reflect #2274.
The contents are largely copy-pasted from p2274.md with minor edits, but the "Open questions" section is new.
Features:
* Split `Member` class into 3 dedicated classes covering named, positional, and a new "base class" element
Changes:
* Rename `Member` to `Element` to better reflect the variants covered
* Add `NamedElement`, `PositionalElement`, and `BaseElement` child classes for `Element`
* Split `GetMember` into 3 function variants based on available attributes (index, name, nothing).
* Add some unit tests to provide coverage of core features
Motivation:
This changeset splits Member into (currently 2) classes, as we see the need for more Member variants (base class access needed for #2378, possibly unnamed mixins, ...), which in addition to the current ones, also have significantly different attributes. This will allow supporting more Member types in the future cleanly.
Alternatives considered:
The alternative solution, "one class for positional, named & base class access", would expose unused or unavailable attributes depending on the Member actual type (`index()` only for positional, `name()` only for named, and neither for base class access).
A quick check suggests this isn't necessary -- probably because the implicit enum cast is used for comparisons. I think this adds a lot to the boilerplate feel of these types, so if we can remove it there's a lot less sharing to do.
This is somewhat based on the name vs Name difference, but I figured I'd split it out and just sweep up the API on the whole while looking at a different approach to #2453
This creates a bit of extra cost in adding parse nodes in that a TODO must be added to semantics, but I think the link is going to last this way long-term. In semantics, it reduces the boilerplate of the main for loop and makes it more obvious what's missing, leaving stub functions to be filled in.
This is just the declaration, without initialization. Partly breaking it out because I'm changing the placeholder builtin types.
Might also need to separate out storage of the var from the name bind.
This makes TokenizedBuffer more consistent with ParseTree and SemanticsIR, which also wrap with [] to produce a sequence value.
It also makes it possible in driver.cpp to just prefix the line with a name, so it ends up with:
var_name: [
(content)
]
Noticed this due to bracketing comments on #2443 and trying to think of better answers. With this, we can also say that the [] bracket a variable.
This makes more modern CPU instructions available. I noticed that we weren't already doing this when working on another bit of code where its actually relevant. This doesn't make a big difference for any of the few benchmarks we have at the moment, but it seems like a good idea.
Modern Clang/LLVM support this exact spelling both on x86 and ARM CPUs, so its surprisingly portable. I've tested it on my ARM mac just in case.
I've picked specific arch flags here because using detection with `native` seems to run into issues in the GitHub actions. Sadly, the x86 macOS runners force a somewhat minimal set of features for x86, but it will still give us consistent results.