v8/test/unittests/torque/torque-unittest.cc
Nico Hartmann 517ed4ad00 Reland "[Torque] Generalize Torque literals to larger size"
Previously, literals in Torque were stored as double values, which
made it impossible to precisely represent 64 bit integer values.
This CL replaces the old literal expression with an integer and
floating point literal expression that are unbounded in size. We
allow implicit conversion of these literals to arbitary integer
and floating point types respectively and insert a corresponding
bounds check into generated CSA.

Changes in the reland: Simplified IntegerLiteral to single digit.

Bug: v8:7793, chromium:1289282
Change-Id: I31c762c2f31165c7a1d0b07842b764e5851ce189
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3406750
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78811}
2022-01-27 13:39:16 +00:00

981 lines
26 KiB
C++

// 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;
type IntegerLiteral constexpr 'IntegerLiteral';
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
@doNotGenerateCppClass
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;
extern macro ConstexprIntegerLiteralToInt32(constexpr IntegerLiteral): constexpr int32;
extern macro SmiFromInt32(int32): Smi;
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);
}
FromConstexpr<int32, constexpr int32>(i: constexpr int32): int32 {
return %FromConstexpr<int32>(i);
}
FromConstexpr<int32, constexpr IntegerLiteral>(i: constexpr IntegerLiteral): int32 {
return FromConstexpr<int32>(ConstexprIntegerLiteralToInt32(i));
}
FromConstexpr<Smi, constexpr IntegerLiteral>(i: constexpr IntegerLiteral): Smi {
return SmiFromInt32(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::WithUnknownOffset(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): void {
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(): void { 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(): void {
Bar(SomeEnum::kValue);
}
macro Bar<T: type>(value: T): void {}
)",
HasSubstr(
"Cannot find non-constexpr type corresponding to constexpr kValue"));
}
TEST(Torque, DoubleUnderScorePrefixIllegalForIdentifiers) {
ExpectFailingCompilation(R"(
@export macro Foo(): void {
let __x;
}
)",
HasSubstr("Lexer Error"));
}
#endif
TEST(Torque, UnusedLetBindingLintError) {
ExpectFailingCompilation(R"(
@export macro Foo(y: Smi): void {
let x: Smi = y;
}
)",
HasSubstr("Variable 'x' is never used."));
}
TEST(Torque, UnderscorePrefixSilencesUnusedWarning) {
ExpectSuccessfulCompilation(R"(
@export macro Foo(y: Smi): void {
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): void {
let _x: Smi = y;
check(_x == y);
}
)",
HasSubstr("Trying to reference '_x'"));
}
#endif
TEST(Torque, UnusedArgumentLintError) {
ExpectFailingCompilation(R"(
@export macro Foo(x: Smi): void {}
)",
HasSubstr("Variable 'x' is never used."));
}
TEST(Torque, UsingUnderscorePrefixedArgumentSilencesWarning) {
ExpectSuccessfulCompilation(R"(
@export macro Foo(_y: Smi): void {}
)");
}
TEST(Torque, UnusedLabelLintError) {
ExpectFailingCompilation(R"(
@export macro Foo(): void labels Bar {}
)",
HasSubstr("Label 'Bar' is never used."));
}
TEST(Torque, UsingUnderScorePrefixLabelSilencesWarning) {
ExpectSuccessfulCompilation(R"(
@export macro Foo(): void labels _Bar {}
)");
}
TEST(Torque, NoUnusedWarningForImplicitArguments) {
ExpectSuccessfulCompilation(R"(
@export macro Foo(implicit c: Context, r: JSReceiver)(): void {}
)");
}
TEST(Torque, NoUnusedWarningForVariablesOnlyUsedInDchecks) {
ExpectSuccessfulCompilation(R"(
@export macro Foo(x: bool): void {
dcheck(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): void;
macro F2<T: type>(x: Foo<T>): void {
F1(x);
}
@export
macro F3(a: Foo<Smi>, b: Foo<HeapObject>): void {
F2(a);
F2(b);
}
)");
ExpectFailingCompilation(R"(
type Foo<T: type> extends HeapObject;
macro F1<T: type>(x: Foo<T>): void {}
@export
macro F2(a: Foo<Smi>): void {
F1<HeapObject>(a);
})",
HasSubstr("cannot find suitable callable"));
ExpectFailingCompilation(R"(
type Foo<T: type> extends HeapObject;
extern macro F1(Foo<HeapObject>): void;
@export
macro F2(a: Foo<Smi>): void {
F1(a);
})",
HasSubstr("cannot find suitable callable"));
}
TEST(Torque, SpecializationRequesters) {
ExpectFailingCompilation(
R"(
macro A<T: type extends HeapObject>(): void {}
macro B<T: type>(): void {
A<T>();
}
macro C<T: type>(): void {
B<T>();
}
macro D(): void {
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>(): void {}
macro B<T: type>(): void {
A<T>();
}
struct C<T: type> {
macro Method(): void {
B<T>();
}
}
macro D(_b: C<Smi>): void {}
)",
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(): void {
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): void {
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): void {
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): void {
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): void {
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): void {
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): void {
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): void {
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): void {
const constRefX: const &int32 = &o.x;
*constRefX = n;
}
)",
HasSubstr("cannot assign to const value"));
}
TEST(Torque, CatchFirstHandler) {
ExpectFailingCompilation(
R"(
@export
macro Test(): void {
try {
} label Foo {
} catch (_e, _m) {}
}
)",
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): void { Test1(b); }
)");
ExpectFailingCompilation(
R"(
macro Test1(implicit c: Smi)(_a: Object): Smi { return c; }
@export
macro Test2(b: Smi): void { 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): void { 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): void { 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): void { 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): void {}
macro Foo(_x: Smi): void {}
macro GenericMacro<T: type>(implicit x: T)(): void {
Foo(x);
}
@export
macro Test1(implicit x: Smi)(): void { GenericMacro(); }
@export
macro Test2(implicit x: Map)(): void { GenericMacro(); }
)");
ExpectFailingCompilation(
R"(
// Wrap in namespace to avoid redeclaration error.
namespace foo {
macro Foo(implicit x: Map)(): void {}
}
macro Foo(implicit x: Smi)(): void {}
namespace foo{
@export
macro Test(implicit x: Smi)(): void { Foo(); }
}
)",
HasSubstr("ambiguous callable"));
ExpectFailingCompilation(
R"(
// Wrap in namespace to avoid redeclaration error.
namespace foo {
macro Foo(implicit x: Map)(): void {}
}
macro Foo(implicit x: Smi)(): void {}
namespace foo{
@export
macro Test(implicit x: Map)(): void { Foo(); }
}
)",
HasSubstr("ambiguous callable"));
}
} // namespace torque
} // namespace internal
} // namespace v8