Commit da654310 authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[torque] Add lint error for 'let' bindings that can be 'const'

This CL adds a lint error for variables that are unnecessarily bound
with 'let' when they could be bound using 'const. This test is skipped
for struct types. For struct types, the "constness" also depends on
the struct methods called and whether these methods write to the struct
or not. This is not straight-forward to detect.

Drive-by: Fix all the newly introduced lint errors.

Bug: v8:7793
Change-Id: I0522ffcc4321350eef2e9573b8430bc78200ddce
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1645322
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62085}
parent 7304d116
......@@ -34,13 +34,13 @@ namespace arguments {
@export
macro GetArgumentsFrameAndCount(implicit context: Context)(f: JSFunction):
ArgumentsInfo {
let frame: Frame = LoadParentFramePointer();
const frame: Frame = LoadParentFramePointer();
assert(frame.function == f);
const shared: SharedFunctionInfo = f.shared_function_info;
const formalParameterCount: bint =
Convert<bint>(Convert<int32>(shared.formal_parameter_count));
let argumentCount: bint = formalParameterCount;
const argumentCount: bint = formalParameterCount;
const adaptor: ArgumentsAdaptorFrame =
Cast<ArgumentsAdaptorFrame>(frame.caller)
......
......@@ -88,7 +88,7 @@ namespace array {
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
let fastO: FastJSArray = Cast<FastJSArray>(o) otherwise goto Bailout(k);
const fastO: FastJSArray = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
......
......@@ -45,7 +45,7 @@ namespace array_find {
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
......
......@@ -46,7 +46,7 @@ namespace array_findindex {
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// This custom lazy deopt point is right after the callback. find() needs
......
......@@ -72,7 +72,7 @@ namespace array_foreach {
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
let fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
......
......@@ -535,7 +535,7 @@ namespace array_join {
sepObj: Object, locales: Object, options: Object): Object {
// 3. If separator is undefined, let sep be the single-element String ",".
// 4. Else, let sep be ? ToString(separator).
let sep: String =
const sep: String =
sepObj == Undefined ? ',' : ToString_Inline(context, sepObj);
// If the receiver is not empty and not already being joined, continue with
......
......@@ -94,7 +94,7 @@ namespace array_map {
}
CreateJSArray(implicit context: Context)(validLength: Smi): JSArray {
let length: Smi = this.fixedArray.length;
const length: Smi = this.fixedArray.length;
assert(validLength <= length);
let kind: ElementsKind = PACKED_SMI_ELEMENTS;
if (!this.onlySmis) {
......@@ -114,7 +114,7 @@ namespace array_map {
kind = FastHoleyElementsKind(kind);
}
let map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
let a: JSArray;
if (IsDoubleElementsKind(kind)) {
......
......@@ -35,7 +35,7 @@ namespace array_of {
// 7. Repeat, while k < len
while (k < len) {
// a. Let kValue be items[k].
let kValue: Object = items[Convert<intptr>(k)];
const kValue: Object = items[Convert<intptr>(k)];
// b. Let Pk be ! ToString(k).
// c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue).
......
......@@ -53,11 +53,11 @@ namespace array {
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// The accumulator is the result from the callback call which just occured.
let r = ArrayReduceRightLoopContinuation(
const r = ArrayReduceRightLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
......@@ -111,7 +111,7 @@ namespace array {
labels Bailout(Number, Object) {
let accumulator = initialAccumulator;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(len - 1, accumulator);
let fastO =
const fastO =
Cast<FastJSArray>(o) otherwise goto Bailout(len - 1, accumulator);
let fastOW = NewFastJSArrayWitness(fastO);
......
......@@ -53,11 +53,11 @@ namespace array {
// JSReceiver.
const jsreceiver = Cast<JSReceiver>(receiver) otherwise unreachable;
const callbackfn = Cast<Callable>(callback) otherwise unreachable;
let numberK = Cast<Number>(initialK) otherwise unreachable;
const numberK = Cast<Number>(initialK) otherwise unreachable;
const numberLength = Cast<Number>(length) otherwise unreachable;
// The accumulator is the result from the callback call which just occured.
let r = ArrayReduceLoopContinuation(
const r = ArrayReduceLoopContinuation(
jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength);
return r;
}
......@@ -111,7 +111,7 @@ namespace array {
const k = 0;
let accumulator = initialAccumulator;
Cast<Smi>(len) otherwise goto Bailout(k, accumulator);
let fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k, accumulator);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k, accumulator);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the array.
......
......@@ -185,7 +185,7 @@ namespace array_slice {
// 10. Repeat, while k < final
while (k < final) {
// a. Let Pk be ! ToString(k).
let pK: Number = k;
const pK: Number = k;
// b. Let kPresent be ? HasProperty(O, Pk).
const fromPresent: Boolean = HasProperty(o, pK);
......
......@@ -88,7 +88,7 @@ namespace array {
labels Bailout(Smi) {
let k: Smi = 0;
const smiLen = Cast<Smi>(len) otherwise goto Bailout(k);
let fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// Build a fast loop over the smi array.
......
......@@ -76,7 +76,7 @@ namespace array_splice {
UnsafeCast<FixedArrayType>(elements), dstIndex, srcIndex, count);
} else {
// Grow.
let capacity: Smi = CalculateNewElementsCapacity(newLength);
const capacity: Smi = CalculateNewElementsCapacity(newLength);
const newElements: FixedArrayType =
Extract<FixedArrayType>(elements, 0, actualStart, capacity);
a.elements = newElements;
......
......@@ -2599,9 +2599,9 @@ LoadElementNoHole<FixedArray>(implicit context: Context)(
a: JSArray, index: Smi): Object
labels IfHole {
try {
let elements: FixedArray =
const elements: FixedArray =
Cast<FixedArray>(a.elements) otherwise Unexpected;
let e: Object = elements.objects[index];
const e: Object = elements.objects[index];
if (e == Hole) {
goto IfHole;
}
......@@ -2616,9 +2616,10 @@ LoadElementNoHole<FixedDoubleArray>(implicit context: Context)(
a: JSArray, index: Smi): Object
labels IfHole {
try {
let elements: FixedDoubleArray =
const elements: FixedDoubleArray =
Cast<FixedDoubleArray>(a.elements) otherwise Unexpected;
let e: float64 = LoadDoubleWithHoleCheck(elements, index) otherwise IfHole;
const e: float64 =
LoadDoubleWithHoleCheck(elements, index) otherwise IfHole;
return AllocateHeapNumberWithValue(e);
}
label Unexpected {
......@@ -2706,12 +2707,12 @@ struct FastJSArrayWitness {
MoveElements(dst: intptr, src: intptr, length: intptr) {
assert(this.arrayIsPushable);
if (this.hasDoubles) {
let elements: FixedDoubleArray =
const elements: FixedDoubleArray =
Cast<FixedDoubleArray>(this.unstable.elements)
otherwise unreachable;
TorqueMoveElements(elements, dst, src, length);
} else {
let elements: FixedArray = Cast<FixedArray>(this.unstable.elements)
const elements: FixedArray = Cast<FixedArray>(this.unstable.elements)
otherwise unreachable;
if (this.hasSmis) {
TorqueMoveElementsSmi(elements, dst, src, length);
......@@ -2730,7 +2731,7 @@ struct FastJSArrayWitness {
}
macro NewFastJSArrayWitness(array: FastJSArray): FastJSArrayWitness {
let kind = array.map.elements_kind;
const kind = array.map.elements_kind;
return FastJSArrayWitness{
stable: array,
unstable: array,
......@@ -2782,7 +2783,7 @@ macro NumberIsNaN(number: Number): bool {
return false;
}
case (hn: HeapNumber): {
let value: float64 = Convert<float64>(hn);
const value: float64 = Convert<float64>(hn);
return value != value;
}
}
......@@ -2806,7 +2807,7 @@ transitioning macro ToIndex(input: Object, context: Context): Number
return 0;
}
let value: Number = ToInteger_Inline(context, input, kTruncateMinusZero);
const value: Number = ToInteger_Inline(context, input, kTruncateMinusZero);
if (value < 0 || value > kMaxSafeInteger) {
goto RangeError;
}
......@@ -2928,7 +2929,7 @@ namespace runtime {
transitioning builtin FastCreateDataProperty(implicit context: Context)(
receiver: JSReceiver, key: Object, value: Object): Object {
try {
let array = Cast<FastJSArray>(receiver) otherwise Slow;
const array = Cast<FastJSArray>(receiver) otherwise Slow;
const index: Smi = Cast<Smi>(key) otherwise goto Slow;
if (index < 0 || index > array.length) goto Slow;
array::EnsureWriteableFastElements(array);
......
......@@ -67,7 +67,7 @@ namespace bigint {
if (length > kBigIntMaxLength) {
ThrowRangeError(kBigIntTooBig);
}
let result: MutableBigInt = AllocateBigInt(length);
const result: MutableBigInt = AllocateBigInt(length);
WriteBigIntSignAndLength(result, sign, length);
return result;
......@@ -93,7 +93,7 @@ namespace bigint {
return resultSign == xsign ? x : BigIntUnaryMinus(x);
}
let result = AllocateEmptyBigInt(resultSign, xlength);
const result = AllocateEmptyBigInt(resultSign, xlength);
CppAbsoluteSubAndCanonicalize(result, x, y);
return Convert<BigInt>(result);
}
......
This diff is collapsed.
......@@ -137,7 +137,7 @@ namespace regexp_replace {
let result: String = kEmptyString;
let lastMatchEnd: Smi = 0;
let unicode: bool = false;
let replaceLength: Smi = replaceString.length_smi;
const replaceLength: Smi = replaceString.length_smi;
const global: bool = regexp.global;
if (global) {
......
......@@ -63,7 +63,7 @@ namespace string {
const searchLength: Smi = searchStr.length_smi;
// 10. Let start be end - searchLength.
let start = end - searchLength;
const start = end - searchLength;
// 11. If start is less than 0, return false.
if (start < 0) return False;
......
......@@ -322,7 +322,7 @@ namespace typed_array {
let loadfn: LoadFn;
let storefn: StoreFn;
let elementsKind: ElementsKind = array.elements_kind;
const elementsKind: ElementsKind = array.elements_kind;
if (IsElementsKindGreaterThan(elementsKind, UINT32_ELEMENTS)) {
if (elementsKind == INT32_ELEMENTS) {
......
......@@ -1830,7 +1830,7 @@ LocationReference ImplementationVisitor::GetLocationReference(
return LocationReference::Temporary(
(*value)->value, "constant value " + expr->name->value);
}
return LocationReference::VariableAccess((*value)->value);
return LocationReference::VariableAccess((*value)->value, *value);
}
}
......@@ -1927,6 +1927,12 @@ void ImplementationVisitor::GenerateAssignToLocation(
GenerateImplicitConvert(variable.type(), assignment_value);
assembler().Poke(variable.stack_range(), converted_value.stack_range(),
variable.type());
// Local variables are detected by the existence of a binding. Assignment
// to local variables is recorded to support lint errors.
if (reference.binding()) {
(*reference.binding())->SetWritten();
}
} else if (reference.IsIndexedFieldAccess()) {
ReportError("assigning a value directly to an indexed field isn't allowed");
} else if (reference.IsHeapReference()) {
......
......@@ -19,6 +19,10 @@ namespace v8 {
namespace internal {
namespace torque {
template <typename T>
class Binding;
struct LocalValue;
// LocationReference is the representation of an l-value, so a value that might
// allow for assignment. For uniformity, this class can also represent
// unassignable temporaries. Assignable values fall in two categories:
......@@ -27,10 +31,13 @@ namespace torque {
class LocationReference {
public:
// An assignable stack range.
static LocationReference VariableAccess(VisitResult variable) {
static LocationReference VariableAccess(
VisitResult variable,
base::Optional<Binding<LocalValue>*> binding = base::nullopt) {
DCHECK(variable.IsOnStack());
LocationReference result;
result.variable_ = std::move(variable);
result.binding_ = binding;
return result;
}
// An unassignable value. {description} is only used for error messages.
......@@ -146,6 +153,10 @@ class LocationReference {
DCHECK(IsCallAccess());
return *assign_function_;
}
base::Optional<Binding<LocalValue>*> binding() const {
DCHECK(IsVariableAccess());
return binding_;
}
private:
base::Optional<VisitResult> variable_;
......@@ -156,6 +167,7 @@ class LocationReference {
base::Optional<std::string> assign_function_;
VisitResultVector call_arguments_;
base::Optional<std::string> index_field_;
base::Optional<Binding<LocalValue>*> binding_;
LocationReference() = default;
};
......@@ -198,7 +210,8 @@ class Binding : public T {
manager_(manager),
name_(name),
previous_binding_(this),
used_(false) {
used_(false),
written_(false) {
std::swap(previous_binding_, manager_->current_bindings_[name]);
}
template <class... Args>
......@@ -207,15 +220,23 @@ class Binding : public T {
declaration_position_ = name->pos;
}
~Binding() {
manager_->current_bindings_[name_] = previous_binding_;
if (!used_ && name_.length() > 0 && name_[0] != '_') {
if (!used_ && !SkipLintCheck()) {
Lint(BindingTypeString(), "'", name_,
"' is never used. Prefix with '_' if this is intentional.")
.Position(declaration_position_);
}
if (CheckWritten() && !written_ && !SkipLintCheck()) {
Lint(BindingTypeString(), "'", name_,
"' is never assigned to. Use 'const' instead of 'let'.")
.Position(declaration_position_);
}
manager_->current_bindings_[name_] = previous_binding_;
}
std::string BindingTypeString() const { return ""; }
std::string BindingTypeString() const;
bool CheckWritten() const;
const std::string& name() const { return name_; }
SourcePosition declaration_position() const { return declaration_position_; }
......@@ -223,12 +244,18 @@ class Binding : public T {
bool Used() const { return used_; }
void SetUsed() { used_ = true; }
bool Written() const { return written_; }
void SetWritten() { written_ = true; }
private:
bool SkipLintCheck() const { return name_.length() > 0 && name_[0] == '_'; }
BindingsManager<T>* manager_;
const std::string name_;
base::Optional<Binding*> previous_binding_;
SourcePosition declaration_position_ = CurrentSourcePosition::Get();
bool used_;
bool written_;
DISALLOW_COPY_AND_ASSIGN(Binding);
};
......@@ -296,9 +323,19 @@ inline std::string Binding<LocalValue>::BindingTypeString() const {
return "Variable ";
}
template <>
inline bool Binding<LocalValue>::CheckWritten() const {
// Do the check only for non-const variables and non struct types.
auto binding = *manager_->current_bindings_[name_];
return !binding->is_const && !binding->value.type()->IsStructType();
}
template <>
inline std::string Binding<LocalLabel>::BindingTypeString() const {
return "Label ";
}
template <>
inline bool Binding<LocalLabel>::CheckWritten() const {
return false;
}
struct Arguments {
VisitResultVector parameters;
......
......@@ -126,8 +126,8 @@ namespace test {
@export
macro TestPartiallyUnusedLabel(): Boolean {
let r1: bool = CallLabelTestHelper4(true);
let r2: bool = CallLabelTestHelper4(false);
const r1: bool = CallLabelTestHelper4(true);
const r2: bool = CallLabelTestHelper4(false);
if (r1 && !r2) {
return True;
......@@ -200,14 +200,15 @@ namespace test {
@export
macro TestTernaryOperator(x: Smi): Smi {
let b: bool = x < 0 ? true : false;
const b: bool = x < 0 ? true : false;
return b ? x - 10 : x + 100;
}
@export
macro TestFunctionPointerToGeneric(c: Context) {
let fptr1: builtin(Context, Smi) => Object = GenericBuiltinTest<Smi>;
let fptr2: builtin(Context, Object) => Object = GenericBuiltinTest<Object>;
const fptr1: builtin(Context, Smi) => Object = GenericBuiltinTest<Smi>;
const fptr2: builtin(Context, Object) => Object =
GenericBuiltinTest<Object>;
check(fptr1(c, 0) == Null);
check(fptr1(c, 1) == Null);
......@@ -224,7 +225,7 @@ namespace test {
@export
macro TestUnsafeCast(implicit context: Context)(n: Number): Boolean {
if (TaggedIsSmi(n)) {
let m: Smi = UnsafeCast<Smi>(n);
const m: Smi = UnsafeCast<Smi>(n);
check(TestHelperPlus1(context, m) == 11);
return True;
......@@ -246,7 +247,7 @@ namespace test {
@export
macro TestMultilineAssert() {
let someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5;
const someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5;
check(
someVeryLongVariableNameThatWillCauseLineBreaks > 0 &&
someVeryLongVariableNameThatWillCauseLineBreaks < 10);
......@@ -313,7 +314,7 @@ namespace test {
let a: TestStructA =
TestStructA{indexes: UnsafeCast<FixedArray>(kEmptyFixedArray), i: 13, k: 5};
let _b: TestStructA = a;
let c: TestStructA = TestStruct2();
const c: TestStructA = TestStruct2();
a.i = TestStruct1(c);
a.k = a.i;
let d: TestStructB;
......@@ -689,7 +690,7 @@ namespace test {
@export
macro TestQualifiedAccess(implicit context: Context)() {
let s: Smi = 0;
const s: Smi = 0;
check(!array::IsJSArray(s));
}
......@@ -873,7 +874,7 @@ namespace test {
@export
macro TestNewFixedArrayFromSpread(implicit context: Context)(): Object {
const i = TestIterator{count: 5};
let i = TestIterator{count: 5};
return new FixedArray{map: kFixedArrayMap, length: 5, objects: ...i};
}
......
......@@ -295,6 +295,25 @@ TEST(Torque, ImportNonExistentFile) {
HasSubstr("File 'foo/bar.tq' not found."));
}
TEST(Torque, LetShouldBeConstLintError) {
ExpectFailingCompilation(R"(
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; }
macro Bar(x: Smi): Foo {
let foo = Foo{a: x};
return foo;
}
)");
}
} // namespace torque
} // namespace internal
} // namespace v8
......@@ -144,7 +144,7 @@ namespace array {
try {
GotoIfForceSlowPath() otherwise Slow;
let a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
const a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
// Copy copy-on-write (COW) arrays.
array::EnsureWriteableFastElements(a);
......@@ -477,7 +477,7 @@ namespace array {
} else {
let srcIdx: Smi = srcPos;
let dstIdx: Smi = dstPos;
let to: Smi = srcPos + length;
const to: Smi = srcPos + length;
while (srcIdx < to) {
target.objects[dstIdx++] = source.objects[srcIdx++];
......@@ -566,7 +566,7 @@ namespace array {
const workArray = sortState.workArray;
let low: Smi = lowArg + 1;
const low: Smi = lowArg + 1;
if (low == high) return 1;
let runLength: Smi = 2;
......@@ -631,7 +631,7 @@ namespace array {
const pendingRuns: FixedArray = sortState.pendingRuns;
let baseA: Smi = GetPendingRunBase(pendingRuns, i);
let lengthA: Smi = GetPendingRunLength(pendingRuns, i);
let baseB: Smi = GetPendingRunBase(pendingRuns, i + 1);
const baseB: Smi = GetPendingRunBase(pendingRuns, i + 1);
let lengthB: Smi = GetPendingRunLength(pendingRuns, i + 1);
assert(lengthA > 0 && lengthB > 0);
assert(baseA + lengthA == baseB);
......@@ -710,7 +710,7 @@ namespace array {
// a[base + hint + lastOfs] < key <= a[base + hint + offset].
// a[base + length - 1] is highest.
let maxOfs: Smi = length - hint;
const maxOfs: Smi = length - hint;
while (offset < maxOfs) {
const offsetElement = array.objects[base + hint + offset];
order = sortState.Compare(offsetElement, key);
......@@ -736,7 +736,7 @@ namespace array {
assert(order >= 0);
// a[base + hint] is lowest.
let maxOfs: Smi = hint + 1;
const maxOfs: Smi = hint + 1;
while (offset < maxOfs) {
const offsetElement = array.objects[base + hint - offset];
order = sortState.Compare(offsetElement, key);
......@@ -807,7 +807,7 @@ namespace array {
// a[base + hint - offset] <= key < a[base + hint - lastOfs].
// a[base + hint] is lowest.
let maxOfs: Smi = hint + 1;
const maxOfs: Smi = hint + 1;
while (offset < maxOfs) {
const offsetElement = array.objects[base + hint - offset];
order = sortState.Compare(key, offsetElement);
......@@ -832,7 +832,7 @@ namespace array {
// a[base + hint + lastOfs] <= key < a[base + hint + offset].
// a[base + length - 1] is highest.
let maxOfs: Smi = length - hint;
const maxOfs: Smi = length - hint;
while (offset < maxOfs) {
const offsetElement = array.objects[base + hint + offset];
order = sortState.Compare(key, offsetElement);
......@@ -920,7 +920,7 @@ namespace array {
while (Int32TrueConstant()) {
assert(lengthA > 1 && lengthB > 0);
let order = sortState.Compare(
const order = sortState.Compare(
workArray.objects[cursorB], tempArray.objects[cursorTemp]);
if (order < 0) {
......@@ -1052,7 +1052,7 @@ namespace array {
while (Int32TrueConstant()) {
assert(lengthA > 0 && lengthB > 1);
let order = sortState.Compare(
const order = sortState.Compare(
tempArray.objects[cursorTemp], workArray.objects[cursorA]);
if (order < 0) {
......@@ -1222,7 +1222,7 @@ namespace array {
// remains. This is used at the end of the mergesort.
transitioning macro
MergeForceCollapse(context: Context, sortState: SortState) {
let pendingRuns: FixedArray = sortState.pendingRuns;
const pendingRuns: FixedArray = sortState.pendingRuns;
// Reload the stack size becuase MergeAt might change it.
while (GetPendingRunsSize(sortState) > 1) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment