Commit Graph
27 Commits
Author SHA1 Message Date
Jon Ross-Perkins 706e611b6d Support designator expressions on structs. (#2716) 2023-03-30 14:19:46 -07:00
Jon Ross-Perkins 2eef8c751b Add semantics for struct type and value literals. (#2709)
This handles the basics of type and value for structs. Structurally, these look like parameters and arguments (respectively) because expressions/generics may result in multiple IR nodes being generated.

Because `{}` needs to be cast to a type for storage, I'm also adding some validation that's not specific to `{}`, e.g. that `1` shouldn't be valid as a type for storage (previously, nothing errored for that).

This adds more stringification of types, particularly literals, because they come up in value errors now.

ImplicitAs is the result of me mulling whether I'm taking the right approach on type conversions. I think it needs to return a value so that if the implicit cast rewrites the value, the result is accessible to the caller. I may reorient the current TryTypeConversion logic to be more based on the ImplicitAs logic.
2023-03-30 10:15:11 -07:00
Jon Ross-Perkins 7fc203c536 Refactor SemanticsNode factory functions into factory templates. (#2711)
I'm trying to reduce the amount of per-SemanticsNodeKind boilerplate, and make mistakes (e.g., SemanticsNodeKind not matching the Make name, misplacing the type, or Get/Make type mismatches) easier to see.

I could've done this with (more) macros, but felt that the template approach was reasonable enough and likely easier to understand/debug. I'm not sure whether there's more that I could be doing with variadics to reduce the amount of factory code, but this feels good right now.
2023-03-27 11:07:49 -07:00
Jon Ross-Perkins 4ca2e81877 Modify SemanticsIR printing to produce stable output on builtin changes. (#2702)
These changes should make output more stable when builtins are added to semantics. By omitting them from nodes and printing nodes as "relative to the last builtin", I should be able to add and remove builtins without automatically affecting every test. Also by printing builtin nodes as `nodeNameOfBuiltin`, it's a little easier to understand what's going on (for me, at least).
2023-03-22 10:24:03 -07:00
Jon Ross-Perkins 32e8fee4ad Add initial lowering of a trivial function (#2640)
This is starting to build out actual lowering logic, for a really simple `fn Main() -> i32 { return 0; }`

Notes for achieving this:

- In semantics, currently function names are bound separate from the signature. When emitting IR, this turns out to be inconvenient because we want to know the name when we process the declaration and the definition. This change addresses that by merging the name into the FunctionDeclaration node, which is also accessible from the definition. It removes the separate BindName. This should be the cause of all the test changes in semantics, because the IR generated changes.

- Add a "Lowering" class which I'm using to hold the llvm builder state. This class now has minimal support for the SemanticsIR generated by the above example.

- In the "Lowering", values from expressions are stored in a DenseMap. I'll keep thinking about whether there's a cleaner way to achieve this, and I'd call it a temporary solution for now. However, this is how the `0` in `return 0` gets properly associated across SemanticsIR instructions, and it'll frequently be an issue in less trivial cases.
2023-02-28 11:30:20 -08:00
Jon Ross-Perkins e4487505dd Update clang-tidy details for the toolchain (#2623)
Adjusts handling of class constants (`static const`) to use CamelCase. This probably better reflects how we use it in C++ code, treating as appropriate for CamelCase instead of under_score.

Fixes adding_children to be preorder in caller (not sure why this wasn't automated).

No automated changes.
2023-02-27 08:23:11 -08:00
Jon Ross-PerkinsandRichard Smith f7924aa93f Implement calls in the toolchain. (#2582)
This adds tracking of call information plus basic type checking. It adds a builtin for the empty tuple, mainly so that I have the basis for a default function return type.

As an aside, it also unifies printing within SemanticsIR, fixing a missing comma after callables.

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
2023-02-13 16:15:04 -08:00
Jon Ross-Perkins 22d7cd19ed Polish out support for reals and strings. (#2593)
Reals were mostly handled, but this PR adds storage of them. It also switches a little towards the FloatingPointType semantic from TokenizedBuffer.

While real literals like `1.0` were handled, the type literals were not. This just adds `f64`, similar to how I also only support `i32`.

The String type literal wasn't used, so I've added support in lexer and parser. Per discussion with @zygoloid String might be renamed based on the newer type literal plan, but it's still String in explorer and the design, so this is just consistent.

The builtin_types.carbon tests the three basic types that are there right now. The test is added to both parser and semantics so that it's clear what the state is in both stages.
2023-02-13 08:04:49 -08:00
Jon Ross-Perkins b76bc875c4 Remove default constructor from IndexBase (#2598)
There were some notes about default constructors being required in parse_tree.h, but they don't seem to be. Removing the default constructor forces the explicit `::Invalid` where there's no value immediately being assigned, which works fine for existing code. I think this actually reduces the chance of accidents more than the prior default-construct-as-invalid approach.
2023-02-10 12:00:50 -08:00
Jon Ross-Perkins aa307b1144 Fix CrossReference to use NodeId instead of NodeBlockId. (#2597)
Probably a mistake from when this was referencing a node within a block instead of a node directly.
2023-02-10 11:59:19 -08:00
Jon Ross-Perkins b2c60b4f53 Change the builtin enum approach to enforce addition. (#2584)
It feels like every time I add a builtin, I forget to add it to the builtin IR. This approach enforces the addition of builtin nodes, and also takes advantage of the normalized structure to set types appropriately (I think the new approach is simpler on this aspect).
2023-02-08 14:17:26 -08:00
Jon Ross-Perkins 5a0613283b Switch constexpr factory functions to constexpr values. (#2581)
Using the same const/constexpr done in EnumBase, adds Invalid and Builtin* values to replace Make functions that produced the same. This should make it clearer at call sites what the cost actually is, and reduces the syntactic overhead for MakeBuiltinReference in particular.

Really, this is that MakeBuiltinReference has been feeling pretty verbose, so I did that, and then one MakeInvalid is right next to it, and then obviously I should replace the other MakeInvalid for consistency...
2023-02-06 15:57:33 -08:00
Jon Ross-Perkins 6feed2ae33 Add tracking of function parameters (#2552)
For parameters (and in the future, arguments too; generally comma-separated lists) track two node blocks:

1. param_ir: The complete IR.
2. param_refs: Nodes within the IR that are the "root" parameter.

param_refs should allow quick counting of the # of parameters, and more efficient comparison of call args with function parameters. param_ir should be necessary to generate the actual signature.

In order to construct this, this refactors the node_block_stack into its own class, which is reused in params_stack. These carry references to the underlying SmallVector for lazy modification in order to avoid a dependency cycle with SemanticsIR (also see notes on empty node blocks below).

When finalized, the block pair is pushed onto finished_params_stack. That's because node_stack only has space for one thing, and this is two things -- so I'm essentially choosing a trade-off of adding another stack in order to avoid consuming more space in the expectation that most parse nodes have 0 or 1 things to return, and 2 will be very rare.

As factored, this currently consolidates most empty node blocks into a single canonical empty node block. This is because I think empty blocks, i.e. `()`, will be very common. In order to achieve this, SemanticsNodeBlockStack does lazy creation.

An alternative approach would have been to use 1 node block per parameter. We decided against this in order to reduce the number of vectors being created.
2023-01-26 14:30:38 -08:00
Jon Ross-Perkins 2a163ca6cd Refactor the node stack into its own class. (#2505)
Currently, there's a mix of accessing node_stack_ both directly and indirectly, and there are already several Push/Pop methods to help wrap the behavior. However, there's also direct access because of shifting over time, as well as variations in _how_ the stack is used.

This migrates to a separate class in order to make a more specific contract for the API. It cleans up existing uses and adds APIs where needed.
2023-01-05 15:16:05 -08:00
Chandler CarruthandJon Ross-Perkins a1ad39fa29 Introduce helpers to build enum-wrapping classes. (#2504)
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>
2023-01-04 12:33:06 -08:00
Jon Ross-Perkins e5d49f5989 Store SemanticsNode in a single list instead of per-block (#2475)
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.
2022-12-21 13:13:13 -08:00
Jon Ross-Perkins 9d234aa7e7 Start doing name lookup for references (#2472)
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.
2022-12-20 10:10:23 -08:00
Jon Ross-Perkins 6c248ea38f Add support for VariableDeclaration and VariableInitialization (#2466)
This starts dealing with assignment for the initializer, please let me know if you have thoughts there (including for naming).
2022-12-14 13:42:53 -08:00
Jon Ross-Perkins 34b5349df9 Change result_id to use is_valid instead of an optional. (#2449)
Incrementally shrinks size, but should also make the errors a bit clearer with better context.
2022-12-07 16:30:23 -08:00
Jon Ross-Perkins 30960d11a9 Start adding var decls to semantics. (#2447)
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.
2022-12-07 15:10:42 -08:00
Jon Ross-PerkinsandRichard Smith d50fef1736 Expand use of IndexBase (#2436)
Initially I'd added this to lexer, this includes parser and semantics. Also adds ComparableIndexBase to unify a few common cases where <> comparisons are supported.

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
2022-12-01 14:09:18 -08:00
Jon Ross-Perkins 4c8fdf5124 Start drafting out semantic type checking. (#2406)
This is a first pass at what semantic type checking might look like. Types propagate along nodes, we use an InvalidType object when there's an error, and once there's an InvalidType we stop doing so much type checking.

This adds some RealLiteral handling in order to get type mismatches. I'm cautious about creating some real value for SemanticsIR (since the tokenized buffer version is a bit constrained), so I'm not doing that yet. But I will probably need to in order to maintain SemanticsIR having hermetic copies of its data, without a parse tree dependency.
2022-11-17 13:36:40 -08:00
Jon Ross-Perkins 37dd968709 Track the ParseTree::Node with SemanticsNode. (#2407)
Longer term, I think this is going to be important as part of associating errors with code after the particular node has been processed, even though it isn't used here.
2022-11-17 08:58:13 -08:00
Jon Ross-Perkins 57090142e8 Start adding builtins to SemanticsIR (#2356)
This starts adding builtins with TypeType and IntegerLiteralType. Note, structurally that's all they are, and not directly accessible in any way.

Adds a type field to SemanticsNode. Now, IntegerLiteralType can be identified as having type=TypeType, and IntegerLiteral as type=IntegerLiteralType. The current iteration doesn't do anything for type propagation, because I wanted to avoid making this too big.

This also switches the Identifier IR to instead BindName, with some side-effects. I'd been trying to think how to provide a name for TypeType, and switching around how things worked seemed like a better approach. And while I think it's the right direction (e.g., alias should just be a BindName), I also realized I don't need to name TypeType: there's probably a keyword to refer to the builtin, so it shouldn't use regular name lookup.
2022-10-28 14:16:51 -07:00
Jon Ross-Perkins 722beb8334 Reduce macro use for node kinds to get flexibility (#2354)
As I'm looking at adding builtins, it feels a bit like the types of args will end up scaling close to the number of semantic kinds. Rather than having this lead to a bunch of macros, this approach drops the macros for plain code.

Note, this adds Get functions too -- I'd planned those regardless for type safety, this is just getting ahead of the issue so that the Print function is pretty clean.
2022-10-26 14:19:35 -07:00
Jon Ross-Perkins 0755598fa8 Refactor semantics to provide a more block-y IR (#2349)
As a step towards builtins, provide more blocks. The intent is that any significant scope change will become its own NodeBlock. Builtins should produce the first set of node blocks.

Note, SemanticsIR as set up here isn't handling ordering of import processing -- I haven't thought that through much beyond that we probably want some lighter-weight processing of the parse tree to achieve it. But I think the essence of loading builtins first as their own IR block is... probably right?
2022-10-25 17:23:04 -07:00
Jon Ross-Perkins 1f8508204b Rewrite semantics towards a more pure instruction model (#2320)
This rewrites semantics towards a more pure instruction model, in pursuit of the simple instruction-style output.

I think I can get this approach to type-check as it goes along, but obviously this change doesn't prove that yet. I'm separating it out because it's a large rewrite of the semantics structure, tossing out a lot of what was there before. But I think it does help towards several requests, like setting up a clear path for consolidating duplicate identifiers and making the node style more standardized.

I expect to need to pass multiple args to function calls, that'd probably be storing vectors of args similar to how I'm showing identifiers and integer literals stored.

This removes the semantics namespace because (a) it was getting annoying writing the `::` everywhere, and (b) I think the leaning with Carbon is to avoid namespaces (@chandlerc asked not to put SemanticsIR/SemanticsFactory in a namespace, which is the crux of the issue). But, it's still necessary to avoid name conflicts so I just prefix everything with "Semantics" (still a lot of typing, but no `::`).
2022-10-20 12:50:10 -07:00