-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Error
and ErrorOr
copyable and assignable
#1152
Conversation
With #842 there's not as much reason for the BUILD anymore, maybe move the template.md file and get rid of it?
Allow `auto` as a return type for functions. Only support functions with one `return` statement for now (open question). This explicitly suggests removing the executable semantics `fn name(args) => expression` syntax, and is motivated by reconciling executable semantics with approved Carbon state. [example](https://github.com/carbon-language/carbon-lang/blob/7b706ad7446fe5ac8dc9c092155c8df371d49a71/executable_semantics/testdata/fun_named_params.carbon) This aspect is a decision that may be affected by lambda syntax, but we might also choose to keep lambda syntax and function syntax separate -- I don't think there's enough benefit to providing the alternate function syntax right now. Co-authored-by: Geoff Romer <[email protected]> Co-authored-by: Chandler Carruth <[email protected]>
This proposal introduces the operators `==`, `!=`, `<`, `<=`, `>`, and `>=` to Carbon. Co-authored-by: Jon Meow <[email protected]> Co-authored-by: Geoff Romer <[email protected]> Co-authored-by: josh11b <[email protected]>
This proposal provides semantics for numeric literals. * Numeric literals have a type derived from their value, and can be converted to any type that can represent that value. * Simple operations such as arithmetic that involve only literals also produce values of literal types. * Literals implicitly convert to types that can represent them. * The Carbon prelude provides: * An arbitrary-precision integer type `BigInt`. * A rational number type `Rational(T:! Type)` with constraints on `T` not yet determined. * A family of integer literal types, `IntLiteral(N:! BigInt)`. * A family of real literal types, `RealLiteral(N:! Rational(BigInt))`. Co-authored-by: Chandler Carruth <[email protected]> Co-authored-by: Jon Meow <[email protected]>
Splitting out the task from #849
Revives BisonWrap because this seems a reasonable use of it (avoiding the need to have an std::optional or pointer for Alternative, both of which I thought could be unclear about the intent).
The code is pretty intertwined: having the AST be truly mutable means (to me) changing parser.ypp to return non-const values, but then the way things are passed around between objects should be non-const (particularly an issue with lists), which then creates issues with construction of lists in the TypeChecker, which then TypeChecker needs to mostly be non-const. Due to the difficulties in breaking this apart, whereas I'd previously considering refactoring accessor naming in the same PR, I've largely avoided doing so. The intent is then that this PR focuses mainly on const -> non-const AST behavior. call_main moves out of interpreter.cpp so that interpreter.cpp can receive a fully const AST.
This implements #826, I think covering everything important there. Regarding ReturnTypeContext, I broke that out because it started feeling like a significant number of args to be passing around, and I think this makes the association inside type checking clearer. Co-authored-by: Geoff Romer <[email protected]>
- Drop unnecessary special-casing of 0-tuples. - Drop unused constructor.
This proposed style change allows C++ classes in the Carbon project to provide methods that are named like variables, so long as they behave like _properties_ of the class. It also requires data member names to have a trailing `_`. Co-authored-by: Chandler Carruth <[email protected]>
Updates style of the Match class at the same time.
This does a mass rename of: - `SourceLoc()` -> `source_loc()` for property naming - `loc` -> `source_loc_` for underscore+consistency - Generally changing function args to `source_loc` for consistency - `Tag()` -> `kind()` for property naming and `Kind` parity - `tag` -> `kind_` for underscore Also renames `Pos` and `Results` on `Action`. These are a bit of an exception in that most base classes only have `Tag` and maybe `SourceLoc`, whereas `Action` has a little more. I felt okay having `source_loc()` and `kind()` on the base class where children do `Exp()` and the like, but it felt weird to me to mix it on the same class. The reason for doing this cross-class in one PR is so that I can do it efficiently with a global replace in the codebase, rather than e.g. changing `Expression` but having to read through compiler errors to determine where it's calling `Expression`'s `Tag` versus a different `Tag`. The end result should be equivalent.
This should get us actual debugging capabilities. Also, adopt the better bazel formulation that I forgot to add from geoffromer's original change.
Previously, the program exit was triggered by the destructor. Unfortunately, C++ doesn't make it precisely clear where the destructor is run, and Clang doesn't generate reliable debug information for that to give good backtraces. Among other things, when combining separate cleanup regions in Clang there may be no single canonical location. Instead, move the ExitingStream system to use an explicit low-precedence operator overload to flush the output and exit. This ensures the stream and other actions are completed first but then immediately exits the program in a way that has a definitive source location and produces reliable backtraces. I've tried to add comments and helpers to make this as clear as possible given that it is a subtle and surprising issue. Co-authored-by: Geoff Romer <[email protected]>
- Replace obsolete references to the arbiters, and clarify that the appeals process will be available once the leads are separate from the conduct team. - Document that leads, like conduct team members, will be excluded from discussions/decisions that concern them.
This list was extracted from currently-approved proposals and corresponding design documents. This is intended to be a summary of the status quo, not a change.
The LLVM-13 release is now on Homebrew, so switch to it. This is important because our CI only installs the latest version currently. If you hit issues after this, make sure to update your Homebrew install to get the latest Clang release. You can always directly set `CC` in your environment to use a specific `clang` compiler.
Prior to this change, `__await` would make a deep copy of the continuation stack, but shallow-copy the individual stack frames within it. As a result, continuations appeared to have shallow semantics so long as the continuation stack had only a single frame. This change also removes an obsolete test from the brief period when we intended continuations to have deep-copy semantics, which has been passing basically by accident.
I should emphasize that I am **completely cheating** here. This PR does not add support for actually _performing_ implicit conversions at run time, because the AST doesn't yet contain the necessary type information. At run time, code like `var p: Point = {.x = 1, .y = 2};` directly initializes the name `p` with the _struct_ value `{.x = 1, .y = 2}`; no object of type `Point` is actually created. I'm only getting away with this because we don't yet have any tests that can tell the difference. Co-authored-by: Jon Meow <[email protected]>
* Modify parser and AST nodes to include let statement * Implement let variables * Remove redundant code in fail_match_choice test * Add comment for has_value_category() * Apply suggestions from code review Co-authored-by: Jon Meow <[email protected]> * clang-format changes * Update comments for new BindingPattern methods * Implement nested vars in patterns * Apply suggestions from @geoffromer's code review Co-authored-by: Geoff Romer <[email protected]> * Implement changes from code review. * Remove maybe_var_pattern grammar rule Co-authored-by: Jon Meow <[email protected]> Co-authored-by: Geoff Romer <[email protected]>
Note the use of an enum allows for the operator to be defined for formatting. This reduces the custom formatting required, which is a simplification carrying over to error handling changes I'm working on too. Also, I kind of feel the code's now clearer about what's supported; I'm dropping CHECKs that seemed redundant with the enum.
Co-authored-by: Jon Meow <[email protected]>
This proposal addresses the question of whether we should perform a fully top-down compilation (like in C), a mostly top-down compilation (like in C++), or whether we should allow information from later in the same source file to be used in earlier program constructs (like in Rust, Swift, Java, C#, Haskell, and so on). The proposed direction is: - Entities declared later in the same source file cannot be used earlier; top-down semantics apply everywhere. - As an exception, class member function bodies are parsed as if they appeared after the class. - Forward declarations can be used to separate interface from implementation and to allow entities to be used before they are defined. - The behavior of the program is nonetheless required to be the same as if we had a globally-consistent rule: it's always a hard error to depend on any information that is not known or that is provided later.
Roadmap updates for 2022. Key goals: * Reach the point where the first draft of the core language design is complete, with some test programs and an approximate implementation in executable semantics. * Go public, and improve public participation. Co-authored-by: Jon Meow <[email protected]>
Provide an initial rough structure for the specification so we can add things as they are decided. Split the specification into a language and a library section. In the language section, use one file per broad area of functionality. Divide the language up based on the intended layering of the language design.
This comes from #1092 where I wrote bad code and I felt it should have been caught more clearly.
Revised landing page, including updated README. Co-authored-by: josh11b <[email protected]> Co-authored-by: Chandler Carruth <[email protected]> Co-authored-by: Jon Meow <[email protected]>
Co-authored-by: Geoff Romer <[email protected]> Co-authored-by: Chandler Carruth <[email protected]>
Separating out existing fixes because of #1148 which turns on more.
…like invalid syntax (#1120) * Replaced std::exit() with return llvm::Expected/llvm::Error<T> for expected errors like invalid syntax. * Use llvm::formatv() for formatting lexer error messages. x * Addresed merge errors. * Fixed impl scope. * Made ErrorBuilder::operator<< nodiscard, to catch code forgetting 'return' in 'return FATAL_COMPILATION_ERROR()'. * FatalComplationError() -> ParseAndLexContext::RecordLexerError(). Other usages of ERROR_TOKEN in lexer.lpp were actually supposed to be END_OF_FILE. * Update executable_semantics/syntax/parse_and_lex_context.h Co-authored-by: Jon Meow <[email protected]> * Code review fixes. * Update executable_semantics/syntax/parser.ypp Co-authored-by: Jon Meow <[email protected]> * More code review fixes. * Update executable_semantics/interpreter/type_checker.h Co-authored-by: Jon Meow <[email protected]> * Yet more code review fixes... * Update executable_semantics/syntax/lexer.lpp Co-authored-by: Jon Meow <[email protected]> * code review comments * Update executable_semantics/interpreter/interpreter.cpp Co-authored-by: Geoff Romer <[email protected]> * Apply suggestions from code review Co-authored-by: Jon Meow <[email protected]> * Update executable_semantics/syntax/lexer.lpp Co-authored-by: Jon Meow <[email protected]> * code review * code review * Apply suggestions from code review Co-authored-by: Jon Meow <[email protected]> * formatted code * review comments * Switched to the new ErrorOr<V> error implementation * code review comments * fixed comment * restored ostream.h as #976 makes the change unnecesary * review comments Co-authored-by: Jon Meow <[email protected]> Co-authored-by: Geoff Romer <[email protected]>
Looks like this might be related to prettier/prettier#6112 , but the observed behavior is more broken than that bug indicates.
@@ -26,7 +26,10 @@ class [[nodiscard]] Error { | |||
CHECK(!message_.empty()) << "Errors must have a message."; | |||
} | |||
|
|||
Error(Error&& other) noexcept : message_(std::move(other.message_)) {} | |||
Error(const Error&) = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious, what's the motivation for the change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style guide encourages making types copyable when copying has a clear meaning, and discourages using const&
/&&
overload pairs. This PR aligns the code with both principles.
Note that llvm::Error
seems to be move-only as part of its mechanism for preventing code from ignoring the error, but we've solved that problem with [[nodiscard]]
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the main rationale is about style, I'll note:
"A type should not be copyable/movable if the meaning of copying/moving is unclear to a casual user, or if it incurs unexpected costs."
Just to note, my original intent had been to minimize accidental copy performance overhead (as copies may be expensive, particularly for ErrorOr). I believe in the toolchain we'd want to be more cautious about copies, and not providing a copy constructor is one way to avoid incurring related costs.
This may be more appropriate for executable_semantics, but maybe then the class should be moved correspondingly in order to decrease the likelihood of reuse elsewhere in the toolchain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Accidental" is importantly different from "unexpected" here. That passage of the style guide is saying you should disable copying if it costs meaningfully more than typical users would expect it to cost. It's not saying that you should disable copying if a user can incur unexpected costs by copying when they didn't mean to. If it was, that exception would swallow the rest of the rule, because any type holding a nontrivial amount of data can "incur unexpected costs" by that definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I disagree -- I don't feel confident about when a copy will occur, and when a move will occur. To say that when I write a copy when I intend for a move is an "accident" and not "unexpected" ignores what I expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you arguing to completely remove the checks that errors are handled? Basically, removing
nodiscard
and the bool?
No, I prefer the status quo on both points, but the status quo is not what you think it is: there's no bool. Early revisions of #1137 had it, but it was removed during review. Neither Error
nor ErrorOr
has any run-time tracking of whether the error has been consumed. They do have some compile-time enforcement through nodiscard
, but that's at best a weak approximation of linear typing, and certainly doesn't ensure the error is consumed. For example, it doesn't prevent bugs like this:
ErrorOr<Success> err = MightFail();
// ...
return Success();
nodiscard
merely ensures that the author (and sufficiently attentive readers) are aware that MightFail()
returns something. In other words, it ensures that bugs like the above are at least visible in the calling function.
Especially for
ErrorOr
, essentially all of the comparable designs I'm aware of use bothnodiscard
and a bool to track that the error is consumed.
absl::StatusOr
doesn't have such a bool, and neither does the proposed std::expected
. llvm::Error
and llvm::ErrorOr
do, but it comes at a substantial ergonomic cost: there's no way to read the error message without consuming the object. That means, for example, that they interoperate poorly with GoogleTest (as we discovered as soon as we started trying to use them), because GoogleTest's design pervasively assumes that you can observe the salient state of a value with const
operations.
Conversely, I've never heard complaints about the lack of error-consumed tracking in either absl::StatusOr
(which I used to co-own) or std::expected
. It doesn't even seem to come up in the design rationale for std::expected
, which addresses the feedback they've received pretty exhaustively. So I'm much more inclined to follow the examples of StatusOr
and expected
here, and both of those types are copyable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the combination of [[nodiscard]]
on the type (not just on a function), but zero dynamic checking... odd.
I guess this really gets to [[nodiscard]]
on a type not meaning what I think it means. It doesn't mean an obligation to the holder at all, it just adds an obligation to the caller of a function that returns the type to do something with the returned value. Those values have no subsequent obligation at all. From that sense, I think I'm convinced that being copyable makes a lot of sense. But this hinges on [[nodiscard]]
not creating on obligation to holder (to address @jonmeow point above) and thus copying it not creating more obligations (to address @zygoloid's point before that).
I'm honestly surprised that this pattern has worked well for absl::StatusOr
and std::expected
but 🤷🏻 - I guess I get to live with being surprised.
Curious whether this makes @zygoloid and @jonmeow happy or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, what you're saying is that we can't completely rely on [[nodiscard]]
because it doesn't handle every situation. It only errors when the return type is immediately discarded, for example:
MightFail();
return Success();
And as a consequence of not handling any case, there's no obligation for the type per se. Is that correct?
Note though, not every function is going to be named MightFail()
, it may be something like AddEntity
and not obviously have a failure mode. [[nodiscard]]
means there will be something at the call site for a human reviewer to see (or author to hit a compile error and fix). Thus, even as an imperfect obligation, it still shifts more of the review burden to a compiler check.
I think that either way you go, not everybody's going to be happy. But if the decision is to not rely on these imperfect compiler checks, and especially if it hinges on [[nodiscard]]
being imperfect, would that imply we should remove [[nodiscard]]
everywhere in Carbon and simply rely on review to handle it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not remove [[nodiscard]] ;) For example, in the std::exit -> return-error conversion PR a nodiscard
on ErrorBuilder
flagged two places where I forgot to add return
to the COMPILATION_ERROR()
macro, which would have been very hard to spot during manual review process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, what you're saying is that we can't completely rely on
[[nodiscard]]
because it doesn't handle every situation. It only errors when the return type is immediately discarded, for example:MightFail(); return Success();
And as a consequence of not handling any case, there's no obligation for the type per se. Is that correct?
I'm saying that we can't rely on it to enforce a linear typing/"obligation" model, because that's not what it's designed to do. I think we can rely on it to ensure that potential discarded-error bugs are locally visible.
Note though, not every function is going to be named
MightFail()
, it may be something likeAddEntity
and not obviously have a failure mode.[[nodiscard]]
means there will be something at the call site for a human reviewer to see (or author to hit a compile error and fix). Thus, even as an imperfect obligation, it still shifts more of the review burden to a compiler check.I think that either way you go, not everybody's going to be happy. But if the decision is to not rely on these imperfect compiler checks, and especially if it hinges on
[[nodiscard]]
being imperfect, would that imply we should remove[[nodiscard]]
everywhere in Carbon and simply rely on review to handle it?
On the contrary, if we're going to "rely on review" to detect discarded-error bugs (and I think we have to, at least as a backstop), that seems like a strong argument for keeping nodiscard
. As you noted in the previous paragraph, one of the main things nodiscard
is good at is ensuring that there's something at the callsite for the reviewer to see.
common/error.h
Outdated
CHECK(!ok()); | ||
return std::get<Error>(val_); | ||
} | ||
auto error() && -> Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does removing this mean the code like return Error(std::move(result_or_error).error())
won't be able to use Error's move constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but since Error
is copyable, that code will still work, it just might be less efficient. I wouldn't be opposed to adding a non-const overload of error()
, which would let people move out of it, but I'm not sure it's necessary.
No description provided.