dart-sdk/runtime/vm/scopes_test.cc
Alexander Markov 9be13288b0 [vm] Support multiple local variables with the same name in the same scope
When lowering patterns, front-end can add multiple distinct local
variables with the same name into the same local scope.
Previously, VM identified local variables in a local scope by name.
However, this no longer works with patterns.

With this change, local variables are now identified by pair (name,
kernel offset). Name is still taken into account as compiler can add
extra variables which do not correspond to kernel variables,
such as 'this'.

TEST=runtime/tests/vm/dart/regress_51091_test.dart
Fixes https://github.com/dart-lang/sdk/issues/51091
Issue https://github.com/dart-lang/sdk/issues/49755

Change-Id: I0263769cb31f3f8d9652f5d6534800510ac882fb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279650
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2023-01-31 18:47:46 +00:00

118 lines
4.3 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/scopes.h"
#include "platform/assert.h"
#include "vm/unit_test.h"
namespace dart {
ISOLATE_UNIT_TEST_CASE(LocalScope) {
// Allocate a couple of local variables first.
const Type& dynamic_type = Type::ZoneHandle(Type::DynamicType());
const String& a = String::ZoneHandle(Symbols::New(thread, "a"));
LocalVariable* var_a = new LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource, a, dynamic_type);
LocalVariable* inner_var_a = new LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource, a, dynamic_type);
const String& b = String::ZoneHandle(Symbols::New(thread, "b"));
LocalVariable* var_b = new LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource, b, dynamic_type);
const String& c = String::ZoneHandle(Symbols::New(thread, "c"));
LocalVariable* var_c = new LocalVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource, c, dynamic_type);
LocalScope* outer_scope = new LocalScope(NULL, 0, 0);
LocalScope* inner_scope1 = new LocalScope(outer_scope, 0, 0);
LocalScope* inner_scope2 = new LocalScope(outer_scope, 0, 0);
EXPECT(outer_scope->parent() == NULL);
EXPECT_EQ(outer_scope, inner_scope1->parent());
EXPECT_EQ(outer_scope, inner_scope2->parent());
EXPECT_EQ(inner_scope2, outer_scope->child());
EXPECT_EQ(inner_scope1, inner_scope2->sibling());
EXPECT(inner_scope1->child() == NULL);
EXPECT(inner_scope2->child() == NULL);
// Populate the local scopes as follows:
// { // outer_scope
// var a;
// { // inner_scope1
// var b;
// }
// L: { // inner_scope2
// var c;
// }
// }
EXPECT(outer_scope->AddVariable(var_a));
EXPECT(inner_scope1->AddVariable(var_b));
EXPECT(inner_scope2->AddVariable(var_c));
EXPECT(!outer_scope->AddVariable(var_a));
// Check the simple layout above.
EXPECT_EQ(var_a, outer_scope->LocalLookupVariable(
a, LocalVariable::kNoKernelOffset));
EXPECT_EQ(var_a, inner_scope1->LookupVariable(
a, LocalVariable::kNoKernelOffset, true));
EXPECT(outer_scope->LocalLookupVariable(b, LocalVariable::kNoKernelOffset) ==
NULL);
EXPECT(inner_scope1->LocalLookupVariable(c, LocalVariable::kNoKernelOffset) ==
NULL);
// Modify the local scopes to contain shadowing:
// { // outer_scope
// var a;
// { // inner_scope1
// var b;
// var a; // inner_var_a
// }
// { // inner_scope2
// var c;
// L: ...
// }
// }
EXPECT(inner_scope1->AddVariable(inner_var_a));
EXPECT_EQ(inner_var_a, inner_scope1->LookupVariable(
a, LocalVariable::kNoKernelOffset, true));
EXPECT(inner_scope1->LookupVariable(a, LocalVariable::kNoKernelOffset,
true) != var_a);
// Modify the local scopes with access of an outer scope variable:
// { // outer_scope
// var a;
// { // inner_scope1
// var b;
// var a; // inner_var_a
// }
// { // inner_scope2
// var c = a;
// L: ...
// }
// }
EXPECT(inner_scope2->LocalLookupVariable(a, LocalVariable::kNoKernelOffset) ==
NULL);
EXPECT(inner_scope2->AddVariable(var_a));
EXPECT_EQ(var_a, inner_scope2->LocalLookupVariable(
a, LocalVariable::kNoKernelOffset));
EXPECT_EQ(1, outer_scope->num_variables());
EXPECT_EQ(2, inner_scope1->num_variables());
EXPECT_EQ(2, inner_scope2->num_variables());
// Cannot depend on the order, but we should find the variables.
EXPECT(outer_scope->VariableAt(0) == var_a);
EXPECT((inner_scope1->VariableAt(0) == inner_var_a) ||
(inner_scope1->VariableAt(1) == inner_var_a));
EXPECT((inner_scope1->VariableAt(0) == var_b) ||
(inner_scope1->VariableAt(1) == var_b));
EXPECT((inner_scope2->VariableAt(0) == var_a) ||
(inner_scope2->VariableAt(1) == var_a) ||
(inner_scope2->VariableAt(2) == var_a));
EXPECT((inner_scope2->VariableAt(0) == var_c) ||
(inner_scope2->VariableAt(1) == var_c) ||
(inner_scope2->VariableAt(2) == var_c));
}
} // namespace dart