From 53af8f04b2510f173b03d30a427feac813f5dd3a Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 30 Aug 2023 14:32:19 -0700 Subject: [PATCH] Provide a Printable CRTP parent to replace HasPrintable templates. (#3166) With the toolchain splitting namespaces, ostream.h's `operator<<` templates aren't reliably found with name lookup, likely due to the loss of associated namespaces (zygoloid commented on this at https://github.com/carbon-language/carbon-lang/pull/3161#discussion_r1307941999). This is especially a barrier to moving the lex files into `Carbon::Lex`; versus other parts of the toolchain, they contain more printable types which are used cross-namespace, including `Carbon::Testing`. As a consequence, I'm looking at migrating ostream.h to a more reliable approach that doesn't rely as much on everything being in the `Carbon` namespace. --- common/BUILD | 17 ---- common/enum_base.h | 10 +- common/enum_base_test.cpp | 10 +- common/error.h | 2 +- common/metaprogramming.h | 34 ------- common/metaprogramming_test.cpp | 32 ------- common/ostream.h | 94 ++++++++----------- explorer/ast/address.h | 7 +- explorer/ast/ast_node.h | 3 +- explorer/ast/bindings.h | 3 +- explorer/ast/declaration.h | 2 +- explorer/ast/element.h | 2 +- explorer/ast/element_path.h | 6 +- explorer/ast/return_term.h | 3 +- explorer/ast/value.h | 25 +++-- explorer/ast/value_node.h | 2 +- explorer/base/source_location.h | 4 +- explorer/interpreter/action.h | 6 +- explorer/interpreter/action_stack.h | 3 +- explorer/interpreter/heap.h | 4 +- explorer/interpreter/impl_scope.cpp | 3 +- explorer/interpreter/impl_scope.h | 2 +- explorer/interpreter/interpreter.cpp | 6 +- explorer/interpreter/type_checker.cpp | 3 +- explorer/interpreter/type_structure.h | 2 +- toolchain/base/index_base.h | 2 +- toolchain/lexer/tokenized_buffer.h | 7 +- .../lexer/tokenized_buffer_test_helpers.h | 15 +-- toolchain/parser/parse_tree.h | 8 +- toolchain/parser/parser_context.h | 2 +- toolchain/semantics/semantics_ir.h | 8 +- toolchain/semantics/semantics_node.h | 27 +++--- 32 files changed, 114 insertions(+), 240 deletions(-) delete mode 100644 common/metaprogramming.h delete mode 100644 common/metaprogramming_test.cpp diff --git a/common/BUILD b/common/BUILD index 82cd4d50f715..09dc556c04d3 100644 --- a/common/BUILD +++ b/common/BUILD @@ -122,27 +122,10 @@ cc_test( ], ) -cc_library( - name = "metaprogramming", - hdrs = ["metaprogramming.h"], -) - -cc_test( - name = "metaprogramming_test", - srcs = ["metaprogramming_test.cpp"], - deps = [ - ":metaprogramming", - "//testing/base:gtest_main", - "@com_google_googletest//:gtest", - "@llvm-project//llvm:Support", - ], -) - cc_library( name = "ostream", hdrs = ["ostream.h"], deps = [ - ":metaprogramming", "@llvm-project//llvm:Support", ], ) diff --git a/common/enum_base.h b/common/enum_base.h index ba18b30b7308..01d871970d14 100644 --- a/common/enum_base.h +++ b/common/enum_base.h @@ -52,7 +52,7 @@ namespace Carbon::Internal { // }; // ``` template -class EnumBase { +class EnumBase : public Printable { public: // An alias for the raw enum type. This is an implementation detail and // should rarely be used directly, only when an actual enum type is needed. @@ -83,14 +83,6 @@ class EnumBase { // Prints this value using its name. auto Print(llvm::raw_ostream& out) const -> void { out << name(); } - // TODO: common/ostream.h tries to provide this, but does not reliably work on - // EnumBase children. Try to find a more generic solution. - friend auto operator<<(llvm::raw_ostream& out, const DerivedT& obj) - -> llvm::raw_ostream& { - obj.Print(out); - return out; - } - protected: // The default constructor is explicitly defaulted (and constexpr) as a // protected constructor to allow derived classes to be constructed but not diff --git a/common/enum_base_test.cpp b/common/enum_base_test.cpp index ea51e8870c40..4744ed0cc6f5 100644 --- a/common/enum_base_test.cpp +++ b/common/enum_base_test.cpp @@ -8,7 +8,8 @@ #include "testing/base/test_raw_ostream.h" -namespace Carbon { +namespace Carbon::Testing { +namespace { // These are directly in the Carbon namespace because the defines require it. CARBON_DEFINE_RAW_ENUM_CLASS(TestKind, uint8_t) { @@ -34,8 +35,8 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(TestKind) = { #include "common/enum_base_test.def" }; -namespace Testing { -namespace { +static_assert(sizeof(TestKind) == sizeof(uint8_t), + "Class size doesn't match enum size!"); TEST(EnumBaseTest, NamesAndConstants) { EXPECT_EQ("Beep", TestKind::Beep.name()); @@ -108,5 +109,4 @@ TEST(EnumBaseTest, IntConversion) { } } // namespace -} // namespace Testing -} // namespace Carbon +} // namespace Carbon::Testing diff --git a/common/error.h b/common/error.h index d4b5d0578795..5c5a2c48e0db 100644 --- a/common/error.h +++ b/common/error.h @@ -20,7 +20,7 @@ namespace Carbon { struct Success {}; // Tracks an error message. -class [[nodiscard]] Error { +class [[nodiscard]] Error : public Printable { public: // Represents an error state. explicit Error(llvm::Twine location, llvm::Twine message) diff --git a/common/metaprogramming.h b/common/metaprogramming.h deleted file mode 100644 index 1415e0ebb507..000000000000 --- a/common/metaprogramming.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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_COMMON_METAPROGRAMMING_H_ -#define CARBON_COMMON_METAPROGRAMMING_H_ - -#include - -namespace Carbon { - -// C++17-compatible emulation of a C++20 `requires` expression, which queries -// whether a given expression is well-formed. The syntax is best explained -// in terms of an example: -// -// template -// static constexpr bool IsStreamableToRawOstream = -// Requires( -// [](auto&& t, auto&& out) -> decltype(out << t) {}); -// -// The expression enclosed in `decltype` is the expression whose validity the -// trait queries. The lambda parameters declare the names that are used in -// that expression, and the types of those names are specified by the -// corresponding template arguments of the `Requires` call. The lambda -// parameters should always have type `auto&&`, and the template arguments -// should not have `&` or `&&` qualifiers. -template -constexpr auto Requires(F /* f */) -> bool { - return std::is_invocable_v; -} - -} // namespace Carbon - -#endif // CARBON_COMMON_METAPROGRAMMING_H_ diff --git a/common/metaprogramming_test.cpp b/common/metaprogramming_test.cpp deleted file mode 100644 index 4a1d29a26465..000000000000 --- a/common/metaprogramming_test.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// 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 - -#include "common/metaprogramming.h" - -#include - -#include - -#include "llvm/Support/raw_ostream.h" - -namespace Carbon::Testing { -namespace { - -TEST(MetaProgrammingTest, RequiresBasic) { - bool result = Requires([](int a, int b) { return a + b; }); - EXPECT_TRUE(result); -} - -struct TypeWithPrint { - void Print(llvm::raw_ostream& os) const { os << "Test"; } -}; - -TEST(MetaProgrammingTest, RequiresPrintMethod) { - bool result = Requires( - [](auto&& t, auto&& out) -> decltype(t.Print(out)) {}); - EXPECT_TRUE(result); -} - -} // namespace -} // namespace Carbon::Testing diff --git a/common/ostream.h b/common/ostream.h index 6482cb2d8329..aa82f7436589 100644 --- a/common/ostream.h +++ b/common/ostream.h @@ -7,60 +7,48 @@ #include -#include "common/metaprogramming.h" #include "llvm/Support/raw_os_ostream.h" // Libraries should include this header instead of raw_ostream. +#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" // IWYU pragma: export namespace Carbon { -// True if T has a method `void Print(llvm::raw_ostream& out) const`. -template -static constexpr bool HasPrintMethod = Requires( - [](auto&& t, auto&& out) -> decltype(t.Print(out)) {}); - -// Support raw_ostream << for types which implement: -// void Print(llvm::raw_ostream& out) const; -template >> -auto operator<<(llvm::raw_ostream& out, const T& obj) -> llvm::raw_ostream& { - obj.Print(out); - return out; -} - -// Prevents raw_ostream << for pointers to printable types. -template >> -__attribute__((unavailable( - "Received a pointer to a printable type, are you missing a `*`? " - "To print as a pointer, cast to void*."))) auto -operator<<(llvm::raw_ostream& out, const T* /*obj*/) -> llvm::raw_ostream&; - -// Support std::ostream << for types which implement: -// void Print(llvm::raw_ostream& out) const; -template >> -auto operator<<(std::ostream& out, const T& obj) -> std::ostream& { - llvm::raw_os_ostream raw_os(out); - obj.Print(raw_os); - return out; -} - -// Prevents std::ostream << for pointers to printable types. -template >> -__attribute__((unavailable( - "Received a pointer to a printable type, are you missing a `*`? " - "To print as a pointer, cast to void*."))) auto -operator<<(std::ostream& out, const T* /*obj*/) -> std::ostream&; - -// Allow GoogleTest and GoogleMock to print even pointers by dereferencing them. -// This is important to allow automatic printing of arguments of mocked APIs. -template >> -void PrintTo(T* p, std::ostream* out) { - *out << static_cast(p); - - // Also print the object if non-null. - if (p) { - *out << " pointing to " << *p; +// CRTP base class for printable types. Children (DerivedT) must implement: +// - auto Print(llvm::raw_ostream& out) -> void +template +class Printable { + // Provides simple printing for debuggers. + LLVM_DUMP_METHOD void Dump() const { + static_cast(this)->Print(llvm::errs()); } -} + + // Supports printing to llvm::raw_ostream. + friend auto operator<<(llvm::raw_ostream& out, const DerivedT& obj) + -> llvm::raw_ostream& { + obj.Print(out); + return out; + } + + // Supports printing to std::ostream. + friend auto operator<<(std::ostream& out, const DerivedT& obj) + -> std::ostream& { + llvm::raw_os_ostream raw_os(out); + obj.Print(raw_os); + return out; + } + + // Allows GoogleTest and GoogleMock to print pointers by dereferencing them. + // This is important to allow automatic printing of arguments of mocked + // APIs. + friend auto PrintTo(DerivedT* p, std::ostream* out) -> void { + *out << static_cast(p); + // Also print the object if non-null. + if (p) { + *out << " pointing to " << *p; + } + } +}; } // namespace Carbon @@ -82,12 +70,12 @@ namespace llvm { // `raw_os_ostream.h` so that we wouldn't need to inject into LLVM's namespace, // but supporting `std::ostream` isn't a priority for LLVM so we handle it // locally instead. -template >>>, - typename = std::enable_if_t>, raw_ostream>>> -auto operator<<(S& standard_out, const T& value) -> S& { +template >>, + typename = std::enable_if_t< + !std::is_same_v, raw_ostream>>> +auto operator<<(StreamT& standard_out, const ClassT& value) -> StreamT& { raw_os_ostream(standard_out) << value; return standard_out; } diff --git a/explorer/ast/address.h b/explorer/ast/address.h index 445016961b79..6423cd05b2cb 100644 --- a/explorer/ast/address.h +++ b/explorer/ast/address.h @@ -12,14 +12,13 @@ #include "common/check.h" #include "common/ostream.h" #include "explorer/ast/element_path.h" -#include "llvm/Support/Compiler.h" namespace Carbon { // An AllocationId identifies an _allocation_ produced by a Heap. An allocation // is analogous to the C++ notion of a complete object: the `Value` in an // allocation is not a sub-part of any other `Value`. -class AllocationId { +class AllocationId : public Printable { public: AllocationId(const AllocationId&) = default; auto operator=(const AllocationId&) -> AllocationId& = default; @@ -52,7 +51,7 @@ class AllocationId { // An Address represents a memory address in the Carbon virtual machine. // Addresses are used to access values stored in a Heap. Unlike an // AllocationId, an Address can refer to a sub-Value of some larger Value. -class Address { +class Address : public Printable
{ public: // Constructs an `Address` that refers to the value stored in `allocation`. explicit Address(AllocationId allocation) : allocation_(allocation) {} @@ -70,8 +69,6 @@ class Address { out << allocation_ << element_path_; } - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - // If *this represents the address of an object with a field named // `field_name`, this method returns the address of that field. auto ElementAddress(Nonnull element) const -> Address { diff --git a/explorer/ast/ast_node.h b/explorer/ast/ast_node.h index 525c3e7f418b..a75f83bf03f6 100644 --- a/explorer/ast/ast_node.h +++ b/explorer/ast/ast_node.h @@ -46,7 +46,7 @@ class CloneContext; // // TODO: To support generic traversal, add children() method, and ensure that // all AstNodes are reachable from a root AstNode. -class AstNode { +class AstNode : public Printable { public: AstNode(AstNode&&) = delete; auto operator=(AstNode&&) -> AstNode& = delete; @@ -56,7 +56,6 @@ class AstNode { virtual void Print(llvm::raw_ostream& out) const = 0; // Print identifying information about the node, such as its name. virtual void PrintID(llvm::raw_ostream& out) const = 0; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Returns an enumerator specifying the concrete type of this node. // diff --git a/explorer/ast/bindings.h b/explorer/ast/bindings.h index e5b8ac963fc2..035f63b79e90 100644 --- a/explorer/ast/bindings.h +++ b/explorer/ast/bindings.h @@ -31,7 +31,7 @@ using ImplWitnessMap = // These are shared by a context and all unparameterized entities within that // context. For example, a class and the name of a method within that class // will have the same set of bindings. -class Bindings { +class Bindings : public Printable { public: // Gets an empty set of bindings. static auto None() -> Nonnull; @@ -66,7 +66,6 @@ class Bindings { } void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Add a value, and perhaps a witness, for a generic binding. void Add(Nonnull binding, Nonnull value, diff --git a/explorer/ast/declaration.h b/explorer/ast/declaration.h index d149220f43b8..c7659bcc0931 100644 --- a/explorer/ast/declaration.h +++ b/explorer/ast/declaration.h @@ -143,7 +143,7 @@ inline auto DeclaresSameEntity(const Declaration& first, } // A name being declared in a named declaration. -class DeclaredName { +class DeclaredName : public Printable { public: struct NameComponent { SourceLocation source_loc; diff --git a/explorer/ast/element.h b/explorer/ast/element.h index 07b7d56db949..7a132c71b3ab 100644 --- a/explorer/ast/element.h +++ b/explorer/ast/element.h @@ -44,7 +44,7 @@ struct NamedValue : HashFromDecompose { // types. As a result, all Elements must be immutable, and all their constructor // arguments must be copyable, equality-comparable, and hashable. See // Arena's documentation for details. -class Element { +class Element : public Printable { protected: explicit Element(ElementKind kind) : kind_(kind) {} diff --git a/explorer/ast/element_path.h b/explorer/ast/element_path.h index d88392b81af7..74d834ac37e2 100644 --- a/explorer/ast/element_path.h +++ b/explorer/ast/element_path.h @@ -33,7 +33,7 @@ class Witness; // there is only one kind of step, a string specifying a child field by name, // but that may change as Carbon develops. Note that an empty ElementPath // refers to the initial Value itself. -class ElementPath { +class ElementPath : public Printable { public: // Constructs an empty ElementPath. ElementPath() = default; @@ -42,7 +42,7 @@ class ElementPath { // of a field. However, inside a generic, when there is a field // access on something of a generic type, e.g., `T`, then we also // need `witness`, a pointer to the witness table containing that field. - class Component { + class Component : public Printable { public: explicit Component(Nonnull element) : element_(element) {} Component(Nonnull element, @@ -128,8 +128,6 @@ class ElementPath { } } - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - private: // The representation of ElementPath describes how to locate a Value within // another Value, so its implementation details are tied to the implementation diff --git a/explorer/ast/return_term.h b/explorer/ast/return_term.h index b7af6b6b3f0b..8153d5f3746f 100644 --- a/explorer/ast/return_term.h +++ b/explorer/ast/return_term.h @@ -25,7 +25,7 @@ class Value; // - An _auto_ term consists of `-> auto`. // - An _omitted_ term consists of no tokens at all. // Each of these forms has a corresponding factory function. -class ReturnTerm { +class ReturnTerm : public Printable { public: explicit ReturnTerm(CloneContext& context, const ReturnTerm& other) : kind_(other.kind_), @@ -80,7 +80,6 @@ class ReturnTerm { auto source_loc() const -> SourceLocation { return source_loc_; } void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } private: enum class ReturnKind { Omitted, Auto, Expression }; diff --git a/explorer/ast/value.h b/explorer/ast/value.h index 778eb2d01ccb..49211f9e164d 100644 --- a/explorer/ast/value.h +++ b/explorer/ast/value.h @@ -64,7 +64,7 @@ inline auto EmptyVTable() -> Nonnull { // As a result, all Values must be immutable, and all their constructor // arguments must be copyable, equality-comparable, and hashable. See // Arena's documentation for details. -class Value { +class Value : public Printable { public: using EnableCanonicalizedAllocation = void; enum class Kind { @@ -81,7 +81,6 @@ class Value { auto Visit(F f) const -> R; void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Returns the sub-Value specified by `path`, which must be a valid element // path for *this. If the sub-Value is a method and its self_pattern is an @@ -1062,7 +1061,19 @@ struct ImplsConstraint : public HashFromDecompose { }; // A constraint that requires an intrinsic property of a type. -struct IntrinsicConstraint : public HashFromDecompose { +struct IntrinsicConstraint : public HashFromDecompose, + public Printable { + enum Kind { + // `type` intrinsically implicitly converts to `parameters[0]`. + // TODO: Split ImplicitAs into more specific constraints (such as + // derived-to-base pointer conversions). + ImplicitAs, + }; + + explicit IntrinsicConstraint(Nonnull type, Kind kind, + std::vector> arguments) + : type(type), kind(kind), arguments(std::move(arguments)) {} + template auto Decompose(F f) const { return f(type, kind, arguments); @@ -1074,12 +1085,6 @@ struct IntrinsicConstraint : public HashFromDecompose { // The type that is required to satisfy the intrinsic property. Nonnull type; // The kind of the intrinsic property. - enum Kind { - // `type` intrinsically implicitly converts to `parameters[0]`. - // TODO: Split ImplicitAs into more specific constraints (such as - // derived-to-base pointer conversions). - ImplicitAs, - }; Kind kind; // Arguments for the intrinsic property. The meaning of these depends on // `kind`. @@ -1466,7 +1471,7 @@ class ParameterizedEntityName : public Value { // These values are used to represent the second operand of a compound member // access expression: `x.(A.B)`, and can also be the value of an alias // declaration, but cannot be used in most other contexts. -class MemberName : public Value { +class MemberName : public Value, public Printable { public: MemberName(std::optional> base_type, std::optional> interface, diff --git a/explorer/ast/value_node.h b/explorer/ast/value_node.h index 39b61f547e73..cad19e17c82a 100644 --- a/explorer/ast/value_node.h +++ b/explorer/ast/value_node.h @@ -56,7 +56,7 @@ template static constexpr bool ImplementsValueNode = true; -class ValueNodeView { +class ValueNodeView : public Printable { public: template >> diff --git a/explorer/base/source_location.h b/explorer/base/source_location.h index 0067bedec4b2..1d746685426a 100644 --- a/explorer/base/source_location.h +++ b/explorer/base/source_location.h @@ -16,7 +16,7 @@ namespace Carbon { // Describes the kind of file that the source location is within. enum class FileKind { Main, Prelude, Import, Unknown, Last = Unknown }; -class SourceLocation { +class SourceLocation : public Printable { public: // Produce a source location that is known to not be used, because it is fed // into an operation that creates no AST nodes and whose diagnostics are @@ -62,8 +62,6 @@ class SourceLocation { return result; } - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - private: std::string_view filename_; int line_num_; diff --git a/explorer/interpreter/action.h b/explorer/interpreter/action.h index 46851f431863..b6de1cf0c9cf 100644 --- a/explorer/interpreter/action.h +++ b/explorer/interpreter/action.h @@ -30,7 +30,7 @@ namespace Carbon { // A RuntimeScope manages and provides access to the storage for names that are // not compile-time constants. -class RuntimeScope { +class RuntimeScope : public Printable { public: // Returns a RuntimeScope whose Get() operation for a given name returns the // storage owned by the first entry in `scopes` that defines that name. This @@ -48,7 +48,6 @@ class RuntimeScope { auto operator=(RuntimeScope&&) noexcept -> RuntimeScope&; void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Allocates storage for `value_node` in `heap`, and initializes it with // `value`. @@ -109,7 +108,7 @@ class RuntimeScope { // The actual behavior of an Action step is defined by Interpreter::Step, not by // Action or its subclasses. // TODO: consider moving this logic to a virtual method `Step`. -class Action { +class Action : public Printable { public: enum class Kind { LocationAction, @@ -131,7 +130,6 @@ class Action { virtual ~Action() = default; void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Resets this Action to its initial state. void Clear() { diff --git a/explorer/interpreter/action_stack.h b/explorer/interpreter/action_stack.h index 5f70fd7ce589..c2c59240eb7b 100644 --- a/explorer/interpreter/action_stack.h +++ b/explorer/interpreter/action_stack.h @@ -21,7 +21,7 @@ namespace Carbon { enum class Phase { CompileTime, RunTime }; // The stack of Actions currently being executed by the interpreter. -class ActionStack { +class ActionStack : public Printable { public: // Constructs an empty compile-time ActionStack. explicit ActionStack(Nonnull trace_stream) @@ -36,7 +36,6 @@ class ActionStack { trace_stream_(trace_stream) {} void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } // Starts execution with `action` at the top of the stack. Cannot be called // when IsEmpty() is false. diff --git a/explorer/interpreter/heap.h b/explorer/interpreter/heap.h index 13ef47f4be6a..d1e25c540d70 100644 --- a/explorer/interpreter/heap.h +++ b/explorer/interpreter/heap.h @@ -19,7 +19,7 @@ namespace Carbon { // A Heap represents the abstract machine's dynamically allocated memory. -class Heap : public HeapAllocationInterface { +class Heap : public HeapAllocationInterface, public Printable { public: enum class ValueState { Uninitialized, @@ -70,8 +70,6 @@ class Heap : public HeapAllocationInterface { // Print all the values on the heap to the stream `out`. void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - auto arena() const -> Arena& override { return *arena_; } private: diff --git a/explorer/interpreter/impl_scope.cpp b/explorer/interpreter/impl_scope.cpp index 18543dc4b12f..e5e4e637cbd8 100644 --- a/explorer/interpreter/impl_scope.cpp +++ b/explorer/interpreter/impl_scope.cpp @@ -183,8 +183,7 @@ auto ImplScope::TryResolve(Nonnull constraint_type, CARBON_ASSIGN_OR_RETURN( Nonnull type, type_checker.Substitute(local_bindings, intrinsic.type)); - IntrinsicConstraint converted = { - .type = type, .kind = intrinsic.kind, .arguments = {}}; + IntrinsicConstraint converted(type, intrinsic.kind, {}); converted.arguments.reserve(intrinsic.arguments.size()); for (Nonnull argument : intrinsic.arguments) { CARBON_ASSIGN_OR_RETURN( diff --git a/explorer/interpreter/impl_scope.h b/explorer/interpreter/impl_scope.h index 1f9ed8650f1a..06f67216d815 100644 --- a/explorer/interpreter/impl_scope.h +++ b/explorer/interpreter/impl_scope.h @@ -41,7 +41,7 @@ class TypeChecker; // // `ImplScope` also tracks the type equalities that are known in a particular // scope. -class ImplScope { +class ImplScope : public Printable { public: // The `ImplFact` struct is a key-value pair where the key is the // combination of a type and an interface, e.g., `List` and `Container`, diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 8c4779987fbf..2fb3231c95c8 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -1723,10 +1723,8 @@ auto Interpreter::StepExp() -> ErrorOr { self_binding->set_symbolic_identity(self); self_binding->set_value(self); self_binding->set_impl_binding(impl_binding); - IntrinsicConstraint constraint = { - .type = self, - .kind = IntrinsicConstraint::ImplicitAs, - .arguments = args}; + IntrinsicConstraint constraint(self, IntrinsicConstraint::ImplicitAs, + args); auto* result = arena_->New( self_binding, std::vector{}, std::vector{std::move(constraint)}, diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 2a2627c3e0fa..ee85317a0ae9 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -1744,8 +1744,7 @@ class TypeChecker::ConstraintTypeBuilder { CARBON_ASSIGN_OR_RETURN( Nonnull type, type_checker.Substitute(local_bindings, intrinsic_constraint.type)); - IntrinsicConstraint converted = { - .type = type, .kind = intrinsic_constraint.kind, .arguments = {}}; + IntrinsicConstraint converted(type, intrinsic_constraint.kind, {}); converted.arguments.reserve(intrinsic_constraint.arguments.size()); for (Nonnull argument : intrinsic_constraint.arguments) { CARBON_ASSIGN_OR_RETURN( diff --git a/explorer/interpreter/type_structure.h b/explorer/interpreter/type_structure.h index 862fb7b152db..f24e83d05d8d 100644 --- a/explorer/interpreter/type_structure.h +++ b/explorer/interpreter/type_structure.h @@ -27,7 +27,7 @@ class Value; // This class extracts the relevant information needed to order type // structures, and provides a weak ordering over type structures in which type // structures that provide a better match compare earlier. -class TypeStructureSortKey { +class TypeStructureSortKey : public Printable { public: // Compute the sort key for `impl type as interface`. static auto ForImpl(Nonnull type, diff --git a/toolchain/base/index_base.h b/toolchain/base/index_base.h index 4c156c93dc22..e8755528c254 100644 --- a/toolchain/base/index_base.h +++ b/toolchain/base/index_base.h @@ -18,7 +18,7 @@ class DataIterator; // // DataIndex is designed to be passed by value, not reference or pointer. They // are also designed to be small and efficient to store in data structures. -struct IndexBase { +struct IndexBase : public Printable { static constexpr int32_t InvalidIndex = -1; IndexBase() = delete; diff --git a/toolchain/lexer/tokenized_buffer.h b/toolchain/lexer/tokenized_buffer.h index f9912170f863..7ece0b6cfddc 100644 --- a/toolchain/lexer/tokenized_buffer.h +++ b/toolchain/lexer/tokenized_buffer.h @@ -33,7 +33,7 @@ class TokenizedBuffer; // // Lexing errors result in a potentially incomplete sequence of tokens and // `HasError` returning true. -class TokenizedBuffer { +class TokenizedBuffer : public Printable { public: // A lightweight handle to a lexed token in a `TokenizedBuffer`. // @@ -87,7 +87,8 @@ class TokenizedBuffer { // Random-access iterator over tokens within the buffer. class TokenIterator : public llvm::iterator_facade_base< - TokenIterator, std::random_access_iterator_tag, const Token, int> { + TokenIterator, std::random_access_iterator_tag, const Token, int>, + public Printable { public: TokenIterator() = delete; @@ -132,7 +133,7 @@ class TokenizedBuffer { // // The `TokenizedBuffer` must outlive any `RealLiteralValue`s referring to // its tokens. - class RealLiteralValue { + class RealLiteralValue : public Printable { public: // The mantissa, represented as an unsigned integer. [[nodiscard]] auto Mantissa() const -> const llvm::APInt& { diff --git a/toolchain/lexer/tokenized_buffer_test_helpers.h b/toolchain/lexer/tokenized_buffer_test_helpers.h index 6b346f710805..dd5a9a545b92 100644 --- a/toolchain/lexer/tokenized_buffer_test_helpers.h +++ b/toolchain/lexer/tokenized_buffer_test_helpers.h @@ -11,17 +11,7 @@ #include "llvm/Support/YAMLParser.h" #include "toolchain/lexer/tokenized_buffer.h" -namespace Carbon { - -inline void PrintTo(const TokenizedBuffer& buffer, std::ostream* output) { - std::string message; - llvm::raw_string_ostream message_stream(message); - message_stream << "\n"; - buffer.Print(message_stream); - *output << message_stream.str(); -} - -namespace Testing { +namespace Carbon::Testing { struct ExpectedToken { friend auto operator<<(std::ostream& output, const ExpectedToken& expected) @@ -150,7 +140,6 @@ MATCHER_P(HasTokens, raw_all_expected, "") { return matches; } -} // namespace Testing -} // namespace Carbon +} // namespace Carbon::Testing #endif // CARBON_TOOLCHAIN_LEXER_TOKENIZED_BUFFER_TEST_HELPERS_H_ diff --git a/toolchain/parser/parse_tree.h b/toolchain/parser/parse_tree.h index 9b6c5c4feab2..85dc97502279 100644 --- a/toolchain/parser/parse_tree.h +++ b/toolchain/parser/parse_tree.h @@ -57,7 +57,7 @@ constexpr Node Node::Invalid = Node(Node::InvalidIndex); // The tree is immutable once built, but is designed to support reasonably // efficient patterns that build a new tree with a specific transformation // applied. -class Tree { +class Tree : public Printable { public: class PostorderIterator; class SiblingIterator; @@ -255,7 +255,8 @@ class Tree { class Tree::PostorderIterator : public llvm::iterator_facade_base { + int, Node*, Node>, + public Printable { public: PostorderIterator() = delete; @@ -305,7 +306,8 @@ class Tree::PostorderIterator // relative order of siblings matches their RPO order. class Tree::SiblingIterator : public llvm::iterator_facade_base< - SiblingIterator, std::forward_iterator_tag, Node, int, Node*, Node> { + SiblingIterator, std::forward_iterator_tag, Node, int, Node*, Node>, + public Printable { public: explicit SiblingIterator() = delete; diff --git a/toolchain/parser/parser_context.h b/toolchain/parser/parser_context.h index 4ae6f502594f..7136edaa2948 100644 --- a/toolchain/parser/parser_context.h +++ b/toolchain/parser/parser_context.h @@ -40,7 +40,7 @@ class Context { }; // Used to track state on state_stack_. - struct StateStackEntry { + struct StateStackEntry : public Printable { explicit StateStackEntry(State state, PrecedenceGroup ambient_precedence, PrecedenceGroup lhs_precedence, TokenizedBuffer::Token token, diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 4c63e706e468..4aba1087ecd6 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/toolchain/semantics/semantics_ir.h @@ -15,7 +15,7 @@ namespace Carbon::SemIR { // A function. -struct Function { +struct Function : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{name: " << name_id << ", " << "param_refs: " << param_refs_id; @@ -32,7 +32,6 @@ struct Function { } out << "}"; } - LLVM_DUMP_METHOD void Dump() const { llvm::errs() << *this; } // The function name. StringId name_id; @@ -52,12 +51,11 @@ struct Function { llvm::SmallVector body_block_ids; }; -struct RealLiteral { +struct RealLiteral : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{mantissa: " << mantissa << ", exponent: " << exponent << ", is_decimal: " << is_decimal << "}"; } - LLVM_DUMP_METHOD void Dump() const { llvm::errs() << *this; } llvm::APInt mantissa; llvm::APInt exponent; @@ -68,7 +66,7 @@ struct RealLiteral { }; // Provides semantic analysis on a Parse::Tree. -class File { +class File : public Printable { public: // Produces the builtins. static auto MakeBuiltinIR() -> File; diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index c6ec4e5cbe18..f49610c32a3f 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -17,7 +17,7 @@ namespace Carbon::SemIR { // The ID of a node. -struct NodeId : public IndexBase { +struct NodeId : public IndexBase, public Printable { // An explicitly invalid node ID. static const NodeId Invalid; @@ -50,7 +50,7 @@ constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex); #include "toolchain/semantics/semantics_builtin_kind.def" // The ID of a function. -struct FunctionId : public IndexBase { +struct FunctionId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "function"; @@ -59,7 +59,8 @@ struct FunctionId : public IndexBase { }; // The ID of a cross-referenced IR. -struct CrossReferenceIRId : public IndexBase { +struct CrossReferenceIRId : public IndexBase, + public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "ir"; @@ -68,7 +69,7 @@ struct CrossReferenceIRId : public IndexBase { }; // A boolean value. -struct BoolValue : public IndexBase { +struct BoolValue : public IndexBase, public Printable { static const BoolValue False; static const BoolValue True; @@ -91,7 +92,7 @@ constexpr BoolValue BoolValue::False = BoolValue(0); constexpr BoolValue BoolValue::True = BoolValue(1); // The ID of an integer literal. -struct IntegerLiteralId : public IndexBase { +struct IntegerLiteralId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "int"; @@ -100,7 +101,7 @@ struct IntegerLiteralId : public IndexBase { }; // The ID of a name scope. -struct NameScopeId : public IndexBase { +struct NameScopeId : public IndexBase, public Printable { // An explicitly invalid ID. static const NameScopeId Invalid; @@ -115,7 +116,7 @@ constexpr NameScopeId NameScopeId::Invalid = NameScopeId(NameScopeId::InvalidIndex); // The ID of a node block. -struct NodeBlockId : public IndexBase { +struct NodeBlockId : public IndexBase, public Printable { // All File instances must provide the 0th node block as empty. static const NodeBlockId Empty; @@ -143,7 +144,7 @@ constexpr NodeBlockId NodeBlockId::Unreachable = NodeBlockId(NodeBlockId::InvalidIndex - 1); // The ID of a real literal. -struct RealLiteralId : public IndexBase { +struct RealLiteralId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "real"; @@ -152,7 +153,7 @@ struct RealLiteralId : public IndexBase { }; // The ID of a string. -struct StringId : public IndexBase { +struct StringId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "str"; @@ -161,7 +162,7 @@ struct StringId : public IndexBase { }; // The ID of a node block. -struct TypeId : public IndexBase { +struct TypeId : public IndexBase, public Printable { // The builtin TypeType. static const TypeId TypeType; @@ -189,7 +190,7 @@ constexpr TypeId TypeId::Error = TypeId(TypeId::InvalidIndex - 1); constexpr TypeId TypeId::Invalid = TypeId(TypeId::InvalidIndex); // The ID of a type block. -struct TypeBlockId : public IndexBase { +struct TypeBlockId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "typeBlock"; @@ -198,7 +199,7 @@ struct TypeBlockId : public IndexBase { }; // An index for member access. -struct MemberIndex : public IndexBase { +struct MemberIndex : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "member"; @@ -226,7 +227,7 @@ struct MemberIndex : public IndexBase { // // Internally, each Kind uses the `Factory*` types to provide a boilerplate // `Make` and `Get` methods. -class Node { +class Node : public Printable { public: struct NoArgs {};