mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:14:42 +01:00
Initial support for statically-sized arrays (#1158)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user