For loop for arrays (#1753)

Co-authored-by: m new <michael.burzan@outlook.de>
Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
pmqtt
2022-08-09 15:34:24 -07:00
committed by GitHub
co-authored by m new Geoff Romer Richard Smith
parent 2c921efe36
commit f754373786
23 changed files with 361 additions and 4 deletions
+7
View File
@@ -265,6 +265,12 @@ message WhileStatement {
optional BlockStatement body = 2;
}
message ForStatement {
optional BindingPattern var_decl = 1;
optional Expression target = 2;
optional BlockStatement body = 3;
}
message MatchClause {
optional Pattern pattern = 1;
optional Statement statement = 2;
@@ -306,6 +312,7 @@ message Statement {
AwaitStatement await_statement = 12;
BreakStatement break_statement = 13;
ContinueStatement continue_statement = 14;
ForStatement for_statement = 15;
}
}
+10
View File
@@ -586,6 +586,16 @@ static auto StatementToCarbon(const Fuzzing::Statement& statement,
BlockStatementToCarbon(while_statement.body(), out);
break;
}
case Fuzzing::Statement::kForStatement: {
const auto& for_statement = statement.for_statement();
out << "for (";
BindingPatternToCarbon(for_statement.var_decl(), out);
out << " in ";
ExpressionToCarbon(for_statement.target(), out);
out << ") ";
BlockStatementToCarbon(for_statement.body(), out);
break;
}
case Fuzzing::Statement::kMatch: {
const auto& match = statement.match();
+1
View File
@@ -40,6 +40,7 @@ abstract class Statement : AstNode;
class Continuation : Statement;
class Run : Statement;
class Await : Statement;
class For : Statement;
abstract class Expression : AstNode;
class BoolTypeLiteral : Expression;
class BoolLiteral : Expression;
+7
View File
@@ -42,6 +42,13 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const {
while_stmt.body().PrintDepth(depth - 1, out);
break;
}
case StatementKind::For: {
const auto& for_stmt = cast<For>(*this);
out << "for (" << for_stmt.variable_declaration() << " in "
<< for_stmt.loop_target() << ")\n";
for_stmt.body().PrintDepth(depth - 1, out);
break;
}
case StatementKind::Break:
out << "break;";
break;
+32
View File
@@ -298,6 +298,38 @@ class While : public Statement {
Nonnull<Block*> body_;
};
class For : public Statement {
public:
For(SourceLocation source_loc, Nonnull<BindingPattern*> variable_declaration,
Nonnull<Expression*> loop_target, Nonnull<Block*> body)
: Statement(AstNodeKind::For, source_loc),
variable_declaration_(variable_declaration),
loop_target_(loop_target),
body_(body) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFor(node->kind());
}
auto variable_declaration() const -> const BindingPattern& {
return *variable_declaration_;
}
auto variable_declaration() -> BindingPattern& {
return *variable_declaration_;
}
auto loop_target() const -> const Expression& { return *loop_target_; }
auto loop_target() -> Expression& { return *loop_target_; }
auto body() const -> const Block& { return *body_; }
auto body() -> Block& { return *body_; }
private:
Nonnull<BindingPattern*> variable_declaration_;
Nonnull<Expression*> loop_target_;
Nonnull<Block*> body_;
};
class Break : public Statement {
public:
explicit Break(SourceLocation source_loc)
+10
View File
@@ -521,6 +521,16 @@ static auto StatementToProto(const Statement& statement) -> Fuzzing::Statement {
// Initializes with the default value; there's nothing to set.
statement_proto.mutable_continue_statement();
break;
case StatementKind::For: {
const auto& for_stmt = cast<For>(statement);
auto* for_proto = statement_proto.mutable_for_statement();
*for_proto->mutable_var_decl() =
BindingPatternToProto(for_stmt.variable_declaration());
*for_proto->mutable_target() = ExpressionToProto(for_stmt.loop_target());
*for_proto->mutable_body() = BlockStatementToProto(for_stmt.body());
break;
}
}
return statement_proto;
}
+4 -1
View File
@@ -116,7 +116,10 @@ class Action {
auto results() const -> const std::vector<Nonnull<const Value*>>& {
return results_;
}
void ReplaceResult(std::size_t index, Nonnull<const Value*> value) {
CARBON_CHECK(index < results_.size());
results_[index] = value;
}
// Appends `result` to `results`.
void AddResult(Nonnull<const Value*> result) { results_.push_back(result); }
+71
View File
@@ -1505,7 +1505,78 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
}
}
}
case StatementKind::For: {
constexpr int TargetVarPosInResult = 0;
constexpr int CurrentIndexPosInResult = 1;
constexpr int EndIndexPosInResult = 2;
constexpr int LoopVarPosInResult = 3;
if (act.pos() == 0) {
return todo_.Spawn(
std::make_unique<ExpressionAction>(&cast<For>(stmt).loop_target()));
}
if (act.pos() == 1) {
Nonnull<const TupleValue*> source_array =
cast<const TupleValue>(act.results()[TargetVarPosInResult]);
auto end_index = static_cast<int>(source_array->elements().size());
if (end_index == 0) {
return todo_.FinishAction();
}
act.AddResult(arena_->New<IntValue>(0));
act.AddResult(arena_->New<IntValue>(end_index));
return todo_.Spawn(std::make_unique<PatternAction>(
&cast<For>(stmt).variable_declaration()));
}
if (act.pos() == 2) {
Nonnull<const BindingPlaceholderValue*> loop_var =
cast<const BindingPlaceholderValue>(
act.results()[LoopVarPosInResult]);
Nonnull<const TupleValue*> source_array =
cast<const TupleValue>(act.results()[TargetVarPosInResult]);
auto start_index =
cast<IntValue>(act.results()[CurrentIndexPosInResult])->value();
todo_.Initialize(*(loop_var->value_node()),
source_array->elements()[start_index]);
act.ReplaceResult(CurrentIndexPosInResult,
arena_->New<IntValue>(start_index + 1));
return todo_.Spawn(
std::make_unique<StatementAction>(&cast<For>(stmt).body()));
}
if (act.pos() >= 3) {
auto current_index =
cast<IntValue>(act.results()[CurrentIndexPosInResult])->value();
auto end_index =
cast<IntValue>(act.results()[EndIndexPosInResult])->value();
if (current_index < end_index) {
Nonnull<const TupleValue*> source_array =
cast<const TupleValue>(act.results()[TargetVarPosInResult]);
Nonnull<const BindingPlaceholderValue*> loop_var =
cast<const BindingPlaceholderValue>(
act.results()[LoopVarPosInResult]);
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> assigned_array_element,
todo_.ValueOfNode(*(loop_var->value_node()), stmt.source_loc()));
auto lvalue = cast<LValue>(assigned_array_element);
CARBON_RETURN_IF_ERROR(heap_.Write(
lvalue->address(), source_array->elements()[current_index],
stmt.source_loc()));
act.ReplaceResult(CurrentIndexPosInResult,
arena_->New<IntValue>(current_index + 1));
return todo_.Spawn(
std::make_unique<StatementAction>(&cast<For>(stmt).body()));
}
}
return todo_.FinishAction();
}
case StatementKind::While:
// TODO: Rewrite While to use ReplaceResult to store condition result.
// This will remove the inconsistency between the while and for
// loops.
if (act.pos() % 2 == 0) {
// { { (while (e) s) :: C, E, F} :: S, H}
// -> { { e :: (while ([]) s) :: C, E, F} :: S, H}
@@ -106,6 +106,11 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
}
return Success();
}
case StatementKind::For: {
CARBON_RETURN_IF_ERROR(ResolveControlFlow(&cast<For>(*statement).body(),
statement, function));
return Success();
}
case StatementKind::While:
CARBON_RETURN_IF_ERROR(ResolveControlFlow(&cast<While>(*statement).body(),
statement, function));
+12
View File
@@ -413,6 +413,18 @@ static auto ResolveNames(Statement& statement, StaticScope& enclosing_scope)
CARBON_RETURN_IF_ERROR(ResolveNames(while_stmt.body(), enclosing_scope));
break;
}
case StatementKind::For: {
StaticScope statement_scope;
statement_scope.AddParent(&enclosing_scope);
auto& for_stmt = cast<For>(statement);
CARBON_RETURN_IF_ERROR(
ResolveNames(for_stmt.loop_target(), statement_scope));
CARBON_RETURN_IF_ERROR(
ResolveNames(for_stmt.variable_declaration(), statement_scope));
CARBON_RETURN_IF_ERROR(ResolveNames(for_stmt.body(), statement_scope));
break;
}
case StatementKind::Match: {
auto& match = cast<Match>(statement);
CARBON_RETURN_IF_ERROR(ResolveNames(match.expression(), enclosing_scope));
@@ -196,6 +196,7 @@ static auto ResolveUnformed(
case StatementKind::Continuation:
case StatementKind::Run:
case StatementKind::Await:
case StatementKind::For:
// do nothing
break;
}
+24
View File
@@ -2965,6 +2965,29 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
CARBON_RETURN_IF_ERROR(TypeCheckStmt(&while_stmt.body(), impl_scope));
return Success();
}
case StatementKind::For: {
auto& for_stmt = cast<For>(*s);
ImplScope inner_impl_scope;
inner_impl_scope.AddParent(&impl_scope);
CARBON_RETURN_IF_ERROR(
TypeCheckExp(&for_stmt.loop_target(), inner_impl_scope));
const Value& rhs = for_stmt.loop_target().static_type();
if (rhs.kind() == Value::Kind::StaticArrayType) {
CARBON_RETURN_IF_ERROR(
TypeCheckPattern(&for_stmt.variable_declaration(),
&cast<StaticArrayType>(rhs).element_type(),
inner_impl_scope, ValueCategory::Var));
} else {
return CompilationError(for_stmt.source_loc())
<< "expected array type after in, found value of type " << rhs;
}
CARBON_RETURN_IF_ERROR(TypeCheckStmt(&for_stmt.body(), inner_impl_scope));
return Success();
}
case StatementKind::Break:
case StatementKind::Continue:
return Success();
@@ -3163,6 +3186,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
case StatementKind::Assign:
case StatementKind::ExpressionStatement:
case StatementKind::While:
case StatementKind::For:
case StatementKind::Break:
case StatementKind::Continue:
case StatementKind::VariableDefinition:
+4
View File
@@ -64,6 +64,7 @@ EXTERNAL "external"
FALSE "false"
FN "fn"
FN_TYPE "__Fn"
FOR "for"
FORALL "forall"
GREATER ">"
GREATER_EQUAL ">="
@@ -71,6 +72,7 @@ GREATER_GREATER ">>"
IF "if"
IMPL "impl"
IMPORT "import"
IN "in"
INTERFACE "interface"
IS "is"
LEFT_CURLY_BRACE "{"
@@ -165,6 +167,7 @@ operand_start [(A-Za-z0-9_\"]
{FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); }
{FN} { return CARBON_SIMPLE_TOKEN(FN); }
{FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); }
{FOR} { return CARBON_SIMPLE_TOKEN(FOR); }
{GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_EQUAL); }
{GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); }
{GREATER} { return CARBON_SIMPLE_TOKEN(GREATER); }
@@ -172,6 +175,7 @@ operand_start [(A-Za-z0-9_\"]
{IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); }
{IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); }
{INTERFACE} { return CARBON_SIMPLE_TOKEN(INTERFACE); }
{IN} { return CARBON_SIMPLE_TOKEN(IN); }
{IS} { return CARBON_SIMPLE_TOKEN(IS); }
{LEFT_CURLY_BRACE} { return CARBON_SIMPLE_TOKEN(LEFT_CURLY_BRACE); }
{LEFT_PARENTHESIS} { return CARBON_SIMPLE_TOKEN(LEFT_PARENTHESIS); }
+4
View File
@@ -228,6 +228,7 @@
FALSE
FN
FN_TYPE
FOR
FORALL
GREATER
GREATER_EQUAL
@@ -235,6 +236,7 @@
IF
IMPL
IMPORT
IN
INTERFACE
IS
LEFT_CURLY_BRACE
@@ -934,6 +936,8 @@ statement:
{ $$ = arena->New<Run>(context.source_loc(), $2); }
| AWAIT SEMICOLON
{ $$ = arena->New<Await>(context.source_loc()); }
| FOR LEFT_PARENTHESIS variable_declaration IN type_expression RIGHT_PARENTHESIS block
{ $$ = arena->New<For>(context.source_loc(), $3, $5, $7); }
;
if_statement:
IF LEFT_PARENTHESIS expression RIGHT_PARENTHESIS block optional_else
+1 -1
View File
@@ -12,7 +12,7 @@ package ExplorerTest api;
fn Main() -> i32 {
// error
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_missing_var.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON
x : i32;
return 1;
}
+1 -1
View File
@@ -14,7 +14,7 @@ fn Main() -> i32 {
// Error: can't use keyword `Self` as the name of a variable.
// TODO: Current error message is unclear, better would be to say
// something like: unexpected `Self`, expecting identifier
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL or SEMICOLON
var Self : i32 = 0;
return Self;
}
+37
View File
@@ -0,0 +1,37 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: result: 8
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 4] = (0, 1,2,3);
var count : i32 = 0;
for( x: i32 in ar){
Print("HALLO WELT ", x);
count = count +1;
}
for( x: i32 in ar){
Print("HALLO WELT ", x);
count = count +1;
}
return count;
}
+29
View File
@@ -0,0 +1,29 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: HALLO WELT
// CHECK: result: 4
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 4] = (0, 1,2,3);
var count : i32 = 0;
for( x: auto in ar){
Print("HALLO WELT ", x);
count = count +1;
}
return count;
}
+27
View File
@@ -0,0 +1,27 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 3
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 4] = (0, 1,2,3);
var count : i32 = 0;
for( x: i32 in ar){
count = count +x;
if( x == 2){
break;
}
}
return count;
}
+27
View File
@@ -0,0 +1,27 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 4
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 4] = (0, 1,2,3);
var count : i32 = 0;
for( x: i32 in ar){
if( x == 2){
continue;
}
count = count +x;
}
return count;
}
+22
View File
@@ -0,0 +1,22 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 0
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 0] = () ;
var count : i32 = 0;
for( x: i32 in ar ){
count = 2;
}
return count;
}
+24
View File
@@ -0,0 +1,24 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 20
package ExplorerTest api;
fn Main() -> i32 {
var ar: [i32; 4] = (0, 1,2,3);
var count : i32 = 0;
for( x: i32 in ar){
count = count +1;
for( x: i32 in ar){
count = count +1;
}
}
return count;
}
+1 -1
View File
@@ -12,7 +12,7 @@ package ExplorerTest api;
fn Main() -> i32 {
// Error: Can't use keyword `Self` as the name of a local.
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting MINUS or PLUS
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL
let Self: auto = 10;
return 0;
}