Skip to content
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

Replaced std::exit() with return Carbon::ErrorOr for expected errors like invalid syntax #1120

Merged
merged 37 commits into from
Mar 22, 2022
Merged

Conversation

pk19604014
Copy link
Contributor

@pk19604014 pk19604014 commented Mar 7, 2022

The trigger for this change is the work to build a structured fuzzer for Carbon, as the fuzzing framework doesn’t like std::exit() calls. However, the change should also allow for using executable semantics as a library, and for easier testing.

Most of the change is pretty mechanical: replacing void with llvm::Error and T with llvm::Expected for fallible functions + updating the code to handle/propagate failures. Internal logic checks continue to use RAW_EXITING_STREAM which will std::abort() if triggered.

Some highlights:

  • executable_semantics/common/error.h - added an ErrorBuilder class for streaming error messages to and then converting to either llvm::Error or llvm::Expected. Updated FATAL_COMPILATION_ERROR macro family to use ErrorBuilder, the code now looks like return FATAL_COMPILATION_ERROR() << “message”. Added RETURN_IF_ERROR and ASSING_OR_RETURN macros to help reduce some boilerplate code.

  • common/ostream.h - adjusted operator<< injected into the llvm namespace to use a new HasLlvmRawOstreamOp predicate, otherwise the operator was trying to define itself for Carbon::AST type and breaking unimplemented_example_test.

  • executable_semantics/syntax/lexer.lpp - updated to return END_OF_FILE on syntax errors. Newer bison versions (3.5+) have a special YYerror token for this, but https://github.com/jmillikin/rules_bison that Carbon is using only supports bison versions up to 3.3.2, and my quick attempt to hack it locally to support 3.5 was not successful (failed to find textstyle.h header, etc.).

  • Added fallible AST node constructors for IntrinsicExpression, ​​AlternativePattern, and FunctionDeclaration, defined as factory functions taking an arena as a template parameter, so that the logic can still live in the same file as the main class while not depending on the actual arena implementation.

  • executable_semantics/syntax/parser.ypp - updated to use the new fallible constructor functions for IntrinsicExpression, ​​AlternativePattern, and FunctionDeclaration.

  • Some fun with the PatternMatch() function which changed to return llvm::Expected, while callers continued to do things like if (PatternMatch(xx)) which return true for any successful call, regardless of whether the actual bool result is true or false ;) Still not sure what the best solution is to effectively prevent such misuse, but for now just updated to use ASSIGN_OR_RETURN.

Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The essence of the RETURN_IF_ERROR and ASSIGN_OR_RETURN approach seems good, although I'm more apprehensive about the specific use of llvm::Error/Expected due to the issues I've encountered with their bool cast behaviors. Rolling our own types for this, without the bool casts, may yield better results long-term.

(note, trying to keep comments light since the PR is still in draft)

executable_semantics/syntax/lexer.lpp Outdated Show resolved Hide resolved
executable_semantics/syntax/lexer.lpp Outdated Show resolved Hide resolved
@pk19604014 pk19604014 marked this pull request as ready for review March 8, 2022 14:50
@pk19604014 pk19604014 requested a review from a team as a code owner March 8, 2022 14:50
@zygoloid zygoloid added the explorer Action items related to Carbon explorer code label Mar 9, 2022
Other usages of ERROR_TOKEN in lexer.lpp were actually supposed to be END_OF_FILE.
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, if it were up to me, I'd just do something naive like:

struct Error {
 public:
  static Error Success() { return Error(); }
  explicit Error(std::string message) : message_(message) {}
  ~Error() { CHECK(used_); }
  bool ok() { return !message.has_value(); }
  const std::string& message() { used_ = true; return *message_; }

 private:
  bool used_ = false; // Suggesting this mainly because I think it's in LLVM.
  std::optional<std::string> message_;
};

template<typename T>
struct ErrorOr {
 public:
  // Should be implicit
  ErrorOr(std::variant<Error, T> val) : val_(val);
  ~ErrorOr() { CHECK(used_); }

  bool ok() { return std::holds_alternative<T>(val_); }
  Error error() { CHECK(!ok()); used_ = true; return std::get<Error>(val_); }
  const T& value() { CHECK(ok()); used_ = true; return std::get<T>(val_); }

 private:
  bool used_ = false;
  std::variant<Error, T> val_;
};

Or even just copy llvm::Expected/Error and rip out a few pieces, like the implicit cast to bool, and start from there -- with the hope of tearing out a few pieces quickly rather than letting llvm::Expected/Error sink into the codebase. I feel I can't say enough how difficult I find those classes to understand, and I'd much rather not introduce them.

But I think this is up to @geoffromer if he strongly prefers going ahead with llvm::Error/Expected, rather than delaying a little more time to avoid stepping more in that direction.

common/ostream.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Outdated Show resolved Hide resolved
executable_semantics/ast/expression.h Outdated Show resolved Hide resolved
executable_semantics/syntax/parse_and_lex_context.cpp Outdated Show resolved Hide resolved
executable_semantics/syntax/parse_and_lex_context.h Outdated Show resolved Hide resolved
executable_semantics/syntax/parser.ypp Outdated Show resolved Hide resolved
executable_semantics/syntax/parser.ypp Outdated Show resolved Hide resolved
executable_semantics/syntax/lexer.lpp Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
executable_semantics/syntax/lexer.lpp Outdated Show resolved Hide resolved
executable_semantics/syntax/parse_and_lex_context.cpp Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/interpreter/resolve_control_flow.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/resolve_names.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/type_checker.h Outdated Show resolved Hide resolved
executable_semantics/syntax/parse_test_matchers_internal.h Outdated Show resolved Hide resolved
@geoffromer
Copy link
Contributor

But I think this is up to @geoffromer if he strongly prefers going ahead with llvm::Error/Expected, rather than delaying a little more time to avoid stepping more in that direction.

I was previously pretty neutral between the two options in the short term, but I'm starting to agree more with you that we should start out with our own types, after learning that the bool conversion is the only way of querying the llvm types, and after seeing the const_cast nastiness that's caused by the lack of read-only access to the error.

Copy link
Contributor Author

@pk19604014 pk19604014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reviews!

And sorry for some single-comment spam, still figuring out how to use github ;)

executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/syntax/parse_test_matchers_internal.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
common/ostream.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/common/error.h Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/common/error_test.cpp Outdated Show resolved Hide resolved
Copy link
Contributor Author

@pk19604014 pk19604014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll be on vacation all of next week, will get back to the PR when I return.

executable_semantics/interpreter/resolve_names.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/resolve_control_flow.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/resolve_control_flow.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/type_checker.h Outdated Show resolved Hide resolved
executable_semantics/interpreter/type_checker.h Outdated Show resolved Hide resolved
executable_semantics/ast/declaration.h Show resolved Hide resolved
executable_semantics/ast/declaration.h Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
executable_semantics/interpreter/interpreter.cpp Outdated Show resolved Hide resolved
@jonmeow jonmeow mentioned this pull request Mar 15, 2022
@jonmeow
Copy link
Contributor

jonmeow commented Mar 17, 2022

Out of side-discussion, there's now Carbon::Error and Carbon::ErrorOr per #1137. Can you please switch from llvm::Error/Expected to that?

@pk19604014 pk19604014 changed the title Replaced std::exit() with return llvm::Expected/llvm::Error<T> for expected errors like invalid syntax Replaced std::exit() with return Carbon::ErrorOr for expected errors like invalid syntax Mar 22, 2022
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great!

My sense is if there are still issues which we need to fix, this is big enough that it may be easiest to just have this merged and fix forward for the rest. But it may be best overall to just start playing with this and see how it goes.

common/ostream.h Outdated Show resolved Hide resolved
common/error.h Show resolved Hide resolved
executable_semantics/common/error.h Show resolved Hide resolved
@pk19604014 pk19604014 merged commit 8805426 into carbon-language:trunk Mar 22, 2022
@pk19604014 pk19604014 deleted the exit_to_return branch March 22, 2022 20:17
chandlerc pushed a commit that referenced this pull request Jun 28, 2022
…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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
explorer Action items related to Carbon explorer code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants