mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Initial support for statically-sized arrays (#1158)
This commit is contained in:
@@ -110,6 +110,11 @@ message TypeTypeLiteral {}
|
||||
|
||||
message UnimplementedExpression {}
|
||||
|
||||
message ArrayTypeLiteral {
|
||||
optional Expression element_type = 1;
|
||||
optional Expression size = 2;
|
||||
}
|
||||
|
||||
message Expression {
|
||||
oneof kind {
|
||||
CallExpression call = 1;
|
||||
@@ -132,6 +137,7 @@ message Expression {
|
||||
StringTypeLiteral string_type_literal = 18;
|
||||
TypeTypeLiteral type_type_literal = 19;
|
||||
UnimplementedExpression unimplemented_expression = 20;
|
||||
ArrayTypeLiteral array_type_literal = 21;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,3 +55,4 @@ abstract class Expression : AstNode;
|
||||
class IntrinsicExpression : Expression;
|
||||
class IfExpression : Expression;
|
||||
class UnimplementedExpression : Expression;
|
||||
class ArrayTypeLiteral : Expression;
|
||||
|
||||
@@ -176,6 +176,12 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
out << ")";
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::ArrayTypeLiteral: {
|
||||
const auto& array_literal = cast<ArrayTypeLiteral>(*this);
|
||||
out << "[" << array_literal.element_type_expression() << "; "
|
||||
<< array_literal.size_expression() << "]";
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::IdentifierExpression:
|
||||
case ExpressionKind::IntLiteral:
|
||||
case ExpressionKind::BoolLiteral:
|
||||
@@ -232,6 +238,7 @@ void Expression::PrintID(llvm::raw_ostream& out) const {
|
||||
case ExpressionKind::IntrinsicExpression:
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
case ExpressionKind::FunctionTypeLiteral:
|
||||
case ExpressionKind::ArrayTypeLiteral:
|
||||
out << "...";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -572,6 +572,39 @@ class UnimplementedExpression : public Expression {
|
||||
std::vector<Nonnull<AstNode*>> children_;
|
||||
};
|
||||
|
||||
// A literal representing a statically-sized array type.
|
||||
class ArrayTypeLiteral : public Expression {
|
||||
public:
|
||||
// Constructs an array type literal which uses the given expressions to
|
||||
// represent the element type and size.
|
||||
ArrayTypeLiteral(SourceLocation source_loc,
|
||||
Nonnull<Expression*> element_type_expression,
|
||||
Nonnull<Expression*> size_expression)
|
||||
: Expression(AstNodeKind::ArrayTypeLiteral, source_loc),
|
||||
element_type_expression_(element_type_expression),
|
||||
size_expression_(size_expression) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
return InheritsFromArrayTypeLiteral(node->kind());
|
||||
}
|
||||
|
||||
auto element_type_expression() const -> const Expression& {
|
||||
return *element_type_expression_;
|
||||
}
|
||||
auto element_type_expression() -> Expression& {
|
||||
return *element_type_expression_;
|
||||
}
|
||||
|
||||
auto size_expression() const -> const Expression& {
|
||||
return *size_expression_;
|
||||
}
|
||||
auto size_expression() -> Expression& { return *size_expression_; }
|
||||
|
||||
private:
|
||||
Nonnull<Expression*> element_type_expression_;
|
||||
Nonnull<Expression*> size_expression_;
|
||||
};
|
||||
|
||||
// Converts paren_contents to an Expression, interpreting the parentheses as
|
||||
// grouping if their contents permit that interpretation, or as forming a
|
||||
// tuple otherwise.
|
||||
|
||||
@@ -227,6 +227,17 @@ static auto ExpressionToProto(const Expression& expression)
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
expression_proto.mutable_unimplemented_expression();
|
||||
break;
|
||||
|
||||
case ExpressionKind::ArrayTypeLiteral: {
|
||||
const auto& array_literal = cast<ArrayTypeLiteral>(expression);
|
||||
Fuzzing::ArrayTypeLiteral* array_literal_proto =
|
||||
expression_proto.mutable_array_type_literal();
|
||||
*array_literal_proto->mutable_element_type() =
|
||||
ExpressionToProto(array_literal.element_type_expression());
|
||||
*array_literal_proto->mutable_size() =
|
||||
ExpressionToProto(array_literal.size_expression());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expression_proto;
|
||||
}
|
||||
|
||||
@@ -363,6 +363,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
case ExpressionKind::StringTypeLiteral:
|
||||
case ExpressionKind::IntrinsicExpression:
|
||||
case ExpressionKind::IfExpression:
|
||||
case ExpressionKind::ArrayTypeLiteral:
|
||||
FATAL() << "Can't treat expression as lvalue: " << exp;
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
FATAL() << "Unimplemented: " << exp;
|
||||
@@ -472,6 +473,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfInterfaceType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
case Value::Kind::StaticArrayType:
|
||||
// TODO: add `CHECK(TypeEqual(type, value->dynamic_type()))`, once we
|
||||
// have Value::dynamic_type.
|
||||
return value;
|
||||
@@ -506,15 +508,28 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
}
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& tuple = cast<TupleValue>(value);
|
||||
const auto& destination_tuple_type = cast<TupleValue>(destination_type);
|
||||
CHECK(tuple->elements().size() ==
|
||||
destination_tuple_type->elements().size());
|
||||
std::vector<Nonnull<const Value*>> destination_element_types;
|
||||
switch (destination_type->kind()) {
|
||||
case Value::Kind::TupleValue:
|
||||
destination_element_types =
|
||||
cast<TupleValue>(destination_type)->elements();
|
||||
break;
|
||||
case Value::Kind::StaticArrayType: {
|
||||
const auto& array_type = cast<StaticArrayType>(*destination_type);
|
||||
destination_element_types.resize(array_type.size(),
|
||||
&array_type.element_type());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
FATAL() << "Can't convert value " << *value << " to type "
|
||||
<< *destination_type;
|
||||
}
|
||||
CHECK(tuple->elements().size() == destination_element_types.size());
|
||||
std::vector<Nonnull<const Value*>> new_elements;
|
||||
for (size_t i = 0; i < tuple->elements().size(); ++i) {
|
||||
ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> val,
|
||||
Convert(tuple->elements()[i], destination_tuple_type->elements()[i],
|
||||
source_loc));
|
||||
ASSIGN_OR_RETURN(Nonnull<const Value*> val,
|
||||
Convert(tuple->elements()[i],
|
||||
destination_element_types[i], source_loc));
|
||||
new_elements.push_back(val);
|
||||
}
|
||||
return arena_->New<TupleValue>(std::move(new_elements));
|
||||
@@ -886,6 +901,19 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
FATAL() << "Unimplemented: " << exp;
|
||||
case ExpressionKind::ArrayTypeLiteral: {
|
||||
const auto& array_literal = cast<ArrayTypeLiteral>(exp);
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&array_literal.element_type_expression()));
|
||||
} else if (act.pos() == 1) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&array_literal.size_expression()));
|
||||
} else {
|
||||
return todo_.FinishAction(arena_->New<StaticArrayType>(
|
||||
act.results()[0], cast<IntValue>(act.results()[1])->value()));
|
||||
}
|
||||
}
|
||||
} // switch (exp->kind)
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,14 @@ static auto ResolveNames(Expression& expression,
|
||||
ResolveNames(*if_expr.else_expression(), enclosing_scope));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::ArrayTypeLiteral: {
|
||||
auto& array_literal = cast<ArrayTypeLiteral>(expression);
|
||||
RETURN_IF_ERROR(ResolveNames(array_literal.element_type_expression(),
|
||||
enclosing_scope));
|
||||
RETURN_IF_ERROR(
|
||||
ResolveNames(array_literal.size_expression(), enclosing_scope));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::BoolTypeLiteral:
|
||||
case ExpressionKind::BoolLiteral:
|
||||
case ExpressionKind::IntTypeLiteral:
|
||||
|
||||
@@ -97,6 +97,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfInterfaceType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
case Value::Kind::StaticArrayType:
|
||||
return true;
|
||||
case Value::Kind::AutoType:
|
||||
// `auto` isn't a concrete type, it's a pattern that matches types.
|
||||
@@ -183,25 +184,40 @@ auto TypeChecker::IsImplicitlyConvertible(Nonnull<const Value*> source,
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Value::Kind::TupleValue:
|
||||
if (destination->kind() == Value::Kind::TupleValue) {
|
||||
const std::vector<Nonnull<const Value*>>& source_elements =
|
||||
cast<TupleValue>(*source).elements();
|
||||
const std::vector<Nonnull<const Value*>>& destination_elements =
|
||||
cast<TupleValue>(*destination).elements();
|
||||
if (source_elements.size() != destination_elements.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < source_elements.size(); ++i) {
|
||||
if (!IsImplicitlyConvertible(source_elements[i],
|
||||
destination_elements[i])) {
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& source_tuple = cast<TupleValue>(*source);
|
||||
switch (destination->kind()) {
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& destination_tuple = cast<TupleValue>(*destination);
|
||||
if (source_tuple.elements().size() !=
|
||||
destination_tuple.elements().size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < source_tuple.elements().size(); ++i) {
|
||||
if (!IsImplicitlyConvertible(source_tuple.elements()[i],
|
||||
destination_tuple.elements()[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
case Value::Kind::StaticArrayType: {
|
||||
const auto& destination_array = cast<StaticArrayType>(*destination);
|
||||
if (destination_array.size() != source_tuple.elements().size()) {
|
||||
return false;
|
||||
}
|
||||
for (Nonnull<const Value*> source_element : source_tuple.elements()) {
|
||||
if (!IsImplicitlyConvertible(source_element,
|
||||
&destination_array.element_type())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case Value::Kind::TypeType:
|
||||
return destination->kind() == Value::Kind::InterfaceType;
|
||||
default:
|
||||
@@ -339,6 +355,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
|
||||
<< "actual: " << *arg_type;
|
||||
}
|
||||
// For the following cases, we check for type convertability.
|
||||
case Value::Kind::StaticArrayType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::InterfaceType:
|
||||
case Value::Kind::ChoiceType:
|
||||
@@ -423,6 +440,7 @@ auto TypeChecker::Substitute(
|
||||
}
|
||||
return new_class_type;
|
||||
}
|
||||
case Value::Kind::StaticArrayType:
|
||||
case Value::Kind::AutoType:
|
||||
case Value::Kind::IntType:
|
||||
case Value::Kind::BoolType:
|
||||
@@ -467,10 +485,14 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
case ExpressionKind::IndexExpression: {
|
||||
auto& index = cast<IndexExpression>(*e);
|
||||
RETURN_IF_ERROR(TypeCheckExp(&index.aggregate(), impl_scope));
|
||||
RETURN_IF_ERROR(TypeCheckExp(&index.offset(), impl_scope));
|
||||
const Value& aggregate_type = index.aggregate().static_type();
|
||||
switch (aggregate_type.kind()) {
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& tuple_type = cast<TupleValue>(aggregate_type);
|
||||
RETURN_IF_ERROR(ExpectExactType(index.offset().source_loc(),
|
||||
"tuple index", arena_->New<IntType>(),
|
||||
&index.offset().static_type()));
|
||||
ASSIGN_OR_RETURN(auto offset_value,
|
||||
InterpExp(&index.offset(), arena_, trace_));
|
||||
int i = cast<IntValue>(*offset_value).value();
|
||||
@@ -483,6 +505,15 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
index.set_value_category(index.aggregate().value_category());
|
||||
return Success();
|
||||
}
|
||||
case Value::Kind::StaticArrayType: {
|
||||
RETURN_IF_ERROR(ExpectExactType(index.offset().source_loc(),
|
||||
"array index", arena_->New<IntType>(),
|
||||
&index.offset().static_type()));
|
||||
index.set_static_type(
|
||||
&cast<StaticArrayType>(aggregate_type).element_type());
|
||||
index.set_value_category(index.aggregate().value_category());
|
||||
return Success();
|
||||
}
|
||||
default:
|
||||
return FATAL_COMPILATION_ERROR(e->source_loc()) << "expected a tuple";
|
||||
}
|
||||
@@ -989,6 +1020,34 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
}
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
FATAL() << "Unimplemented: " << *e;
|
||||
case ExpressionKind::ArrayTypeLiteral: {
|
||||
auto& array_literal = cast<ArrayTypeLiteral>(*e);
|
||||
RETURN_IF_ERROR(
|
||||
TypeCheckExp(&array_literal.element_type_expression(), impl_scope));
|
||||
ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> element_type,
|
||||
InterpExp(&array_literal.element_type_expression(), arena_, trace_));
|
||||
RETURN_IF_ERROR(ExpectIsConcreteType(
|
||||
array_literal.element_type_expression().source_loc(), element_type));
|
||||
|
||||
RETURN_IF_ERROR(
|
||||
TypeCheckExp(&array_literal.size_expression(), impl_scope));
|
||||
RETURN_IF_ERROR(
|
||||
ExpectExactType(array_literal.size_expression().source_loc(),
|
||||
"array size", arena_->New<IntType>(),
|
||||
&array_literal.size_expression().static_type()));
|
||||
ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> size_value,
|
||||
InterpExp(&array_literal.size_expression(), arena_, trace_));
|
||||
if (cast<IntValue>(size_value)->value() < 0) {
|
||||
return FATAL_COMPILATION_ERROR(
|
||||
array_literal.size_expression().source_loc())
|
||||
<< "Array size cannot be negative";
|
||||
}
|
||||
array_literal.set_static_type(arena_->New<TypeType>());
|
||||
array_literal.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -377,6 +377,12 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
out << "typeof(" << cast<TypeOfChoiceType>(*this).choice_type().name()
|
||||
<< ")";
|
||||
break;
|
||||
case Value::Kind::StaticArrayType: {
|
||||
const auto& array_type = cast<StaticArrayType>(*this);
|
||||
out << "[" << array_type.element_type() << "; " << array_type.size()
|
||||
<< "]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,6 +500,12 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
return TypeEqual(&cast<TypeOfChoiceType>(*t1).choice_type(),
|
||||
&cast<TypeOfChoiceType>(*t2).choice_type());
|
||||
case Value::Kind::StaticArrayType: {
|
||||
const auto& array1 = cast<StaticArrayType>(*t1);
|
||||
const auto& array2 = cast<StaticArrayType>(*t2);
|
||||
return TypeEqual(&array1.element_type(), &array2.element_type()) &&
|
||||
array1.size() == array2.size();
|
||||
}
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
@@ -595,6 +607,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
|
||||
case Value::Kind::TypeOfClassType:
|
||||
case Value::Kind::TypeOfInterfaceType:
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
case Value::Kind::StaticArrayType:
|
||||
return TypeEqual(v1, v2);
|
||||
case Value::Kind::NominalClassValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
|
||||
@@ -65,6 +65,7 @@ class Value {
|
||||
TypeOfClassType,
|
||||
TypeOfInterfaceType,
|
||||
TypeOfChoiceType,
|
||||
StaticArrayType,
|
||||
};
|
||||
|
||||
Value(const Value&) = delete;
|
||||
@@ -800,6 +801,30 @@ class TypeOfChoiceType : public Value {
|
||||
Nonnull<const ChoiceType*> choice_type_;
|
||||
};
|
||||
|
||||
// The type of a statically-sized array.
|
||||
//
|
||||
// Note that values of this type are represented as tuples.
|
||||
class StaticArrayType : public Value {
|
||||
public:
|
||||
// Constructs a statically-sized array type with the given element type and
|
||||
// size.
|
||||
StaticArrayType(Nonnull<const Value*> element_type, size_t size)
|
||||
: Value(Kind::StaticArrayType),
|
||||
element_type_(element_type),
|
||||
size_(size) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::StaticArrayType;
|
||||
}
|
||||
|
||||
auto element_type() const -> const Value& { return *element_type_; }
|
||||
auto size() const -> size_t { return size_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Value*> element_type_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool;
|
||||
auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool;
|
||||
|
||||
|
||||
@@ -312,6 +312,8 @@ primary_expression:
|
||||
| paren_expression { $$ = $1; }
|
||||
| struct_literal { $$ = $1; }
|
||||
| struct_type_literal { $$ = $1; }
|
||||
| LEFT_SQUARE_BRACKET expression SEMICOLON expression RIGHT_SQUARE_BRACKET
|
||||
{ $$ = arena->New<ArrayTypeLiteral>(context.source_loc(), $2, $4); }
|
||||
;
|
||||
postfix_expression:
|
||||
primary_expression
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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: %{not} %{executable_semantics} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{executable_semantics} %s
|
||||
// CHECK: RUNTIME ERROR: index 2 out of range in (0, 1)
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: [i32; 2] = (0, 1);
|
||||
return x[2];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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: %{not} %{executable_semantics} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{executable_semantics} %s
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/array/fail_negative_size.carbon:15: Array size cannot be negative
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: [i32; -1] = ();
|
||||
return x[0];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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: %{not} %{executable_semantics} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{executable_semantics} %s
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/array/fail_size_mismatch.carbon:15: type error in name binding: '(i32, i32, i32)' is not implicitly convertible to '[i32; 2]'
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: [i32; 2] = (0, 1, 2);
|
||||
return x[0];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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: %{executable_semantics} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{executable_semantics} --trace %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{executable_semantics} %s
|
||||
// CHECK: result: 0
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: [i32; 2] = (0, 1);
|
||||
var index: i32 = 1;
|
||||
x[index] = 0;
|
||||
return x[0] + x[1];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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: %{executable_semantics} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{executable_semantics} --trace %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{executable_semantics} %s
|
||||
// CHECK: result: 0
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: [[i32; 3]; 2] = ((0, 1, 2), (3, 4, 5));
|
||||
return x[1][2] - 5;
|
||||
}
|
||||
Reference in New Issue
Block a user