declarable.h 22.6 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_TORQUE_DECLARABLE_H_
#define V8_TORQUE_DECLARABLE_H_

8
#include <cassert>
9
#include <string>
10
#include <unordered_map>
11

12
#include "src/base/functional.h"
Marja Hölttä's avatar
Marja Hölttä committed
13
#include "src/base/logging.h"
14
#include "src/torque/ast.h"
15 16 17 18 19 20 21 22
#include "src/torque/types.h"
#include "src/torque/utils.h"

namespace v8 {
namespace internal {
namespace torque {

class Scope;
23
class Namespace;
24
class TypeArgumentInference;
25 26

DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*);
27

28 29 30 31 32 33 34 35 36 37 38
struct QualifiedName {
  std::vector<std::string> namespace_qualification;
  std::string name;

  QualifiedName(std::vector<std::string> namespace_qualification,
                std::string name)
      : namespace_qualification(std::move(namespace_qualification)),
        name(std::move(name)) {}
  explicit QualifiedName(std::string name)
      : QualifiedName({}, std::move(name)) {}

39 40 41 42 43 44 45 46 47 48 49
  bool HasNamespaceQualification() const {
    return !namespace_qualification.empty();
  }

  QualifiedName DropFirstNamespaceQualification() const {
    return QualifiedName{
        std::vector<std::string>(namespace_qualification.begin() + 1,
                                 namespace_qualification.end()),
        name};
  }

50 51 52
  friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name);
};

53 54
class Declarable {
 public:
55
  virtual ~Declarable() = default;
56
  enum Kind {
57
    kNamespace,
58 59
    kTorqueMacro,
    kExternMacro,
60
    kMethod,
61
    kBuiltin,
62
    kRuntimeFunction,
63
    kIntrinsic,
64 65
    kGenericCallable,
    kGenericType,
66
    kTypeAlias,
67
    kExternConstant,
68
    kNamespaceConstant
69 70
  };
  Kind kind() const { return kind_; }
71
  bool IsNamespace() const { return kind() == kNamespace; }
72 73
  bool IsMacro() const { return IsTorqueMacro() || IsExternMacro(); }
  bool IsTorqueMacro() const { return kind() == kTorqueMacro || IsMethod(); }
74
  bool IsMethod() const { return kind() == kMethod; }
75
  bool IsExternMacro() const { return kind() == kExternMacro; }
76
  bool IsIntrinsic() const { return kind() == kIntrinsic; }
77
  bool IsBuiltin() const { return kind() == kBuiltin; }
78
  bool IsRuntimeFunction() const { return kind() == kRuntimeFunction; }
79 80
  bool IsGenericCallable() const { return kind() == kGenericCallable; }
  bool IsGenericType() const { return kind() == kGenericType; }
81
  bool IsTypeAlias() const { return kind() == kTypeAlias; }
82
  bool IsExternConstant() const { return kind() == kExternConstant; }
83 84 85
  bool IsNamespaceConstant() const { return kind() == kNamespaceConstant; }
  bool IsValue() const { return IsExternConstant() || IsNamespaceConstant(); }
  bool IsScope() const { return IsNamespace() || IsCallable(); }
86
  bool IsCallable() const {
87 88
    return IsMacro() || IsBuiltin() || IsRuntimeFunction() || IsIntrinsic() ||
           IsMethod();
89
  }
90
  virtual const char* type_name() const { return "<<unknown>>"; }
91
  Scope* ParentScope() const { return parent_scope_; }
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

  // The SourcePosition of the whole declarable. For example, for a macro
  // this will encompass not only the signature, but also the body.
  SourcePosition Position() const { return position_; }
  void SetPosition(const SourcePosition& position) { position_ = position; }

  // The SourcePosition of the identifying name of the declarable. For example,
  // for a macro this will be the SourcePosition of the name.
  // Note that this SourcePosition might not make sense for all kinds of
  // declarables, in that case, the default SourcePosition is returned.
  SourcePosition IdentifierPosition() const {
    return identifier_position_.source.IsValid() ? identifier_position_
                                                 : position_;
  }
  void SetIdentifierPosition(const SourcePosition& position) {
    identifier_position_ = position;
  }
109

110 111 112 113 114
  bool IsUserDefined() const { return is_user_defined_; }
  void SetIsUserDefined(bool is_user_defined) {
    is_user_defined_ = is_user_defined;
  }

115 116 117
 protected:
  explicit Declarable(Kind kind) : kind_(kind) {}

118
 private:
119
  const Kind kind_;
120
  Scope* const parent_scope_ = CurrentScope::Get();
121 122
  SourcePosition position_ = CurrentSourcePosition::Get();
  SourcePosition identifier_position_ = SourcePosition::Invalid();
123
  bool is_user_defined_ = true;
124 125
};

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#define DECLARE_DECLARABLE_BOILERPLATE(x, y)                  \
  static x* cast(Declarable* declarable) {                    \
    DCHECK(declarable->Is##x());                              \
    return static_cast<x*>(declarable);                       \
  }                                                           \
  static const x* cast(const Declarable* declarable) {        \
    DCHECK(declarable->Is##x());                              \
    return static_cast<const x*>(declarable);                 \
  }                                                           \
  const char* type_name() const override { return #y; }       \
  static x* DynamicCast(Declarable* declarable) {             \
    if (!declarable) return nullptr;                          \
    if (!declarable->Is##x()) return nullptr;                 \
    return static_cast<x*>(declarable);                       \
  }                                                           \
  static const x* DynamicCast(const Declarable* declarable) { \
    if (!declarable) return nullptr;                          \
    if (!declarable->Is##x()) return nullptr;                 \
    return static_cast<const x*>(declarable);                 \
  }
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
// Information about what code caused a specialization to exist. This is used
// for error reporting.
struct SpecializationRequester {
  // The position of the expression that caused this specialization.
  SourcePosition position;
  // The Scope which contains the expression that caused this specialization.
  // It may in turn also be within a specialization, which allows us to print
  // the stack of requesters when an error occurs.
  Scope* scope;
  // The name of the specialization.
  std::string name;

  static SpecializationRequester None() {
    return {SourcePosition::Invalid(), nullptr, ""};
  }

  bool IsNone() const {
    return position == SourcePosition::Invalid() && scope == nullptr &&
           name == "";
  }
167 168
  SpecializationRequester(SourcePosition position, Scope* scope,
                          std::string name);
169 170
};

171 172
class Scope : public Declarable {
 public:
173
  DECLARE_DECLARABLE_BOILERPLATE(Scope, scope)
174 175
  explicit Scope(Declarable::Kind kind) : Declarable(kind) {}

176
  std::vector<Declarable*> LookupShallow(const QualifiedName& name) {
177
    if (!name.HasNamespaceQualification()) return declarations_[name.name];
178 179 180 181 182 183 184 185 186 187 188 189
    Scope* child = nullptr;
    for (Declarable* declarable :
         declarations_[name.namespace_qualification.front()]) {
      if (Scope* scope = Scope::DynamicCast(declarable)) {
        if (child != nullptr) {
          ReportError("ambiguous reference to scope ",
                      name.namespace_qualification.front());
        }
        child = scope;
      }
    }
    if (child == nullptr) return {};
190
    return child->LookupShallow(name.DropFirstNamespaceQualification());
191 192
  }

193
  std::vector<Declarable*> Lookup(const QualifiedName& name);
194 195 196 197 198 199
  template <class T>
  T* AddDeclarable(const std::string& name, T* declarable) {
    declarations_[name].push_back(declarable);
    return declarable;
  }

200 201 202 203 204 205 206
  const SpecializationRequester& GetSpecializationRequester() const {
    return requester_;
  }
  void SetSpecializationRequester(const SpecializationRequester& requester) {
    requester_ = requester;
  }

207 208
 private:
  std::unordered_map<std::string, std::vector<Declarable*>> declarations_;
209 210 211 212 213

  // If this Scope was created for specializing a generic type or callable,
  // then {requester_} refers to the place that caused the specialization so we
  // can construct useful error messages.
  SpecializationRequester requester_ = SpecializationRequester::None();
214 215
};

216
class Namespace : public Scope {
217
 public:
218
  DECLARE_DECLARABLE_BOILERPLATE(Namespace, namespace)
219 220
  explicit Namespace(const std::string& name)
      : Scope(Declarable::kNamespace), name_(name) {}
221
  const std::string& name() const { return name_; }
222 223
  bool IsDefaultNamespace() const;
  bool IsTestNamespace() const;
224 225 226 227 228

 private:
  std::string name_;
};

229
inline Namespace* CurrentNamespace() {
230 231
  Scope* scope = CurrentScope::Get();
  while (true) {
232 233
    if (Namespace* n = Namespace::DynamicCast(scope)) {
      return n;
234 235 236 237 238
    }
    scope = scope->ParentScope();
  }
}

239 240
class Value : public Declarable {
 public:
241
  DECLARE_DECLARABLE_BOILERPLATE(Value, value)
242
  const Identifier* name() const { return name_; }
243
  virtual bool IsConst() const { return true; }
244
  VisitResult value() const { return *value_; }
245
  const Type* type() const { return type_; }
246

247 248 249 250 251
  void set_value(VisitResult value) {
    DCHECK(!value_);
    value_ = value;
  }

252
 protected:
253
  Value(Kind kind, const Type* type, Identifier* name)
254 255
      : Declarable(kind), type_(type), name_(name) {}

256
 private:
257
  const Type* type_;
258
  Identifier* name_;
259
  base::Optional<VisitResult> value_;
260 261
};

262
class NamespaceConstant : public Value {
263
 public:
264
  DECLARE_DECLARABLE_BOILERPLATE(NamespaceConstant, constant)
265

266 267
  const std::string& external_name() const { return external_name_; }
  Expression* body() const { return body_; }
268 269 270

 private:
  friend class Declarations;
271 272
  explicit NamespaceConstant(Identifier* constant_name,
                             std::string external_name, const Type* type,
273 274
                             Expression* body)
      : Value(Declarable::kNamespaceConstant, type, constant_name),
275
        external_name_(std::move(external_name)),
276
        body_(body) {}
277

278
  std::string external_name_;
279
  Expression* body_;
280 281
};

282
class ExternConstant : public Value {
283
 public:
284
  DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant)
285 286

 private:
287
  friend class Declarations;
288 289
  explicit ExternConstant(Identifier* name, const Type* type, std::string value)
      : Value(Declarable::kExternConstant, type, name) {
290 291
    set_value(VisitResult(type, std::move(value)));
  }
292 293
};

294 295 296 297 298
enum class OutputType {
  kCSA,
  kCC,
};

299
class Callable : public Scope {
300
 public:
301
  DECLARE_DECLARABLE_BOILERPLATE(Callable, callable)
302 303
  const std::string& ExternalName() const { return external_name_; }
  const std::string& ReadableName() const { return readable_name_; }
304
  const Signature& signature() const { return signature_; }
305
  bool IsTransitioning() const { return signature().transitioning; }
306 307 308 309
  const NameVector& parameter_names() const {
    return signature_.parameter_names;
  }
  bool HasReturnValue() const {
310
    return !signature_.return_type->IsVoidOrNever();
311 312 313
  }
  void IncrementReturns() { ++returns_; }
  bool HasReturns() const { return returns_; }
314 315
  base::Optional<Statement*> body() const { return body_; }
  bool IsExternal() const { return !body_.has_value(); }
316 317 318 319 320 321 322 323 324
  virtual bool ShouldBeInlined(OutputType output_type) const {
    // C++ output doesn't support exiting to labels, so functions with labels in
    // the signature must be inlined.
    return output_type == OutputType::kCC && !signature().labels.empty();
  }
  bool ShouldGenerateExternalCode(OutputType output_type) const {
    return !ShouldBeInlined(output_type);
  }

325 326 327 328 329 330 331
  static std::string PrefixNameForCCOutput(const std::string& name) {
    // If a Torque macro requires a C++ runtime function to be generated, then
    // the generated function begins with this prefix to avoid any naming
    // collisions with the generated CSA function for the same macro.
    return "TqRuntime" + name;
  }

332
  // Name to use in runtime C++ code.
333 334 335
  virtual std::string CCName() const {
    return PrefixNameForCCOutput(ExternalName());
  }
336

337
 protected:
338
  Callable(Declarable::Kind kind, std::string external_name,
339
           std::string readable_name, Signature signature,
340 341
           base::Optional<Statement*> body)
      : Scope(kind),
342 343 344 345
        external_name_(std::move(external_name)),

        readable_name_(std::move(readable_name)),
        signature_(std::move(signature)),
346
        returns_(0),
347 348 349
        body_(body) {
    DCHECK(!body || *body);
  }
350

351
 private:
352 353
  std::string external_name_;
  std::string readable_name_;
354 355
  Signature signature_;
  size_t returns_;
356
  base::Optional<Statement*> body_;
357 358 359 360
};

class Macro : public Callable {
 public:
361
  DECLARE_DECLARABLE_BOILERPLATE(Macro, macro)
362
  bool ShouldBeInlined(OutputType output_type) const override {
363 364
    for (const LabelDeclaration& label : signature().labels) {
      for (const Type* type : label.types) {
365
        if (type->StructSupertype()) return true;
366 367
      }
    }
368 369 370
    // Intrinsics that are used internally in Torque and implemented as torque
    // code should be inlined and not generate C++ definitions.
    if (ReadableName()[0] == '%') return true;
371
    return Callable::ShouldBeInlined(output_type);
372
  }
373

374 375 376
  void SetUsed() { used_ = true; }
  bool IsUsed() const { return used_; }

377 378
 protected:
  Macro(Declarable::Kind kind, std::string external_name,
379
        std::string readable_name, const Signature& signature,
380
        base::Optional<Statement*> body)
381
      : Callable(kind, std::move(external_name), std::move(readable_name),
382
                 signature, body),
383
        used_(false) {
384 385 386 387
    if (signature.parameter_types.var_args) {
      ReportError("Varargs are not supported for macros.");
    }
  }
388 389 390

 private:
  bool used_;
391 392 393 394 395 396 397 398 399
};

class ExternMacro : public Macro {
 public:
  DECLARE_DECLARABLE_BOILERPLATE(ExternMacro, ExternMacro)

  const std::string& external_assembler_name() const {
    return external_assembler_name_;
  }
400

401 402 403 404 405
  std::string CCName() const override {
    return "TorqueRuntimeMacroShims::" + external_assembler_name() +
           "::" + ExternalName();
  }

406 407
 private:
  friend class Declarations;
408
  ExternMacro(const std::string& name, std::string external_assembler_name,
409
              Signature signature)
410
      : Macro(Declarable::kExternMacro, name, name, std::move(signature),
411
              base::nullopt),
412
        external_assembler_name_(std::move(external_assembler_name)) {}
413

414
  std::string external_assembler_name_;
415 416
};

417 418 419 420
class TorqueMacro : public Macro {
 public:
  DECLARE_DECLARABLE_BOILERPLATE(TorqueMacro, TorqueMacro)
  bool IsExportedToCSA() const { return exported_to_csa_; }
421
  std::string CCName() const override {
422 423
    // Exported functions must have unique and C++-friendly readable names, so
    // prefer those wherever possible.
424 425
    return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName()
                                                   : ExternalName());
426
  }
427 428 429 430

 protected:
  TorqueMacro(Declarable::Kind kind, std::string external_name,
              std::string readable_name, const Signature& signature,
431 432
              base::Optional<Statement*> body, bool is_user_defined,
              bool exported_to_csa)
433
      : Macro(kind, std::move(external_name), std::move(readable_name),
434
              signature, body),
435 436 437 438 439 440 441
        exported_to_csa_(exported_to_csa) {
    SetIsUserDefined(is_user_defined);
  }

 private:
  friend class Declarations;
  TorqueMacro(std::string external_name, std::string readable_name,
442 443
              const Signature& signature, base::Optional<Statement*> body,
              bool is_user_defined, bool exported_to_csa)
444
      : TorqueMacro(Declarable::kTorqueMacro, std::move(external_name),
445 446
                    std::move(readable_name), signature, body, is_user_defined,
                    exported_to_csa) {}
447 448 449 450 451

  bool exported_to_csa_ = false;
};

class Method : public TorqueMacro {
452
 public:
453
  DECLARE_DECLARABLE_BOILERPLATE(Method, Method)
454 455
  bool ShouldBeInlined(OutputType output_type) const override {
    return Macro::ShouldBeInlined(output_type) ||
456 457 458
           signature()
               .parameter_types.types[signature().implicit_count]
               ->IsStructType();
459 460 461 462 463 464
  }
  AggregateType* aggregate_type() const { return aggregate_type_; }

 private:
  friend class Declarations;
  Method(AggregateType* aggregate_type, std::string external_name,
465
         std::string readable_name, const Signature& signature, Statement* body)
466
      : TorqueMacro(Declarable::kMethod, std::move(external_name),
467
                    std::move(readable_name), signature, body, true, false),
468 469 470 471
        aggregate_type_(aggregate_type) {}
  AggregateType* aggregate_type_;
};

472 473
class Builtin : public Callable {
 public:
474
  enum Kind { kStub, kFixedArgsJavaScript, kVarArgsJavaScript };
475
  DECLARE_DECLARABLE_BOILERPLATE(Builtin, builtin)
476 477 478 479
  Kind kind() const { return kind_; }
  bool IsStub() const { return kind_ == kStub; }
  bool IsVarArgsJavaScript() const { return kind_ == kVarArgsJavaScript; }
  bool IsFixedArgsJavaScript() const { return kind_ == kFixedArgsJavaScript; }
480 481

 private:
482
  friend class Declarations;
483
  Builtin(std::string external_name, std::string readable_name,
484
          Builtin::Kind kind, const Signature& signature,
485
          base::Optional<Statement*> body)
486
      : Callable(Declarable::kBuiltin, std::move(external_name),
487
                 std::move(readable_name), signature, body),
488
        kind_(kind) {}
489

490
  Kind kind_;
491 492
};

493
class RuntimeFunction : public Callable {
494
 public:
495
  DECLARE_DECLARABLE_BOILERPLATE(RuntimeFunction, runtime)
496 497

 private:
498
  friend class Declarations;
499
  RuntimeFunction(const std::string& name, const Signature& signature)
500
      : Callable(Declarable::kRuntimeFunction, name, name, signature,
501
                 base::nullopt) {}
502 503
};

504 505
class Intrinsic : public Callable {
 public:
506
  DECLARE_DECLARABLE_BOILERPLATE(Intrinsic, intrinsic)
507 508 509 510

 private:
  friend class Declarations;
  Intrinsic(std::string name, const Signature& signature)
511
      : Callable(Declarable::kIntrinsic, name, name, signature, base::nullopt) {
512 513 514 515 516 517
    if (signature.parameter_types.var_args) {
      ReportError("Varargs are not supported for intrinsics.");
    }
  }
};

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
class TypeConstraint {
 public:
  base::Optional<std::string> IsViolated(const Type*) const;

  static TypeConstraint Unconstrained() { return {}; }
  static TypeConstraint SubtypeConstraint(const Type* upper_bound) {
    TypeConstraint result;
    result.upper_bound = {upper_bound};
    return result;
  }

 private:
  base::Optional<const Type*> upper_bound;
};

base::Optional<std::string> FindConstraintViolation(
    const std::vector<const Type*>& types,
    const std::vector<TypeConstraint>& constraints);

std::vector<TypeConstraint> ComputeConstraints(
    Scope* scope, const GenericParameters& parameters);

template <class SpecializationType, class DeclarationType>
class GenericDeclarable : public Declarable {
542
 private:
543 544
  using Map = std::unordered_map<TypeVector, SpecializationType,
                                 base::hash<TypeVector>>;
545

546
 public:
547 548
  void AddSpecialization(const TypeVector& type_arguments,
                         SpecializationType specialization) {
549
    DCHECK_EQ(0, specializations_.count(type_arguments));
550 551 552 553
    if (auto violation =
            FindConstraintViolation(type_arguments, Constraints())) {
      Error(*violation).Throw();
    }
554 555
    specializations_[type_arguments] = specialization;
  }
556 557
  base::Optional<SpecializationType> GetSpecialization(
      const TypeVector& type_arguments) const {
558 559 560 561
    auto it = specializations_.find(type_arguments);
    if (it != specializations_.end()) return it->second;
    return base::nullopt;
  }
562 563 564 565 566

  using iterator = typename Map::const_iterator;
  iterator begin() const { return specializations_.begin(); }
  iterator end() const { return specializations_.end(); }

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
  const std::string& name() const { return name_; }
  auto declaration() const { return generic_declaration_->declaration; }
  const GenericParameters& generic_parameters() const {
    return generic_declaration_->generic_parameters;
  }

  const std::vector<TypeConstraint>& Constraints() {
    if (!constraints_)
      constraints_ = {ComputeConstraints(ParentScope(), generic_parameters())};
    return *constraints_;
  }

 protected:
  GenericDeclarable(Declarable::Kind kind, const std::string& name,
                    DeclarationType generic_declaration)
      : Declarable(kind),
        name_(name),
        generic_declaration_(generic_declaration) {
    DCHECK(!generic_declaration->generic_parameters.empty());
  }

588
 private:
589 590
  std::string name_;
  DeclarationType generic_declaration_;
591
  Map specializations_;
592
  base::Optional<std::vector<TypeConstraint>> constraints_;
593 594
};

595 596
class GenericCallable
    : public GenericDeclarable<Callable*, GenericCallableDeclaration*> {
597
 public:
598
  DECLARE_DECLARABLE_BOILERPLATE(GenericCallable, generic_callable)
599

600 601
  base::Optional<Statement*> CallableBody();

602
  TypeArgumentInference InferSpecializationTypes(
603
      const TypeVector& explicit_specialization_types,
604
      const std::vector<base::Optional<const Type*>>& arguments);
605 606 607

 private:
  friend class Declarations;
608 609
  GenericCallable(const std::string& name,
                  GenericCallableDeclaration* generic_declaration)
610 611
      : GenericDeclarable<Callable*, GenericCallableDeclaration*>(
            Declarable::kGenericCallable, name, generic_declaration) {}
612 613
};

614 615
class GenericType
    : public GenericDeclarable<const Type*, GenericTypeDeclaration*> {
616
 public:
617
  DECLARE_DECLARABLE_BOILERPLATE(GenericType, generic_type)
618 619 620

 private:
  friend class Declarations;
621 622
  GenericType(const std::string& name,
              GenericTypeDeclaration* generic_declaration)
623 624
      : GenericDeclarable<const Type*, GenericTypeDeclaration*>(
            Declarable::kGenericType, name, generic_declaration) {}
625 626
};

627 628
class TypeAlias : public Declarable {
 public:
629
  DECLARE_DECLARABLE_BOILERPLATE(TypeAlias, type_alias)
630

631 632 633 634 635
  const Type* type() const {
    if (type_) return *type_;
    return Resolve();
  }
  const Type* Resolve() const;
636
  bool IsRedeclaration() const { return redeclaration_; }
637 638 639
  SourcePosition GetDeclarationPosition() const {
    return declaration_position_;
  }
640 641 642

 private:
  friend class Declarations;
643 644
  friend class TypeVisitor;

645 646 647
  explicit TypeAlias(
      const Type* type, bool redeclaration,
      SourcePosition declaration_position = SourcePosition::Invalid())
648 649
      : Declarable(Declarable::kTypeAlias),
        type_(type),
650 651
        redeclaration_(redeclaration),
        declaration_position_(declaration_position) {}
652 653 654 655 656 657 658
  explicit TypeAlias(
      TypeDeclaration* type, bool redeclaration,
      SourcePosition declaration_position = SourcePosition::Invalid())
      : Declarable(Declarable::kTypeAlias),
        delayed_(type),
        redeclaration_(redeclaration),
        declaration_position_(declaration_position) {}
659

660 661 662
  mutable bool being_resolved_ = false;
  mutable base::Optional<TypeDeclaration*> delayed_;
  mutable base::Optional<const Type*> type_;
663
  bool redeclaration_;
664
  const SourcePosition declaration_position_;
665 666
};

667 668 669
std::ostream& operator<<(std::ostream& os, const Callable& m);
std::ostream& operator<<(std::ostream& os, const Builtin& b);
std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b);
670
std::ostream& operator<<(std::ostream& os, const GenericCallable& g);
671 672 673 674 675 676 677 678

#undef DECLARE_DECLARABLE_BOILERPLATE

}  // namespace torque
}  // namespace internal
}  // namespace v8

#endif  // V8_TORQUE_DECLARABLE_H_