text: // Copyright 2018 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.
  
  #include "src/torque/torque-compiler.h"
  #include "src/torque/utils.h"
  #include "test/unittests/test-utils.h"
  #include "testing/gmock-support.h"
  
  namespace v8 {
  namespace internal {
  namespace torque {
  
  namespace {
  
  // This is a simplified version of the basic Torque type definitions.
  // Some class types are replaced by abstact types to keep it self-contained and
  // small.
  constexpr const char* kTestTorquePrelude = R"(
  type void;
  type never;
  
  namespace torque_internal {
    struct Reference<T: type> {
      const object: HeapObject;
      const offset: intptr;
    }
    type ConstReference<T : type> extends Reference<T>;
    type MutableReference<T : type> extends ConstReference<T>;
  
    type UninitializedHeapObject extends HeapObject;
    macro DownCastForTorqueClass<T : type extends HeapObject>(o: HeapObject):
        T labels _CastError {
      return %RawDownCast<T>(o);
    }
    macro IsWithContext<T : type extends HeapObject>(o: HeapObject): bool {
      return false;
    }
  }
  
  type Tagged generates 'TNode<MaybeObject>' constexpr 'MaybeObject';
  type StrongTagged extends Tagged
      generates 'TNode<Object>' constexpr 'ObjectPtr';
  type Smi extends StrongTagged generates 'TNode<Smi>' constexpr 'Smi';
  type WeakHeapObject extends Tagged;
  type Weak<T : type extends HeapObject> extends WeakHeapObject;
  type Uninitialized extends Tagged;
  type TaggedIndex extends StrongTagged;
  type TaggedZeroPattern extends TaggedIndex;
  
  @abstract
  extern class HeapObject extends StrongTagged {
    map: Map;
  }
  type Map extends HeapObject generates 'TNode<Map>';
  type Object = Smi | HeapObject;
  type Number = Smi|HeapNumber;
  type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
  type JSObject extends JSReceiver generates 'TNode<JSObject>';
  type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
  type uint32 generates 'TNode<Uint32T>' constexpr 'uint32_t';
  type int31 extends int32
      generates 'TNode<Int32T>' constexpr 'int31_t';
  type uint31 extends uint32
      generates 'TNode<Uint32T>' constexpr 'uint31_t';
  type int16 extends int31
      generates 'TNode<Int16T>' constexpr 'int16_t';
  type uint16 extends uint31
      generates 'TNode<Uint16T>' constexpr 'uint16_t';
  type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
  type uint8 extends uint16
      generates 'TNode<Uint8T>' constexpr 'uint8_t';
  type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
  type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
  type uintptr generates 'TNode<UintPtrT>' constexpr 'uintptr_t';
  type float32 generates 'TNode<Float32T>' constexpr 'float';
  type float64 generates 'TNode<Float64T>' constexpr 'double';
  type bool generates 'TNode<BoolT>' constexpr 'bool';
  type bint generates 'TNode<BInt>' constexpr 'BInt';
  type string constexpr 'const char*';
  type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
  type ExternalPointer
      generates 'TNode<ExternalPointerT>' constexpr 'ExternalPointer_t';
  type Code extends HeapObject generates 'TNode<Code>';
  type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';
  type Context extends HeapObject generates 'TNode<Context>';
  type NativeContext extends Context;
  type SmiTagged<T : type extends uint31> extends Smi;
  type String extends HeapObject;
  type HeapNumber extends HeapObject;
  type FixedArrayBase extends HeapObject;
  type Lazy<T: type>;
  
  struct float64_or_hole {
    is_hole: bool;
    value: float64;
  }
  
  extern operator '+' macro IntPtrAdd(intptr, intptr): intptr;
  extern operator '!' macro Word32BinaryNot(bool): bool;
  extern operator '==' macro Word32Equal(int32, int32): bool;
  
  intrinsic %FromConstexpr<To: type, From: type>(b: From): To;
  intrinsic %RawDownCast<To: type, From: type>(x: From): To;
  intrinsic %RawConstexprCast<To: type, From: type>(f: From): To;
  extern macro SmiConstant(constexpr Smi): Smi;
  extern macro TaggedToSmi(Object): Smi
      labels CastError;
  extern macro TaggedToHeapObject(Object): HeapObject
      labels CastError;
  extern macro Float64SilenceNaN(float64): float64;
  
  extern macro IntPtrConstant(constexpr int31): intptr;
  
  macro FromConstexpr<To: type, From: type>(o: From): To;
  FromConstexpr<Smi, constexpr Smi>(s: constexpr Smi): Smi {
    return SmiConstant(s);
  }
  FromConstexpr<Smi, constexpr int31>(s: constexpr int31): Smi {
    return %FromConstexpr<Smi>(s);
  }
  FromConstexpr<intptr, constexpr int31>(i: constexpr int31): intptr {
    return IntPtrConstant(i);
  }
  FromConstexpr<intptr, constexpr intptr>(i: constexpr intptr): intptr {
    return %FromConstexpr<intptr>(i);
  }
  extern macro BoolConstant(constexpr bool): bool;
  FromConstexpr<bool, constexpr bool>(b: constexpr bool): bool {
    return BoolConstant(b);
  }
  FromConstexpr<int32, constexpr int31>(i: constexpr int31): int32 {
    return %FromConstexpr<int32>(i);
  }
  
  macro Cast<A : type extends Object>(implicit context: Context)(o: Object): A
      labels CastError {
    return Cast<A>(TaggedToHeapObject(o) otherwise CastError)
        otherwise CastError;
  }
  macro Cast<A : type extends HeapObject>(o: HeapObject): A
      labels CastError;
  Cast<Smi>(o: Object): Smi
      labels CastError {
    return TaggedToSmi(o) otherwise CastError;
  }
  )";
  
  TorqueCompilerResult TestCompileTorque(std::string source) {
    TorqueCompilerOptions options;
    options.output_directory = "";
    options.collect_language_server_data = false;
    options.force_assert_statements = false;
    options.v8_root = ".";
  
    source = kTestTorquePrelude + source;
    return CompileTorque(source, options);
  }
  
  void ExpectSuccessfulCompilation(std::string source) {
    TorqueCompilerResult result = TestCompileTorque(std::move(source));
    std::vector<std::string> messages;
    for (const auto& message : result.messages) {
      messages.push_back(message.message);
    }
    EXPECT_EQ(messages, std::vector<std::string>{});
  }
  
  template <class T>
  using MatcherVector =
      std::vector<std::pair<::testing::PolymorphicMatcher<T>, LineAndColumn>>;
  
  template <class T>
  void ExpectFailingCompilation(std::string source,
                                MatcherVector<T> message_patterns) {
    TorqueCompilerResult result = TestCompileTorque(std::move(source));
    ASSERT_FALSE(result.messages.empty());
    EXPECT_GE(result.messages.size(), message_patterns.size());
    size_t limit = message_patterns.size();
    if (result.messages.size() < limit) {
      limit = result.messages.size();
    }
    for (size_t i = 0; i < limit; ++i) {
      EXPECT_THAT(result.messages[i].message, message_patterns[i].first);
      if (message_patterns[i].second != LineAndColumn::Invalid()) {
        base::Optional<SourcePosition> actual = result.messages[i].position;
        EXPECT_TRUE(actual.has_value());
        EXPECT_EQ(actual->start, message_patterns[i].second);
      }
    }
  }
  
  template <class T>
  void ExpectFailingCompilation(
      std::string source, ::testing::PolymorphicMatcher<T> message_pattern) {
    ExpectFailingCompilation(
        source, MatcherVector<T>{{message_pattern, LineAndColumn::Invalid()}});
  }
  
  // TODO(almuthanna): the definition of this function is skipped on Fuchsia
  // because it causes an 'unused function' exception upon buidling gn
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  int CountPreludeLines() {
    static int result = -1;
    if (result == -1) {
      std::string prelude(kTestTorquePrelude);
      result = static_cast<int>(std::count(prelude.begin(), prelude.end(), '\n'));
    }
    return result;
  }
  #endif
  
  using SubstrWithPosition =
      std::pair<::testing::PolymorphicMatcher<
                    ::testing::internal::HasSubstrMatcher<std::string>>,
                LineAndColumn>;
  
  // TODO(almuthanna): the definition of this function is skipped on Fuchsia
  // because it causes an 'unused function' exception upon buidling gn
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  SubstrWithPosition SubstrTester(const std::string& message, int line, int col) {
    // Change line and column from 1-based to 0-based.
    return {::testing::HasSubstr(message),
            LineAndColumn{line + CountPreludeLines() - 1, col - 1}};
  }
  #endif
  
  using SubstrVector = std::vector<SubstrWithPosition>;
  
  }  // namespace
  
  TEST(Torque, Prelude) { ExpectSuccessfulCompilation(""); }
  
  TEST(Torque, StackDeleteRange) {
    Stack<int> stack = {1, 2, 3, 4, 5, 6, 7};
    stack.DeleteRange(StackRange{BottomOffset{2}, BottomOffset{4}});
    Stack<int> result = {1, 2, 5, 6, 7};
    ASSERT_TRUE(stack == result);
  }
  
  using ::testing::HasSubstr;
  TEST(Torque, TypeNamingConventionLintError) {
    ExpectFailingCompilation(R"(
      type foo generates 'TNode<Foo>';
    )",
                             HasSubstr("\"foo\""));
  }
  
  TEST(Torque, StructNamingConventionLintError) {
    ExpectFailingCompilation(R"(
      struct foo {}
    )",
                             HasSubstr("\"foo\""));
  }
  
  TEST(Torque, ClassDefinition) {
    ExpectSuccessfulCompilation(R"(
      extern class TestClassWithAllTypes extends HeapObject {
        a: int8;
        b: uint8;
        b2: uint8;
        b3: uint8;
        c: int16;
        d: uint16;
        e: int32;
        f: uint32;
        g: RawPtr;
        h: intptr;
        i: uintptr;
      }
  
      @export
      macro TestClassWithAllTypesLoadsAndStores(
          t: TestClassWithAllTypes, r: RawPtr, v1: int8, v2: uint8, v3: int16,
          v4: uint16, v5: int32, v6: uint32, v7: intptr, v8: uintptr) {
        t.a = v1;
        t.b = v2;
        t.c = v3;
        t.d = v4;
        t.e = v5;
        t.f = v6;
        t.g = r;
        t.h = v7;
        t.i = v8;
        t.a = t.a;
        t.b = t.b;
        t.c = t.c;
        t.d = t.d;
        t.e = t.e;
        t.f = t.f;
        t.g = t.g;
        t.h = t.h;
        t.i = t.i;
      }
    )");
  }
  
  TEST(Torque, TypeDeclarationOrder) {
    ExpectSuccessfulCompilation(R"(
      type Baztype = Foo | FooType;
  
      @abstract
      extern class Foo extends HeapObject {
        fooField: FooType;
      }
  
      extern class Bar extends Foo {
        barField: Bartype;
        bazfield: Baztype;
      }
  
      type Bartype = FooType;
  
      type FooType = Smi | Bar;
    )");
  }
  
  // TODO(almuthanna): These tests were skipped because they cause a crash when
  // they are ran on Fuchsia. This issue should be solved later on
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  TEST(Torque, ConditionalFields) {
    // This class should throw alignment errors if @if decorators aren't
    // working.
    ExpectSuccessfulCompilation(R"(
    extern class PreprocessingTest extends HeapObject {
      @if(FALSE_FOR_TESTING) a: int8;
      @if(TRUE_FOR_TESTING) a: int16;
      b: int16;
      d: int32;
      @ifnot(TRUE_FOR_TESTING) e: int8;
      @ifnot(FALSE_FOR_TESTING) f: int16;
      g: int16;
      h: int32;
    }
    )");
    ExpectFailingCompilation(R"(
    extern class PreprocessingTest extends HeapObject {
      @if(TRUE_FOR_TESTING) a: int8;
      @if(FALSE_FOR_TESTING) a: int16;
      b: int16;
      d: int32;
      @ifnot(FALSE_FOR_TESTING) e: int8;
      @ifnot(TRUE_FOR_TESTING) f: int16;
      g: int16;
      h: int32;
    }
    )",
                             HasSubstr("aligned"));
  }
  
  TEST(Torque, ConstexprLetBindingDoesNotCrash) {
    ExpectFailingCompilation(
        R"(@export macro FooBar() { let foo = 0; check(foo >= 0); })",
        HasSubstr("Use 'const' instead of 'let' for variable 'foo'"));
  }
  
  TEST(Torque, FailedImplicitCastFromConstexprDoesNotCrash) {
    ExpectFailingCompilation(
        R"(
      extern enum SomeEnum {
        kValue,
        ...
      }
      macro Foo() {
        Bar(SomeEnum::kValue);
      }
      macro Bar<T: type>(value: T) {}
    )",
        HasSubstr(
            "Cannot find non-constexpr type corresponding to constexpr kValue"));
  }
  
  TEST(Torque, DoubleUnderScorePrefixIllegalForIdentifiers) {
    ExpectFailingCompilation(R"(
      @export macro Foo() {
        let __x;
      }
    )",
                             HasSubstr("Lexer Error"));
  }
  #endif
  
  TEST(Torque, UnusedLetBindingLintError) {
    ExpectFailingCompilation(R"(
      @export macro Foo(y: Smi) {
        let x: Smi = y;
      }
    )",
                             HasSubstr("Variable 'x' is never used."));
  }
  
  TEST(Torque, UnderscorePrefixSilencesUnusedWarning) {
    ExpectSuccessfulCompilation(R"(
      @export macro Foo(y: Smi) {
        let _x: Smi = y;
      }
    )");
  }
  
  // TODO(almuthanna): This test was skipped because it causes a crash when it is
  // ran on Fuchsia. This issue should be solved later on
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  TEST(Torque, UsingUnderscorePrefixedIdentifierError) {
    ExpectFailingCompilation(R"(
      @export macro Foo(y: Smi) {
        let _x: Smi = y;
        check(_x == y);
      }
    )",
                             HasSubstr("Trying to reference '_x'"));
  }
  #endif
  
  TEST(Torque, UnusedArgumentLintError) {
    ExpectFailingCompilation(R"(
      @export macro Foo(x: Smi) {}
    )",
                             HasSubstr("Variable 'x' is never used."));
  }
  
  TEST(Torque, UsingUnderscorePrefixedArgumentSilencesWarning) {
    ExpectSuccessfulCompilation(R"(
      @export macro Foo(_y: Smi) {}
    )");
  }
  
  TEST(Torque, UnusedLabelLintError) {
    ExpectFailingCompilation(R"(
      @export macro Foo() labels Bar {}
    )",
                             HasSubstr("Label 'Bar' is never used."));
  }
  
  TEST(Torque, UsingUnderScorePrefixLabelSilencesWarning) {
    ExpectSuccessfulCompilation(R"(
      @export macro Foo() labels _Bar {}
    )");
  }
  
  TEST(Torque, NoUnusedWarningForImplicitArguments) {
    ExpectSuccessfulCompilation(R"(
      @export macro Foo(implicit c: Context, r: JSReceiver)() {}
    )");
  }
  
  TEST(Torque, NoUnusedWarningForVariablesOnlyUsedInAsserts) {
    ExpectSuccessfulCompilation(R"(
      @export macro Foo(x: bool) {
        assert(x);
      }
    )");
  }
  
  // TODO(almuthanna): This test was skipped because it causes a crash when it is
  // ran on Fuchsia. This issue should be solved later on
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  TEST(Torque, ImportNonExistentFile) {
    ExpectFailingCompilation(R"(import "foo/bar.tq")",
                             HasSubstr("File 'foo/bar.tq' not found."));
  }
  #endif
  
  TEST(Torque, LetShouldBeConstLintError) {
    ExpectFailingCompilation(R"(
      @export macro Foo(y: Smi): Smi {
        let x: Smi = y;
        return x;
      })",
                             HasSubstr("Variable 'x' is never assigned to."));
  }
  
  TEST(Torque, LetShouldBeConstIsSkippedForStructs) {
    ExpectSuccessfulCompilation(R"(
      struct Foo{ a: Smi; }
      @export macro Bar(x: Smi): Foo {
        let foo = Foo{a: x};
        return foo;
      }
    )");
  }
  
  // TODO(almuthanna): These tests were skipped because they cause a crash when
  // they are ran on Fuchsia. This issue should be solved later on
  // Ticket: https://crbug.com/1028617
  #if !defined(V8_TARGET_OS_FUCHSIA)
  TEST(Torque, GenericAbstractType) {
    ExpectSuccessfulCompilation(R"(
      type Foo<T: type> extends HeapObject;
      extern macro F1(HeapObject);
      macro F2<T: type>(x: Foo<T>) {
        F1(x);
      }
      @export
      macro F3(a: Foo<Smi>, b: Foo<HeapObject>){
        F2(a);
        F2(b);
      }
    )");
  
    ExpectFailingCompilation(R"(
      type Foo<T: type> extends HeapObject;
      macro F1<T: type>(x: Foo<T>) {}
      @export
      macro F2(a: Foo<Smi>) {
        F1<HeapObject>(a);
      })",
                             HasSubstr("cannot find suitable callable"));
  
    ExpectFailingCompilation(R"(
      type Foo<T: type> extends HeapObject;
      extern macro F1(Foo<HeapObject>);
      @export
      macro F2(a: Foo<Smi>) {
        F1(a);
      })",
                             HasSubstr("cannot find suitable callable"));
  }
  
  TEST(Torque, SpecializationRequesters) {
    ExpectFailingCompilation(
        R"(
      macro A<T: type extends HeapObject>() {}
      macro B<T: type>() {
        A<T>();
      }
      macro C<T: type>() {
        B<T>();
      }
      macro D() {
        C<Smi>();
      }
    )",
        SubstrVector{
            SubstrTester("cannot find suitable callable", 4, 7),
            SubstrTester("Note: in specialization B<Smi> requested here", 7, 7),
            SubstrTester("Note: in specialization C<Smi> requested here", 10,
                         7)});
  
    ExpectFailingCompilation(
        R"(
      extern macro RetVal(): Object;
      builtin A<T: type extends HeapObject>(implicit context: Context)(): Object {
        return RetVal();
      }
      builtin B<T: type>(implicit context: Context)(): Object {
        return A<T>();
      }
      builtin C<T: type>(implicit context: Context)(): Object {
        return B<T>();
      }
      builtin D(implicit context: Context)(): Object {
        return C<Smi>();
      }
    )",
        SubstrVector{
            SubstrTester("cannot find suitable callable", 7, 14),
            SubstrTester("Note: in specialization B<Smi> requested here", 10, 14),
            SubstrTester("Note: in specialization C<Smi> requested here", 13,
                         14)});
  
    ExpectFailingCompilation(
        R"(
      struct A<T: type extends HeapObject> {}
      struct B<T: type> {
        a: A<T>;
      }
      struct C<T: type> {
        b: B<T>;
      }
      struct D {
        c: C<Smi>;
      }
    )",
        SubstrVector{
            SubstrTester("Could not instantiate generic", 4, 10),
            SubstrTester("Note: in specialization B<Smi> requested here", 7, 10),
            SubstrTester("Note: in specialization C<Smi> requested here", 10,
                         10)});
  
    ExpectFailingCompilation(
        R"(
      macro A<T: type extends HeapObject>() {}
      macro B<T: type>() {
        A<T>();
      }
      struct C<T: type> {
        macro Method() {
          B<T>();
        }
      }
      macro D(_b: C<Smi>) {}
    )",
        SubstrVector{
            SubstrTester("cannot find suitable callable", 4, 7),
            SubstrTester("Note: in specialization B<Smi> requested here", 8, 9),
            SubstrTester("Note: in specialization C<Smi> requested here", 11,
                         5)});
  }
  #endif
  
  TEST(Torque, Enums) {
    ExpectSuccessfulCompilation(R"(
      extern enum MyEnum {
        kValue0,
        kValue1,
        kValue2,
        kValue3
      }
    )");
  
    ExpectFailingCompilation(R"(
      extern enum MyEmptyEnum {
      }
    )",
                             HasSubstr("unexpected token \"}\""));
  }
  
  TEST(Torque, EnumInTypeswitch) {
    ExpectSuccessfulCompilation(R"(
      extern enum MyEnum extends Smi {
        kA,
        kB,
        kC
      }
  
      @export
      macro Test(implicit context: Context)(v : MyEnum): Smi {
        typeswitch(v) {
          case (MyEnum::kA | MyEnum::kB): {
            return 1;
          }
          case (MyEnum::kC): {
            return 2;
          }
        }
      }
    )");
  
    ExpectSuccessfulCompilation(R"(
      extern enum MyEnum extends Smi {
        kA,
        kB,
        kC,
        ...
      }
  
      @export
      macro Test(implicit context: Context)(v : MyEnum): Smi {
        typeswitch(v) {
           case (MyEnum::kC): {
            return 2;
          }
          case (MyEnum::kA | MyEnum::kB): {
            return 1;
          }
         case (MyEnum): {
            return 0;
          }
        }
      }
    )");
  
    ExpectSuccessfulCompilation(R"(
    extern enum MyEnum extends Smi {
      kA,
      kB,
      kC,
      ...
    }
  
    @export
    macro Test(implicit context: Context)(b: bool): Smi {
      return b ? MyEnum::kB : MyEnum::kA;
    }
  )");
  }
  
  TEST(Torque, EnumTypeAnnotations) {
    ExpectSuccessfulCompilation(R"(
      type Type1 extends intptr;
      type Type2 extends intptr;
      extern enum MyEnum extends intptr {
        kValue1: Type1,
        kValue2: Type2,
        kValue3
      }
      @export macro Foo() {
        const _a: Type1 = MyEnum::kValue1;
        const _b: Type2 = MyEnum::kValue2;
        const _c: intptr = MyEnum::kValue3;
      }
    )");
  }
  
  TEST(Torque, ConstClassFields) {
    ExpectSuccessfulCompilation(R"(
      class Foo extends HeapObject {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        const _x: int32 = o.x;
        o.y = n;
      }
    )");
  
    ExpectFailingCompilation(R"(
      class Foo extends HeapObject {
        const x: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        o.x = n;
      }
    )",
                             HasSubstr("cannot assign to const value"));
  
    ExpectSuccessfulCompilation(R"(
      class Foo extends HeapObject {
        s: Bar;
      }
      struct Bar {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        const _x: int32 = o.s.x;
        // Assigning a struct as a value is OK, even when the struct contains
        // const fields.
        o.s = Bar{x: n, y: n};
        o.s.y = n;
      }
    )");
  
    ExpectFailingCompilation(R"(
      class Foo extends HeapObject {
        const s: Bar;
      }
      struct Bar {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        o.s.y = n;
      }
    )",
                             HasSubstr("cannot assign to const value"));
  
    ExpectFailingCompilation(R"(
      class Foo extends HeapObject {
        s: Bar;
      }
      struct Bar {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        o.s.x = n;
      }
    )",
                             HasSubstr("cannot assign to const value"));
  }
  
  TEST(Torque, References) {
    ExpectSuccessfulCompilation(R"(
      class Foo extends HeapObject {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        const constRefX: const &int32 = &o.x;
        const refY: &int32 = &o.y;
        const constRefY: const &int32 = refY;
        const _x: int32 = *constRefX;
        const _y1: int32 = *refY;
        const _y2: int32 = *constRefY;
        *refY = n;
        let r: const &int32 = constRefX;
        r = constRefY;
      }
    )");
  
    ExpectFailingCompilation(R"(
      class Foo extends HeapObject {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo) {
        const _refX: &int32 = &o.x;
      }
    )",
                             HasSubstr("cannot use expression of type const "
                                       "&int32 as a value of type &int32"));
  
    ExpectFailingCompilation(R"(
      class Foo extends HeapObject {
        const x: int32;
        y: int32;
      }
  
      @export
      macro Test(implicit context: Context)(o: Foo, n: int32) {
        const constRefX: const &int32 = &o.x;
        *constRefX = n;
      }
    )",
                             HasSubstr("cannot assign to const value"));
  }
  
  TEST(Torque, CatchFirstHandler) {
    ExpectFailingCompilation(
        R"(
      @export
      macro Test() {
        try {
        } label Foo {
        } catch (e) {}
      }
    )",
        HasSubstr(
            "catch handler always has to be first, before any label handler"));
  }
  
  TEST(Torque, BitFieldLogicalAnd) {
    std::string prelude = R"(
      bitfield struct S extends uint32 {
        a: bool: 1 bit;
        b: bool: 1 bit;
        c: int32: 5 bit;
      }
      macro Test(s: S): bool { return
    )";
    std::string postlude = ";}";
    std::string message = "use & rather than &&";
    ExpectFailingCompilation(prelude + "s.a && s.b" + postlude,
                             HasSubstr(message));
    ExpectFailingCompilation(prelude + "s.a && !s.b" + postlude,
                             HasSubstr(message));
    ExpectFailingCompilation(prelude + "!s.b && s.c == 34" + postlude,
                             HasSubstr(message));
  }
  
  TEST(Torque, FieldAccessOnNonClassType) {
    ExpectFailingCompilation(
        R"(
      @export
      macro Test(x: Number): Map {
        return x.map;
      }
    )",
        HasSubstr("map"));
  }
  
  TEST(Torque, UnusedImplicit) {
    ExpectSuccessfulCompilation(R"(
      @export
      macro Test1(implicit c: Smi)(a: Object): Object { return a; }
      @export
      macro Test2(b: Object) { Test1(b);  }
    )");
  
    ExpectFailingCompilation(
        R"(
      macro Test1(implicit c: Smi)(_a: Object): Smi { return c; }
      @export
      macro Test2(b: Smi) { Test1(b);  }
    )",
        HasSubstr("undefined expression of type Smi: the implicit "
                  "parameter 'c' is not defined when invoking Test1 at"));
  
    ExpectFailingCompilation(
        R"(
      extern macro Test3(implicit c: Smi)(Object): Smi;
      @export
      macro Test4(b: Smi) { Test3(b);  }
    )",
        HasSubstr("unititialized implicit parameters can only be passed to "
                  "Torque-defined macros: the implicit parameter 'c' is not "
                  "defined when invoking Test3"));
    ExpectSuccessfulCompilation(
        R"(
      macro Test7<T: type>(implicit c: Smi)(o: T): Smi;
      Test7<Smi>(implicit c: Smi)(o: Smi): Smi { return o; }
      @export
      macro Test8(b: Smi) { Test7(b); }
    )");
  
    ExpectFailingCompilation(
        R"(
      macro Test6<T: type>(_o: T): T;
      macro Test6<T: type>(implicit c: T)(_o: T): T {
        return c;
      }
      macro Test7<T: type>(o: T): Smi;
      Test7<Smi>(o: Smi): Smi { return Test6<Smi>(o); }
      @export
      macro Test8(b: Smi) { Test7(b); }
    )",
        HasSubstr("\nambiguous callable : \n  Test6(Smi)\ncandidates are:\n  "
                  "Test6(Smi): Smi\n  Test6(implicit Smi)(Smi): Smi"));
  }
  
  TEST(Torque, ImplicitTemplateParameterInference) {
    ExpectSuccessfulCompilation(R"(
      macro Foo(_x: Map) {}
      macro Foo(_x: Smi) {}
      macro GenericMacro<T: type>(implicit x: T)() {
        Foo(x);
      }
      @export
      macro Test1(implicit x: Smi)() { GenericMacro(); }
      @export
      macro Test2(implicit x: Map)() { GenericMacro();  }
    )");
  
    ExpectFailingCompilation(
        R"(
      // Wrap in namespace to avoid redeclaration error.
      namespace foo {
      macro Foo(implicit x: Map)() {}
      }
      macro Foo(implicit x: Smi)() {}
      namespace foo{
      @export
      macro Test(implicit x: Smi)() { Foo(); }
      }
    )",
        HasSubstr("ambiguous callable"));
  
    ExpectFailingCompilation(
        R"(
      // Wrap in namespace to avoid redeclaration error.
      namespace foo {
      macro Foo(implicit x: Map)() {}
      }
      macro Foo(implicit x: Smi)() {}
      namespace foo{
      @export
      macro Test(implicit x: Map)() { Foo(); }
      }
    )",
        HasSubstr("ambiguous callable"));
  }
  
  }  // namespace torque
  }  // namespace internal
  }  // namespace v8
  