mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-27 22:02:33 +01:00
This switches `DCHECK` and `FATAL` as well. The goal is to reduce the code size impact of these assertions so that we can keep more of them enabled. Currently, the largest cost I see from `CHECK` is not the actual check or the cold code itself, but actually the failure to inline trivial functions due to the presence of the cold code. This means that our goal isn't to reduce apparent code size in the final binary but the LLVM IR cost assessed for these routines in the inliner, which closely correlates with code size but is a bit different. As discussed in #4283, experimentation shows that a single function call with a minimal number of arguments is the lowest cost model for these. This is easily achieved with a format-string API that internally uses `llvm::formatv`. This PR is essentially the `CHECK` version of #4283. However, the check macros are substantially harder to make work with both format strings and streaming because they also take a condition. Also, unexpectedly, I was very successful at devising a regular expression based automated rewrite from the streaming to the format string form with only low 10s of manual fixes. This includes compacting strings broken up across lines, etc. Given how well that went, I've prepared this PR which just directly switches to the format string API and migrate everything to use it. One nice side-effect is that the format string approach ends up greatly simplifying the implementation here as well. This is ... *shockingly* effective. Parsing speeds up by more than 3% with just this change. And checking speeds up by **8%** with this change alone: ``` BM_CompileAPIFileDenseDecls<Phase::Parse>/256 86.3µs ± 1% 82.9µs ± 1% -3.94% (p=0.000 n=17+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 431µs ± 1% 415µs ± 1% -3.76% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 1.77ms ± 1% 1.71ms ± 1% -3.18% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 7.44ms ± 1% 7.17ms ± 2% -3.56% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 30.7ms ± 1% 29.7ms ± 1% -3.15% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 131ms ± 1% 127ms ± 1% -2.81% (p=0.000 n=18+18) BM_CompileAPIFileDenseDecls<Phase::Check>/256 878µs ± 2% 800µs ± 1% -8.91% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/1024 1.88ms ± 2% 1.72ms ± 1% -8.56% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/4096 5.78ms ± 2% 5.28ms ± 1% -8.70% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/16384 21.9ms ± 1% 20.1ms ± 1% -8.02% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Check>/65536 90.4ms ± 2% 83.1ms ± 1% -8.04% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/262144 381ms ± 2% 352ms ± 1% -7.79% (p=0.000 n=19+19) ``` --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
485 lines
17 KiB
C++
485 lines
17 KiB
C++
// 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
|
|
|
|
#ifndef CARBON_EXPLORER_AST_PATTERN_H_
|
|
#define CARBON_EXPLORER_AST_PATTERN_H_
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "common/check.h"
|
|
#include "common/ostream.h"
|
|
#include "explorer/ast/ast_node.h"
|
|
#include "explorer/ast/ast_rtti.h"
|
|
#include "explorer/ast/clone_context.h"
|
|
#include "explorer/ast/expression.h"
|
|
#include "explorer/ast/expression_category.h"
|
|
#include "explorer/ast/value_node.h"
|
|
#include "explorer/base/source_location.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/STLFunctionalExtras.h"
|
|
|
|
namespace Carbon {
|
|
|
|
class Value;
|
|
|
|
// Abstract base class of all AST nodes representing patterns.
|
|
//
|
|
// Pattern and its derived classes support LLVM-style RTTI, including
|
|
// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
|
|
// class derived from Pattern must provide a `classof` operation, and
|
|
// every concrete derived class must have a corresponding enumerator
|
|
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
|
|
// details.
|
|
class Pattern : public AstNode {
|
|
public:
|
|
explicit Pattern(CloneContext& context, const Pattern& other)
|
|
: AstNode(context, other),
|
|
static_type_(context.Clone(other.static_type_)),
|
|
value_(context.Clone(other.value_)) {}
|
|
|
|
Pattern(const Pattern&) = delete;
|
|
auto operator=(const Pattern&) -> Pattern& = delete;
|
|
|
|
~Pattern() override = 0;
|
|
|
|
void Print(llvm::raw_ostream& out) const override;
|
|
void PrintID(llvm::raw_ostream& out) const override;
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromPattern(node->kind());
|
|
}
|
|
|
|
// Returns the enumerator corresponding to the most-derived type of this
|
|
// object.
|
|
auto kind() const -> PatternKind {
|
|
return static_cast<PatternKind>(root_kind());
|
|
}
|
|
|
|
// The static type of this pattern. Cannot be called before typechecking.
|
|
auto static_type() const -> const Value& {
|
|
CARBON_CHECK(static_type_.has_value());
|
|
return **static_type_;
|
|
}
|
|
auto has_static_type() const -> bool { return static_type_.has_value(); }
|
|
|
|
// Sets the static type of this expression. Can only be called once, during
|
|
// typechecking.
|
|
void set_static_type(Nonnull<const Value*> type) {
|
|
CARBON_CHECK(!static_type_.has_value());
|
|
static_type_ = type;
|
|
}
|
|
|
|
// The value of this pattern. Cannot be called before typechecking.
|
|
// TODO: Rename to avoid confusion with BindingPattern::constant_value
|
|
auto value() const -> const Value& { return **value_; }
|
|
|
|
// Sets the value of this pattern. Can only be called once, during
|
|
// typechecking.
|
|
void set_value(Nonnull<const Value*> value) {
|
|
CARBON_CHECK(!value_, "set_value called more than once");
|
|
value_ = value;
|
|
}
|
|
|
|
// Returns whether the value has been set. Should only be called
|
|
// during typechecking: before typechecking it's guaranteed to be false,
|
|
// and after typechecking it's guaranteed to be true.
|
|
auto has_value() const -> bool { return value_.has_value(); }
|
|
|
|
// Determines whether the pattern has already been type-checked. Should
|
|
// only be used by type-checking.
|
|
auto is_type_checked() const -> bool {
|
|
return static_type_.has_value() && value_.has_value();
|
|
}
|
|
|
|
protected:
|
|
// Constructs a Pattern representing syntax at the given line number.
|
|
// `kind` must be the enumerator corresponding to the most-derived type being
|
|
// constructed.
|
|
Pattern(AstNodeKind kind, SourceLocation source_loc)
|
|
: AstNode(kind, source_loc) {}
|
|
|
|
private:
|
|
std::optional<Nonnull<const Value*>> static_type_;
|
|
std::optional<Nonnull<const Value*>> value_;
|
|
};
|
|
|
|
// Call the given `visitor` on all patterns nested within the given pattern,
|
|
// including `pattern` itself, in a preorder traversal. Aborts and returns
|
|
// `false` if `visitor` returns `false`, otherwise returns `true`.
|
|
auto VisitNestedPatterns(const Pattern& pattern,
|
|
llvm::function_ref<bool(const Pattern&)> visitor)
|
|
-> bool;
|
|
inline auto VisitNestedPatterns(Pattern& pattern,
|
|
llvm::function_ref<bool(Pattern&)> visitor)
|
|
-> bool {
|
|
// The non-const version is implemented in terms of the const version. The
|
|
// const_cast is safe because every pattern reachable through a non-const
|
|
// pattern is also non-const.
|
|
const Pattern& const_pattern = pattern;
|
|
return VisitNestedPatterns(const_pattern, [&](const Pattern& inner) {
|
|
return visitor(const_cast<Pattern&>(inner));
|
|
});
|
|
}
|
|
|
|
// A pattern consisting of the `auto` keyword.
|
|
class AutoPattern : public Pattern {
|
|
public:
|
|
explicit AutoPattern(SourceLocation source_loc)
|
|
: Pattern(AstNodeKind::AutoPattern, source_loc) {}
|
|
|
|
explicit AutoPattern(CloneContext& context, const AutoPattern& other)
|
|
: Pattern(context, other) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromAutoPattern(node->kind());
|
|
}
|
|
};
|
|
|
|
class VarPattern : public Pattern {
|
|
public:
|
|
explicit VarPattern(SourceLocation source_loc, Nonnull<Pattern*> pattern)
|
|
: Pattern(AstNodeKind::VarPattern, source_loc), pattern_(pattern) {}
|
|
|
|
explicit VarPattern(CloneContext& context, const VarPattern& other)
|
|
: Pattern(context, other), pattern_(context.Clone(other.pattern_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromVarPattern(node->kind());
|
|
}
|
|
|
|
auto pattern() const -> const Pattern& { return *pattern_; }
|
|
auto pattern() -> Pattern& { return *pattern_; }
|
|
|
|
auto expression_category() const -> ExpressionCategory {
|
|
return ExpressionCategory::Reference;
|
|
}
|
|
|
|
private:
|
|
Nonnull<Pattern*> pattern_;
|
|
};
|
|
|
|
// A pattern that matches a value of a specified type, and optionally binds
|
|
// a name to it.
|
|
class BindingPattern : public Pattern {
|
|
public:
|
|
using ImplementsCarbonValueNode = void;
|
|
|
|
BindingPattern(SourceLocation source_loc, std::string name,
|
|
Nonnull<Pattern*> type,
|
|
std::optional<ExpressionCategory> expression_category)
|
|
: Pattern(AstNodeKind::BindingPattern, source_loc),
|
|
name_(std::move(name)),
|
|
type_(type),
|
|
expression_category_(expression_category) {}
|
|
|
|
explicit BindingPattern(CloneContext& context, const BindingPattern& other)
|
|
: Pattern(context, other),
|
|
name_(other.name_),
|
|
type_(context.Clone(other.type_)),
|
|
expression_category_(other.expression_category_) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromBindingPattern(node->kind());
|
|
}
|
|
|
|
// The name this pattern binds, if any. If equal to AnonymousName, indicates
|
|
// that this BindingPattern does not bind a name, which in turn means it
|
|
// should not be used as a ValueNode.
|
|
auto name() const -> const std::string& { return name_; }
|
|
|
|
// The pattern specifying the type of values that this pattern matches.
|
|
auto type() const -> const Pattern& { return *type_; }
|
|
auto type() -> Pattern& { return *type_; }
|
|
|
|
// Returns the value category of this pattern. Can only be called after
|
|
// typechecking.
|
|
auto expression_category() const -> ExpressionCategory {
|
|
return expression_category_.value();
|
|
}
|
|
|
|
// Returns whether the value category has been set. Should only be called
|
|
// during typechecking.
|
|
auto has_expression_category() const -> bool {
|
|
return expression_category_.has_value();
|
|
}
|
|
|
|
// Sets the value category of the variable being bound. Can only be called
|
|
// once during typechecking
|
|
void set_expression_category(ExpressionCategory vc) {
|
|
CARBON_CHECK(!expression_category_.has_value());
|
|
expression_category_ = vc;
|
|
}
|
|
|
|
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
|
|
return std::nullopt;
|
|
}
|
|
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
|
|
return std::nullopt;
|
|
}
|
|
|
|
private:
|
|
std::string name_;
|
|
Nonnull<Pattern*> type_;
|
|
std::optional<ExpressionCategory> expression_category_;
|
|
};
|
|
|
|
class AddrPattern : public Pattern {
|
|
public:
|
|
explicit AddrPattern(SourceLocation source_loc,
|
|
Nonnull<BindingPattern*> binding)
|
|
: Pattern(AstNodeKind::AddrPattern, source_loc), binding_(binding) {}
|
|
|
|
explicit AddrPattern(CloneContext& context, const AddrPattern& other)
|
|
: Pattern(context, other), binding_(context.Clone(other.binding_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromAddrPattern(node->kind());
|
|
}
|
|
|
|
auto binding() const -> const BindingPattern& { return *binding_; }
|
|
auto binding() -> BindingPattern& { return *binding_; }
|
|
|
|
private:
|
|
Nonnull<BindingPattern*> binding_;
|
|
};
|
|
|
|
// A pattern that matches a tuple value field-wise.
|
|
class TuplePattern : public Pattern {
|
|
public:
|
|
TuplePattern(SourceLocation source_loc, std::vector<Nonnull<Pattern*>> fields)
|
|
: Pattern(AstNodeKind::TuplePattern, source_loc),
|
|
fields_(std::move(fields)) {}
|
|
|
|
explicit TuplePattern(CloneContext& context, const TuplePattern& other)
|
|
: Pattern(context, other), fields_(context.Clone(other.fields_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromTuplePattern(node->kind());
|
|
}
|
|
|
|
auto fields() const -> llvm::ArrayRef<Nonnull<const Pattern*>> {
|
|
return fields_;
|
|
}
|
|
auto fields() -> llvm::ArrayRef<Nonnull<Pattern*>> { return fields_; }
|
|
|
|
private:
|
|
std::vector<Nonnull<Pattern*>> fields_;
|
|
};
|
|
|
|
class GenericBinding : public Pattern {
|
|
public:
|
|
using ImplementsCarbonValueNode = void;
|
|
|
|
enum class BindingKind {
|
|
// A checked generic binding, `T:! type`.
|
|
Checked,
|
|
// A template generic binding, `template T:! type`.
|
|
Template,
|
|
};
|
|
|
|
explicit GenericBinding(SourceLocation source_loc, std::string name,
|
|
Nonnull<Expression*> type, BindingKind binding_kind)
|
|
: Pattern(AstNodeKind::GenericBinding, source_loc),
|
|
name_(std::move(name)),
|
|
type_(type),
|
|
binding_kind_(binding_kind) {}
|
|
|
|
explicit GenericBinding(CloneContext& context, const GenericBinding& other);
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromGenericBinding(node->kind());
|
|
}
|
|
|
|
auto name() const -> const std::string& { return name_; }
|
|
auto type() const -> const Expression& { return *type_; }
|
|
auto type() -> Expression& { return *type_; }
|
|
auto binding_kind() const -> BindingKind { return binding_kind_; }
|
|
|
|
// The index of this binding, which is the number of bindings that are in
|
|
// scope at the point where this binding is declared.
|
|
auto index() const -> int {
|
|
CARBON_CHECK(index_);
|
|
return *index_;
|
|
}
|
|
|
|
// Set the index of this binding. Should be called only during type-checking.
|
|
void set_index(int index) {
|
|
CARBON_CHECK(!index_, "should only set depth and index once");
|
|
index_ = index;
|
|
}
|
|
|
|
auto expression_category() const -> ExpressionCategory {
|
|
return ExpressionCategory::Value;
|
|
}
|
|
|
|
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
|
|
return template_value_;
|
|
}
|
|
|
|
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
|
|
return symbolic_identity_;
|
|
}
|
|
void set_symbolic_identity(Nonnull<const Value*> value) {
|
|
CARBON_CHECK(!symbolic_identity_.has_value());
|
|
symbolic_identity_ = value;
|
|
}
|
|
|
|
void set_template_value(Nonnull<const Value*> template_value) {
|
|
CARBON_CHECK(binding_kind_ == BindingKind::Template);
|
|
template_value_ = template_value;
|
|
}
|
|
auto has_template_value() const -> bool {
|
|
CARBON_CHECK(binding_kind_ == BindingKind::Template);
|
|
return template_value_.has_value();
|
|
}
|
|
|
|
// The impl binding associated with this type variable.
|
|
auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
|
|
return impl_binding_;
|
|
}
|
|
// Set the impl binding.
|
|
void set_impl_binding(Nonnull<const ImplBinding*> binding) {
|
|
CARBON_CHECK(!impl_binding_.has_value());
|
|
impl_binding_ = binding;
|
|
}
|
|
|
|
// Return the original generic binding.
|
|
auto original() const -> Nonnull<const GenericBinding*> {
|
|
if (original_.has_value()) {
|
|
return *original_;
|
|
} else {
|
|
return this;
|
|
}
|
|
}
|
|
// Set the original generic binding.
|
|
void set_original(Nonnull<const GenericBinding*> orig) { original_ = orig; }
|
|
|
|
// Returns whether this binding has been named as a type within its own type
|
|
// expression via `.Self`. Set by type-checking.
|
|
auto named_as_type_via_dot_self() const -> bool {
|
|
return named_as_type_via_dot_self_;
|
|
}
|
|
// Set that this binding was named as a type within its own type expression
|
|
// via `.Self`. May only be called during type-checking.
|
|
void set_named_as_type_via_dot_self() { named_as_type_via_dot_self_ = true; }
|
|
|
|
private:
|
|
std::string name_;
|
|
Nonnull<Expression*> type_;
|
|
BindingKind binding_kind_;
|
|
std::optional<int> index_;
|
|
std::optional<Nonnull<const Value*>> template_value_;
|
|
std::optional<Nonnull<const Value*>> symbolic_identity_;
|
|
std::optional<Nonnull<const ImplBinding*>> impl_binding_;
|
|
std::optional<Nonnull<const GenericBinding*>> original_;
|
|
bool named_as_type_via_dot_self_ = false;
|
|
};
|
|
|
|
// Converts paren_contents to a Pattern, interpreting the parentheses as
|
|
// grouping if their contents permit that interpretation, or as forming a
|
|
// tuple otherwise.
|
|
auto PatternFromParenContents(Nonnull<Arena*> arena, SourceLocation source_loc,
|
|
const ParenContents<Pattern>& paren_contents)
|
|
-> Nonnull<Pattern*>;
|
|
|
|
// Converts paren_contents to a TuplePattern, interpreting the parentheses as
|
|
// forming a tuple.
|
|
auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
|
|
SourceLocation source_loc,
|
|
const ParenContents<Pattern>& paren_contents)
|
|
-> Nonnull<TuplePattern*>;
|
|
|
|
// Converts `contents` to ParenContents<Pattern> by replacing each Expression
|
|
// with an ExpressionPattern.
|
|
auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
|
|
const ParenContents<Expression>& contents)
|
|
-> ParenContents<Pattern>;
|
|
|
|
// A pattern that matches an alternative of a choice type.
|
|
class AlternativePattern : public Pattern {
|
|
public:
|
|
// Constructs an AlternativePattern that matches the alternative specified
|
|
// by `alternative`, if its arguments match `arguments`.
|
|
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
|
|
Nonnull<Expression*> alternative,
|
|
Nonnull<TuplePattern*> arguments)
|
|
-> ErrorOr<Nonnull<AlternativePattern*>> {
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
Nonnull<SimpleMemberAccessExpression*> member_access,
|
|
RequireSimpleMemberAccess(alternative));
|
|
return arena->New<AlternativePattern>(source_loc, &member_access->object(),
|
|
member_access->member_name(),
|
|
arguments);
|
|
}
|
|
|
|
// Constructs an AlternativePattern that matches a value of the type
|
|
// specified by choice_type if it represents an alternative named
|
|
// alternative_name, and its arguments match `arguments`.
|
|
AlternativePattern(SourceLocation source_loc,
|
|
Nonnull<Expression*> choice_type,
|
|
std::string alternative_name,
|
|
Nonnull<TuplePattern*> arguments)
|
|
: Pattern(AstNodeKind::AlternativePattern, source_loc),
|
|
choice_type_(choice_type),
|
|
alternative_name_(std::move(alternative_name)),
|
|
arguments_(arguments) {}
|
|
|
|
explicit AlternativePattern(CloneContext& context,
|
|
const AlternativePattern& other)
|
|
: Pattern(context, other),
|
|
choice_type_(context.Clone(other.choice_type_)),
|
|
alternative_name_(other.alternative_name_),
|
|
arguments_(context.Clone(other.arguments_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromAlternativePattern(node->kind());
|
|
}
|
|
|
|
auto choice_type() const -> const Expression& { return *choice_type_; }
|
|
auto choice_type() -> Expression& { return *choice_type_; }
|
|
auto alternative_name() const -> const std::string& {
|
|
return alternative_name_;
|
|
}
|
|
auto arguments() const -> const TuplePattern& { return *arguments_; }
|
|
auto arguments() -> TuplePattern& { return *arguments_; }
|
|
|
|
private:
|
|
static auto RequireSimpleMemberAccess(Nonnull<Expression*> alternative)
|
|
-> ErrorOr<Nonnull<SimpleMemberAccessExpression*>>;
|
|
|
|
Nonnull<Expression*> choice_type_;
|
|
std::string alternative_name_;
|
|
Nonnull<TuplePattern*> arguments_;
|
|
};
|
|
|
|
// A pattern that matches a value if it is equal to the value of a given
|
|
// expression.
|
|
class ExpressionPattern : public Pattern {
|
|
public:
|
|
explicit ExpressionPattern(Nonnull<Expression*> expression)
|
|
: Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()),
|
|
expression_(expression) {}
|
|
|
|
explicit ExpressionPattern(CloneContext& context,
|
|
const ExpressionPattern& other)
|
|
: Pattern(context, other),
|
|
expression_(context.Clone(other.expression_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromExpressionPattern(node->kind());
|
|
}
|
|
|
|
auto expression() const -> const Expression& { return *expression_; }
|
|
auto expression() -> Expression& { return *expression_; }
|
|
|
|
private:
|
|
Nonnull<Expression*> expression_;
|
|
};
|
|
|
|
} // namespace Carbon
|
|
|
|
#endif // CARBON_EXPLORER_AST_PATTERN_H_
|