Initial support for statically-sized arrays (#1158)

This commit is contained in:
Geoff Romer
2022-03-31 10:55:34 -07:00
committed by GitHub
parent 0b8a96af6d
commit 8f9638d759
16 changed files with 302 additions and 22 deletions
@@ -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:
+25
View File
@@ -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;