mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Add support for struct types, following p0561. (#856)
This commit is contained in:
@@ -64,10 +64,11 @@ static void PrintOp(llvm::raw_ostream& out, Operator op) {
|
||||
}
|
||||
|
||||
static void PrintFields(llvm::raw_ostream& out,
|
||||
const std::vector<FieldInitializer>& fields) {
|
||||
const std::vector<FieldInitializer>& fields,
|
||||
std::string_view separator) {
|
||||
llvm::ListSeparator sep;
|
||||
for (const auto& field : fields) {
|
||||
out << sep << field.name << " = " << *field.expression;
|
||||
out << sep << "." << field.name << separator << *field.expression;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +86,19 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
}
|
||||
case Expression::Kind::TupleLiteral:
|
||||
out << "(";
|
||||
PrintFields(out, cast<TupleLiteral>(*this).Fields());
|
||||
PrintFields(out, cast<TupleLiteral>(*this).Fields(), " = ");
|
||||
out << ")";
|
||||
break;
|
||||
case Expression::Kind::StructLiteral:
|
||||
out << "{";
|
||||
PrintFields(out, cast<StructLiteral>(*this).fields(), " = ");
|
||||
out << "}";
|
||||
break;
|
||||
case Expression::Kind::StructTypeLiteral:
|
||||
out << "{";
|
||||
PrintFields(out, cast<StructTypeLiteral>(*this).fields(), ": ");
|
||||
out << "}";
|
||||
break;
|
||||
case Expression::Kind::IntLiteral:
|
||||
out << cast<IntLiteral>(*this).Val();
|
||||
break;
|
||||
|
||||
@@ -35,6 +35,8 @@ class Expression {
|
||||
StringLiteral,
|
||||
StringTypeLiteral,
|
||||
TupleLiteral,
|
||||
StructLiteral,
|
||||
StructTypeLiteral,
|
||||
TypeTypeLiteral,
|
||||
IdentifierExpression,
|
||||
IntrinsicExpression,
|
||||
@@ -230,6 +232,57 @@ class TupleLiteral : public Expression {
|
||||
std::vector<FieldInitializer> fields;
|
||||
};
|
||||
|
||||
// A non-empty literal value of a struct type.
|
||||
//
|
||||
// It can't be empty because the syntax `{}` is a struct type literal as well
|
||||
// as a literal value of that type, so for consistency we always represent it
|
||||
// as a StructTypeLiteral rather than let it oscillate unpredictably between
|
||||
// the two.
|
||||
class StructLiteral : public Expression {
|
||||
public:
|
||||
explicit StructLiteral(SourceLocation loc,
|
||||
std::vector<FieldInitializer> fields)
|
||||
: Expression(Kind::StructLiteral, loc), fields_(std::move(fields)) {
|
||||
CHECK(!fields_.empty())
|
||||
<< "`{}` is represented as a StructTypeLiteral, not a StructLiteral.";
|
||||
}
|
||||
|
||||
static auto classof(const Expression* exp) -> bool {
|
||||
return exp->Tag() == Kind::StructLiteral;
|
||||
}
|
||||
|
||||
auto fields() const -> const std::vector<FieldInitializer>& {
|
||||
return fields_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<FieldInitializer> fields_;
|
||||
};
|
||||
|
||||
// A literal representing a struct type.
|
||||
//
|
||||
// Code that handles this type may sometimes need to have special-case handling
|
||||
// for `{}`, which is a struct value in addition to being a struct type.
|
||||
class StructTypeLiteral : public Expression {
|
||||
public:
|
||||
explicit StructTypeLiteral(SourceLocation loc) : StructTypeLiteral(loc, {}) {}
|
||||
|
||||
explicit StructTypeLiteral(SourceLocation loc,
|
||||
std::vector<FieldInitializer> fields)
|
||||
: Expression(Kind::StructTypeLiteral, loc), fields_(std::move(fields)) {}
|
||||
|
||||
static auto classof(const Expression* exp) -> bool {
|
||||
return exp->Tag() == Kind::StructTypeLiteral;
|
||||
}
|
||||
|
||||
auto fields() const -> const std::vector<FieldInitializer>& {
|
||||
return fields_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<FieldInitializer> fields_;
|
||||
};
|
||||
|
||||
class PrimitiveOperatorExpression : public Expression {
|
||||
public:
|
||||
explicit PrimitiveOperatorExpression(
|
||||
|
||||
@@ -141,8 +141,8 @@ void Interpreter::InitEnv(const Declaration& d, Env* env) {
|
||||
}
|
||||
}
|
||||
}
|
||||
auto st = arena->New<ClassType>(class_def.name(), std::move(fields),
|
||||
std::move(methods));
|
||||
auto st = arena->New<NominalClassType>(
|
||||
class_def.name(), std::move(fields), std::move(methods));
|
||||
auto a = heap.AllocateValue(st);
|
||||
env->Set(class_def.name(), a);
|
||||
break;
|
||||
@@ -211,6 +211,18 @@ auto Interpreter::CreateTuple(Nonnull<Action*> act,
|
||||
return arena->New<TupleValue>(std::move(elements));
|
||||
}
|
||||
|
||||
auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
|
||||
const std::vector<Nonnull<const Value*>>& values)
|
||||
-> Nonnull<const Value*> {
|
||||
CHECK(fields.size() == values.size());
|
||||
std::vector<TupleElement> elements;
|
||||
for (size_t i = 0; i < fields.size(); ++i) {
|
||||
elements.push_back({.name = fields[i].name, .value = values[i]});
|
||||
}
|
||||
|
||||
return arena->New<StructValue>(std::move(elements));
|
||||
}
|
||||
|
||||
auto Interpreter::PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
SourceLocation loc) -> std::optional<Env> {
|
||||
switch (p->Tag()) {
|
||||
@@ -255,6 +267,24 @@ auto Interpreter::PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
default:
|
||||
FATAL() << "expected a tuple value in pattern, not " << *v;
|
||||
}
|
||||
case Value::Kind::StructValue: {
|
||||
const auto& p_struct = cast<StructValue>(*p);
|
||||
const auto& v_struct = cast<StructValue>(*v);
|
||||
CHECK(p_struct.elements().size() == v_struct.elements().size());
|
||||
Env values(arena);
|
||||
for (size_t i = 0; i < p_struct.elements().size(); ++i) {
|
||||
CHECK(p_struct.elements()[i].name == v_struct.elements()[i].name);
|
||||
std::optional<Env> matches = PatternMatch(
|
||||
p_struct.elements()[i].value, v_struct.elements()[i].value, loc);
|
||||
if (!matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
for (const auto& [name, value] : *matches) {
|
||||
values.Set(name, value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
case Value::Kind::AlternativeValue:
|
||||
switch (v->Tag()) {
|
||||
case Value::Kind::AlternativeValue: {
|
||||
@@ -426,6 +456,8 @@ auto Interpreter::StepLvalue() -> Transition {
|
||||
return Done{CreateTuple(act, exp)};
|
||||
}
|
||||
}
|
||||
case Expression::Kind::StructLiteral:
|
||||
case Expression::Kind::StructTypeLiteral:
|
||||
case Expression::Kind::IntLiteral:
|
||||
case Expression::Kind::BoolLiteral:
|
||||
case Expression::Kind::CallExpression:
|
||||
@@ -492,6 +524,29 @@ auto Interpreter::StepExp() -> Transition {
|
||||
return Done{CreateTuple(act, exp)};
|
||||
}
|
||||
}
|
||||
case Expression::Kind::StructLiteral: {
|
||||
const auto& literal = cast<StructLiteral>(*exp);
|
||||
if (act->Pos() < static_cast<int>(literal.fields().size())) {
|
||||
Nonnull<const Expression*> elt =
|
||||
literal.fields()[act->Pos()].expression;
|
||||
return Spawn{arena->New<ExpressionAction>(elt)};
|
||||
} else {
|
||||
return Done{CreateStruct(literal.fields(), act->Results())};
|
||||
}
|
||||
}
|
||||
case Expression::Kind::StructTypeLiteral: {
|
||||
const auto& struct_type = cast<StructTypeLiteral>(*exp);
|
||||
if (act->Pos() < static_cast<int>(struct_type.fields().size())) {
|
||||
return Spawn{arena->New<ExpressionAction>(
|
||||
struct_type.fields()[act->Pos()].expression)};
|
||||
} else {
|
||||
VarValues fields;
|
||||
for (size_t i = 0; i < struct_type.fields().size(); ++i) {
|
||||
fields.push_back({struct_type.fields()[i].name, act->Results()[i]});
|
||||
}
|
||||
return Done{arena->New<StructType>(std::move(fields))};
|
||||
}
|
||||
}
|
||||
case Expression::Kind::FieldAccessExpression: {
|
||||
const auto& access = cast<FieldAccessExpression>(*exp);
|
||||
if (act->Pos() == 0) {
|
||||
@@ -548,10 +603,10 @@ auto Interpreter::StepExp() -> Transition {
|
||||
// { { v2 :: v1([]) :: C, E, F} :: S, H}
|
||||
// -> { {C',E',F'} :: {C, E, F} :: S, H}
|
||||
switch (act->Results()[0]->Tag()) {
|
||||
case Value::Kind::ClassType: {
|
||||
case Value::Kind::NominalClassType: {
|
||||
Nonnull<const Value*> arg =
|
||||
CopyVal(arena, act->Results()[1], exp->SourceLoc());
|
||||
return Done{arena->New<StructValue>(act->Results()[0], arg)};
|
||||
return Done{arena->New<NominalClassValue>(act->Results()[0], arg)};
|
||||
}
|
||||
case Value::Kind::AlternativeConstructorValue: {
|
||||
const auto& alt =
|
||||
|
||||
@@ -138,6 +138,9 @@ class Interpreter {
|
||||
|
||||
auto CreateTuple(Nonnull<Action*> act, Nonnull<const Expression*> exp)
|
||||
-> Nonnull<const Value*>;
|
||||
auto CreateStruct(const std::vector<FieldInitializer>& fields,
|
||||
const std::vector<Nonnull<const Value*>>& values)
|
||||
-> Nonnull<const Value*>;
|
||||
|
||||
auto EvalPrim(Operator op, const std::vector<Nonnull<const Value*>>& args,
|
||||
SourceLocation loc) -> Nonnull<const Value*>;
|
||||
|
||||
@@ -84,8 +84,16 @@ auto TypeChecker::ReifyType(Nonnull<const Value*> t, SourceLocation loc)
|
||||
}
|
||||
return arena->New<TupleLiteral>(loc, args);
|
||||
}
|
||||
case Value::Kind::ClassType:
|
||||
return arena->New<IdentifierExpression>(loc, cast<ClassType>(*t).Name());
|
||||
case Value::Kind::StructType: {
|
||||
std::vector<FieldInitializer> args;
|
||||
for (const auto& [name, type] : cast<StructType>(*t).fields()) {
|
||||
args.push_back(FieldInitializer(name, ReifyType(type, loc)));
|
||||
}
|
||||
return arena->New<StructTypeLiteral>(loc, args);
|
||||
}
|
||||
case Value::Kind::NominalClassType:
|
||||
return arena->New<IdentifierExpression>(
|
||||
loc, cast<NominalClassType>(*t).Name());
|
||||
case Value::Kind::ChoiceType:
|
||||
return arena->New<IdentifierExpression>(loc, cast<ChoiceType>(*t).Name());
|
||||
case Value::Kind::PointerType:
|
||||
@@ -109,6 +117,7 @@ auto TypeChecker::ReifyType(Nonnull<const Value*> t, SourceLocation loc)
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::StringValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
FATAL() << "expected a type, not " << *t;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +162,27 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
|
||||
}
|
||||
return deduced;
|
||||
}
|
||||
case Value::Kind::StructType: {
|
||||
if (arg->Tag() != Value::Kind::StructType) {
|
||||
ExpectType(loc, "argument deduction", param, arg);
|
||||
}
|
||||
const auto& param_struct = cast<StructType>(*param);
|
||||
const auto& arg_struct = cast<StructType>(*arg);
|
||||
if (param_struct.fields().size() != arg_struct.fields().size()) {
|
||||
ExpectType(loc, "argument deduction", param, arg);
|
||||
}
|
||||
for (size_t i = 0; i < param_struct.fields().size(); ++i) {
|
||||
if (param_struct.fields()[i].first != arg_struct.fields()[i].first) {
|
||||
FATAL_COMPILATION_ERROR(loc)
|
||||
<< "mismatch in field names, " << param_struct.fields()[i].first
|
||||
<< " != " << arg_struct.fields()[i].first;
|
||||
}
|
||||
deduced =
|
||||
ArgumentDeduction(loc, deduced, param_struct.fields()[i].second,
|
||||
arg_struct.fields()[i].second);
|
||||
}
|
||||
return deduced;
|
||||
}
|
||||
case Value::Kind::FunctionType: {
|
||||
if (arg->Tag() != Value::Kind::FunctionType) {
|
||||
ExpectType(loc, "argument deduction", param, arg);
|
||||
@@ -178,7 +208,7 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
|
||||
}
|
||||
// For the following cases, we check for type equality.
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::ClassType:
|
||||
case Value::Kind::NominalClassType:
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::IntType:
|
||||
case Value::Kind::BoolType:
|
||||
@@ -192,6 +222,7 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
@@ -221,6 +252,14 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
|
||||
}
|
||||
return arena->New<TupleValue>(elts);
|
||||
}
|
||||
case Value::Kind::StructType: {
|
||||
VarValues fields;
|
||||
for (const auto& [name, value] : cast<StructType>(*type).fields()) {
|
||||
auto new_type = Substitute(dict, value);
|
||||
fields.push_back({name, new_type});
|
||||
}
|
||||
return arena->New<StructType>(std::move(fields));
|
||||
}
|
||||
case Value::Kind::FunctionType: {
|
||||
const auto& fn_type = cast<FunctionType>(*type);
|
||||
auto param = Substitute(dict, fn_type.Param());
|
||||
@@ -236,7 +275,7 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
|
||||
case Value::Kind::IntType:
|
||||
case Value::Kind::BoolType:
|
||||
case Value::Kind::TypeType:
|
||||
case Value::Kind::ClassType:
|
||||
case Value::Kind::NominalClassType:
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::StringType:
|
||||
@@ -247,6 +286,7 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
@@ -305,13 +345,64 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
|
||||
auto tuple_t = arena->New<TupleValue>(std::move(arg_types));
|
||||
return TCExpression(tuple_e, tuple_t, new_types);
|
||||
}
|
||||
case Expression::Kind::StructLiteral: {
|
||||
std::vector<FieldInitializer> new_args;
|
||||
VarValues arg_types;
|
||||
auto new_types = types;
|
||||
for (const auto& arg : cast<StructLiteral>(*e).fields()) {
|
||||
auto arg_res = TypeCheckExp(arg.expression, new_types, values);
|
||||
new_types = arg_res.types;
|
||||
new_args.push_back(FieldInitializer(arg.name, arg_res.exp));
|
||||
arg_types.push_back({arg.name, arg_res.type});
|
||||
}
|
||||
auto new_e = arena->New<StructLiteral>(e->SourceLoc(), new_args);
|
||||
auto type = arena->New<StructType>(std::move(arg_types));
|
||||
return TCExpression(new_e, type, new_types);
|
||||
}
|
||||
case Expression::Kind::StructTypeLiteral: {
|
||||
const auto& struct_type = cast<StructTypeLiteral>(*e);
|
||||
std::vector<FieldInitializer> new_args;
|
||||
auto new_types = types;
|
||||
for (const auto& arg : struct_type.fields()) {
|
||||
auto arg_res = TypeCheckExp(arg.expression, new_types, values);
|
||||
new_types = arg_res.types;
|
||||
Nonnull<const Value*> type = interpreter.InterpExp(values, arg_res.exp);
|
||||
new_args.push_back(
|
||||
FieldInitializer(arg.name, ReifyType(type, e->SourceLoc())));
|
||||
}
|
||||
auto new_e = arena->New<StructTypeLiteral>(e->SourceLoc(), new_args);
|
||||
Nonnull<const Value*> type;
|
||||
if (struct_type.fields().empty()) {
|
||||
// `{}` is the type of `{}`, just as `()` is the type of `()`.
|
||||
// This applies only if there are no fields, because (unlike with
|
||||
// tuples) non-empty struct types are syntactically disjoint
|
||||
// from non-empty struct values.
|
||||
type = arena->New<StructType>();
|
||||
} else {
|
||||
type = arena->New<TypeType>();
|
||||
}
|
||||
return TCExpression(new_e, type, new_types);
|
||||
}
|
||||
case Expression::Kind::FieldAccessExpression: {
|
||||
auto& access = cast<FieldAccessExpression>(*e);
|
||||
auto res = TypeCheckExp(access.Aggregate(), types, values);
|
||||
auto t = res.type;
|
||||
switch (t->Tag()) {
|
||||
case Value::Kind::ClassType: {
|
||||
const auto& t_class = cast<ClassType>(*t);
|
||||
case Value::Kind::StructType: {
|
||||
const auto& struct_type = cast<StructType>(*t);
|
||||
for (const auto& [field_name, field_type] : struct_type.fields()) {
|
||||
if (access.Field() == field_name) {
|
||||
Nonnull<Expression*> new_e = arena->New<FieldAccessExpression>(
|
||||
access.SourceLoc(), res.exp, access.Field());
|
||||
return TCExpression(new_e, field_type, res.types);
|
||||
}
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(access.SourceLoc())
|
||||
<< "struct " << struct_type << " does not have a field named "
|
||||
<< access.Field();
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& t_class = cast<NominalClassType>(*t);
|
||||
// Search for a field
|
||||
for (auto& field : t_class.Fields()) {
|
||||
if (access.Field() == field.first) {
|
||||
@@ -951,8 +1042,8 @@ auto TypeChecker::TypeOfClassDef(const ClassDefinition* sd, TypeEnv /*types*/,
|
||||
}
|
||||
}
|
||||
}
|
||||
return arena->New<ClassType>(sd->name(), std::move(fields),
|
||||
std::move(methods));
|
||||
return arena->New<NominalClassType>(sd->name(), std::move(fields),
|
||||
std::move(methods));
|
||||
}
|
||||
|
||||
static auto GetName(const Declaration& d) -> const std::string& {
|
||||
@@ -1042,7 +1133,7 @@ void TypeChecker::TopLevel(Nonnull<Declaration*> d, TypeCheckContext* tops) {
|
||||
tops->values.Set(class_def.name(), a); // Is this obsolete?
|
||||
std::vector<TupleElement> field_types;
|
||||
for (const auto& [field_name, field_value] :
|
||||
cast<ClassType>(*st).Fields()) {
|
||||
cast<NominalClassType>(*st).Fields()) {
|
||||
field_types.push_back({.name = field_name, .value = field_value});
|
||||
}
|
||||
auto fun_ty = arena->New<FunctionType>(
|
||||
|
||||
@@ -44,6 +44,16 @@ auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
auto StructValue::FindField(const std::string& name) const
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
for (const TupleElement& element : elements_) {
|
||||
if (element.name == name) {
|
||||
return element.value;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto TupleValue::FindField(const std::string& name) const
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
for (const TupleElement& element : elements) {
|
||||
@@ -62,7 +72,15 @@ auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
switch (v->Tag()) {
|
||||
case Value::Kind::StructValue: {
|
||||
std::optional<Nonnull<const Value*>> field =
|
||||
cast<TupleValue>(*cast<StructValue>(*v).Inits()).FindField(f);
|
||||
cast<StructValue>(*v).FindField(f);
|
||||
if (field == std::nullopt) {
|
||||
FATAL_RUNTIME_ERROR(loc) << "member " << f << " not in " << *v;
|
||||
}
|
||||
return *field;
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
std::optional<Nonnull<const Value*>> field =
|
||||
cast<TupleValue>(*cast<NominalClassValue>(*v).Inits()).FindField(f);
|
||||
if (field == std::nullopt) {
|
||||
FATAL_RUNTIME_ERROR(loc) << "member " << f << " not in " << *v;
|
||||
}
|
||||
@@ -111,8 +129,22 @@ auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
|
||||
}
|
||||
switch (value->Tag()) {
|
||||
case Value::Kind::StructValue: {
|
||||
return SetFieldImpl(arena, cast<StructValue>(*value).Inits(), path_begin,
|
||||
path_end, field_value, loc);
|
||||
std::vector<TupleElement> elements = cast<StructValue>(*value).elements();
|
||||
auto it = std::find_if(elements.begin(), elements.end(),
|
||||
[path_begin](const TupleElement& element) {
|
||||
return element.name == *path_begin;
|
||||
});
|
||||
if (it == elements.end()) {
|
||||
FATAL_RUNTIME_ERROR(loc)
|
||||
<< "field " << *path_begin << " not in " << *value;
|
||||
}
|
||||
it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end,
|
||||
field_value, loc);
|
||||
return arena->New<StructValue>(elements);
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
return SetFieldImpl(arena, cast<NominalClassValue>(*value).Inits(),
|
||||
path_begin, path_end, field_value, loc);
|
||||
}
|
||||
case Value::Kind::TupleValue: {
|
||||
std::vector<TupleElement> elements = cast<TupleValue>(*value).Elements();
|
||||
@@ -167,8 +199,18 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
break;
|
||||
}
|
||||
case Value::Kind::StructValue: {
|
||||
const auto& s = cast<StructValue>(*this);
|
||||
out << cast<ClassType>(*s.Type()).Name() << *s.Inits();
|
||||
const auto& struct_val = cast<StructValue>(*this);
|
||||
out << "{";
|
||||
llvm::ListSeparator sep;
|
||||
for (const TupleElement& element : struct_val.elements()) {
|
||||
out << sep << "." << element.name << " = " << *element.value;
|
||||
}
|
||||
out << "}";
|
||||
break;
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const auto& s = cast<NominalClassValue>(*this);
|
||||
out << cast<NominalClassType>(*s.Type()).Name() << *s.Inits();
|
||||
break;
|
||||
}
|
||||
case Value::Kind::TupleValue: {
|
||||
@@ -228,8 +270,17 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
out << *fn_type.Param() << " -> " << *fn_type.Ret();
|
||||
break;
|
||||
}
|
||||
case Value::Kind::ClassType:
|
||||
out << "struct " << cast<ClassType>(*this).Name();
|
||||
case Value::Kind::StructType: {
|
||||
out << "{";
|
||||
llvm::ListSeparator sep;
|
||||
for (const auto& [name, type] : cast<StructType>(*this).fields()) {
|
||||
out << sep << "." << name << ": " << *type;
|
||||
}
|
||||
out << "}";
|
||||
break;
|
||||
}
|
||||
case Value::Kind::NominalClassType:
|
||||
out << "class " << cast<NominalClassType>(*this).Name();
|
||||
break;
|
||||
case Value::Kind::ChoiceType:
|
||||
out << "choice " << cast<ChoiceType>(*this).Name();
|
||||
@@ -274,9 +325,17 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
|
||||
return arena->New<AlternativeValue>(alt.AltName(), alt.ChoiceName(), arg);
|
||||
}
|
||||
case Value::Kind::StructValue: {
|
||||
const auto& s = cast<StructValue>(*val);
|
||||
std::vector<TupleElement> elements;
|
||||
for (const TupleElement& element : cast<StructValue>(*val).elements()) {
|
||||
elements.push_back({.name = element.name,
|
||||
.value = CopyVal(arena, element.value, loc)});
|
||||
}
|
||||
return arena->New<StructValue>(std::move(elements));
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const auto& s = cast<NominalClassValue>(*val);
|
||||
Nonnull<const Value*> inits = CopyVal(arena, s.Inits(), loc);
|
||||
return arena->New<StructValue>(s.Type(), inits);
|
||||
return arena->New<NominalClassValue>(s.Type(), inits);
|
||||
}
|
||||
case Value::Kind::IntValue:
|
||||
return arena->New<IntValue>(cast<IntValue>(*val).Val());
|
||||
@@ -315,8 +374,15 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
|
||||
return arena->New<StringType>();
|
||||
case Value::Kind::StringValue:
|
||||
return arena->New<StringValue>(cast<StringValue>(*val).Val());
|
||||
case Value::Kind::StructType: {
|
||||
VarValues fields;
|
||||
for (const auto& [name, type] : cast<StructType>(*val).fields()) {
|
||||
fields.push_back({name, CopyVal(arena, type, loc)});
|
||||
}
|
||||
return arena->New<StructType>(fields);
|
||||
}
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::ClassType:
|
||||
case Value::Kind::NominalClassType:
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
@@ -339,8 +405,24 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
|
||||
return TypeEqual(fn1.Param(), fn2.Param()) &&
|
||||
TypeEqual(fn1.Ret(), fn2.Ret());
|
||||
}
|
||||
case Value::Kind::ClassType:
|
||||
return cast<ClassType>(*t1).Name() == cast<ClassType>(*t2).Name();
|
||||
case Value::Kind::StructType: {
|
||||
const auto& struct1 = cast<StructType>(*t1);
|
||||
const auto& struct2 = cast<StructType>(*t2);
|
||||
if (struct1.fields().size() != struct2.fields().size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < struct1.fields().size(); ++i) {
|
||||
if (struct1.fields()[i].first != struct2.fields()[i].first ||
|
||||
!TypeEqual(struct1.fields()[i].second,
|
||||
struct2.fields()[i].second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::NominalClassType:
|
||||
return cast<NominalClassType>(*t1).Name() ==
|
||||
cast<NominalClassType>(*t2).Name();
|
||||
case Value::Kind::ChoiceType:
|
||||
return cast<ChoiceType>(*t1).Name() == cast<ChoiceType>(*t2).Name();
|
||||
case Value::Kind::TupleValue: {
|
||||
@@ -420,6 +502,9 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
case Value::Kind::TupleValue:
|
||||
return FieldsValueEqual(cast<TupleValue>(*v1).Elements(),
|
||||
cast<TupleValue>(*v2).Elements(), loc);
|
||||
case Value::Kind::StructValue:
|
||||
return FieldsValueEqual(cast<StructValue>(*v1).elements(),
|
||||
cast<StructValue>(*v2).elements(), loc);
|
||||
case Value::Kind::StringValue:
|
||||
return cast<StringValue>(*v1).Val() == cast<StringValue>(*v2).Val();
|
||||
case Value::Kind::IntType:
|
||||
@@ -428,13 +513,14 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
case Value::Kind::FunctionType:
|
||||
case Value::Kind::PointerType:
|
||||
case Value::Kind::AutoType:
|
||||
case Value::Kind::ClassType:
|
||||
case Value::Kind::StructType:
|
||||
case Value::Kind::NominalClassType:
|
||||
case Value::Kind::ChoiceType:
|
||||
case Value::Kind::ContinuationType:
|
||||
case Value::Kind::VariableType:
|
||||
case Value::Kind::StringType:
|
||||
return TypeEqual(v1, v2);
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
case Value::Kind::AlternativeValue:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
|
||||
@@ -37,6 +37,7 @@ class Value {
|
||||
PointerValue,
|
||||
BoolValue,
|
||||
StructValue,
|
||||
NominalClassValue,
|
||||
AlternativeValue,
|
||||
TupleValue,
|
||||
IntType,
|
||||
@@ -45,7 +46,8 @@ class Value {
|
||||
FunctionType,
|
||||
PointerType,
|
||||
AutoType,
|
||||
ClassType,
|
||||
StructType,
|
||||
NominalClassType,
|
||||
ChoiceType,
|
||||
ContinuationType, // The type of a continuation.
|
||||
VariableType, // e.g., generic type parameters.
|
||||
@@ -92,7 +94,11 @@ auto FindInVarValues(const std::string& field, const VarValues& inits)
|
||||
-> std::optional<Nonnull<const Value*>>;
|
||||
auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool;
|
||||
|
||||
// A TupleElement represents the value of a single tuple field.
|
||||
// A TupleElement represents the value of a single tuple or struct field.
|
||||
//
|
||||
// TODO(geoffromer): Rename this, and look for ways to eliminate duplication
|
||||
// among TupleElement, VarValues::value_type, FieldInitializer,
|
||||
// TuplePattern::Field, and any similar types.
|
||||
struct TupleElement {
|
||||
// The field name.
|
||||
std::string name;
|
||||
@@ -173,16 +179,48 @@ class BoolValue : public Value {
|
||||
bool val;
|
||||
};
|
||||
|
||||
// A function value.
|
||||
// A non-empty value of a struct type.
|
||||
//
|
||||
// It can't be empty because `{}` is a struct type as well as a value of that
|
||||
// type, so for consistency we always represent it as a StructType rather than
|
||||
// let it oscillate unpredictably between the two. However, this means code
|
||||
// that handles StructValue instances may also need to be able to handle
|
||||
// StructType instances.
|
||||
class StructValue : public Value {
|
||||
public:
|
||||
StructValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
|
||||
: Value(Kind::StructValue), type(type), inits(inits) {}
|
||||
explicit StructValue(std::vector<TupleElement> elements)
|
||||
: Value(Kind::StructValue), elements_(std::move(elements)) {
|
||||
CHECK(!elements_.empty())
|
||||
<< "`{}` is represented as a StructType, not a StructValue.";
|
||||
}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::StructValue;
|
||||
}
|
||||
|
||||
auto elements() const -> const std::vector<TupleElement>& {
|
||||
return elements_;
|
||||
}
|
||||
|
||||
// Returns the value of the field named `name` in this struct, or
|
||||
// nullopt if there is no such field.
|
||||
auto FindField(const std::string& name) const
|
||||
-> std::optional<Nonnull<const Value*>>;
|
||||
|
||||
private:
|
||||
std::vector<TupleElement> elements_;
|
||||
};
|
||||
|
||||
// A value of a nominal class type.
|
||||
class NominalClassValue : public Value {
|
||||
public:
|
||||
NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
|
||||
: Value(Kind::NominalClassValue), type(type), inits(inits) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::NominalClassValue;
|
||||
}
|
||||
|
||||
auto Type() const -> Nonnull<const Value*> { return type; }
|
||||
auto Inits() const -> Nonnull<const Value*> { return inits; }
|
||||
|
||||
@@ -365,16 +403,37 @@ class AutoType : public Value {
|
||||
};
|
||||
|
||||
// A struct type.
|
||||
class ClassType : public Value {
|
||||
//
|
||||
// Code that handles this type may sometimes need to have special-case handling
|
||||
// for `{}`, which is a struct value in addition to being a struct type.
|
||||
class StructType : public Value {
|
||||
public:
|
||||
ClassType(std::string name, VarValues fields, VarValues methods)
|
||||
: Value(Kind::ClassType),
|
||||
StructType() : StructType(VarValues{}) {}
|
||||
|
||||
explicit StructType(VarValues fields)
|
||||
: Value(Kind::StructType), fields_(std::move(fields)) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::StructType;
|
||||
}
|
||||
|
||||
auto fields() const -> const VarValues& { return fields_; }
|
||||
|
||||
private:
|
||||
VarValues fields_;
|
||||
};
|
||||
|
||||
// A class type.
|
||||
class NominalClassType : public Value {
|
||||
public:
|
||||
NominalClassType(std::string name, VarValues fields, VarValues methods)
|
||||
: Value(Kind::NominalClassType),
|
||||
name(std::move(name)),
|
||||
fields(std::move(fields)),
|
||||
methods(std::move(methods)) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->Tag() == Kind::ClassType;
|
||||
return value->Tag() == Kind::NominalClassType;
|
||||
}
|
||||
|
||||
auto Name() const -> const std::string& { return name; }
|
||||
|
||||
@@ -106,7 +106,9 @@
|
||||
%type <Nonnull<Statement*>> if_statement
|
||||
%type <std::optional<Nonnull<Statement*>>> optional_else
|
||||
%type <std::pair<Nonnull<Expression*>, bool>> return_expression
|
||||
%type <Nonnull<Statement*>> nonempty_block
|
||||
%type <Nonnull<Statement*>> block
|
||||
%type <Nonnull<Statement*>> nonempty_statement_list
|
||||
%type <std::optional<Nonnull<Statement*>>> statement_list
|
||||
%type <Nonnull<Expression*>> expression
|
||||
%type <GenericBinding> generic_binding
|
||||
@@ -116,6 +118,10 @@
|
||||
%type <Nonnull<Pattern*>> non_expression_pattern
|
||||
%type <std::pair<Nonnull<Expression*>, bool>> return_type
|
||||
%type <Nonnull<Expression*>> paren_expression
|
||||
%type <Nonnull<Expression*>> struct_literal
|
||||
%type <std::vector<FieldInitializer>> struct_literal_contents
|
||||
%type <Nonnull<Expression*>> struct_type_literal
|
||||
%type <std::vector<FieldInitializer>> struct_type_literal_contents
|
||||
%type <Nonnull<Expression*>> tuple
|
||||
%type <std::optional<std::string>> binding_lhs
|
||||
%type <Nonnull<BindingPattern*>> variable_declaration
|
||||
@@ -299,6 +305,8 @@ expression:
|
||||
| CONTINUATION_TYPE
|
||||
{ $$ = arena->New<ContinuationTypeLiteral>(context.SourceLoc()); }
|
||||
| paren_expression { $$ = $1; }
|
||||
| struct_literal { $$ = $1; }
|
||||
| struct_type_literal { $$ = $1; }
|
||||
| expression EQUAL_EQUAL expression
|
||||
{
|
||||
$$ = arena->New<PrimitiveOperatorExpression>(
|
||||
@@ -415,6 +423,40 @@ paren_expression_contents:
|
||||
}
|
||||
;
|
||||
|
||||
struct_literal:
|
||||
LEFT_CURLY_BRACE struct_literal_contents RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<StructLiteral>(context.SourceLoc(), $2); }
|
||||
| LEFT_CURLY_BRACE struct_literal_contents COMMA RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<StructLiteral>(context.SourceLoc(), $2); }
|
||||
;
|
||||
struct_literal_contents:
|
||||
designator EQUAL expression
|
||||
{ $$ = {FieldInitializer($1, $3)}; }
|
||||
| struct_literal_contents COMMA designator EQUAL expression
|
||||
{
|
||||
$$ = $1;
|
||||
$$.push_back(FieldInitializer($3, $5));
|
||||
}
|
||||
;
|
||||
|
||||
struct_type_literal:
|
||||
LEFT_CURLY_BRACE RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<StructTypeLiteral>(context.SourceLoc()); }
|
||||
| LEFT_CURLY_BRACE struct_type_literal_contents RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<StructTypeLiteral>(context.SourceLoc(), $2); }
|
||||
| LEFT_CURLY_BRACE struct_type_literal_contents COMMA RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<StructTypeLiteral>(context.SourceLoc(), $2); }
|
||||
;
|
||||
struct_type_literal_contents:
|
||||
designator COLON expression
|
||||
{ $$ = {FieldInitializer($1, $3)}; }
|
||||
| struct_type_literal_contents COMMA designator COLON expression
|
||||
{
|
||||
$$ = $1;
|
||||
$$.push_back(FieldInitializer($3, $5));
|
||||
}
|
||||
;
|
||||
|
||||
// In many cases, using `pattern` recursively will result in ambiguities.
|
||||
// When that happens, it's necessary to factor out two separate productions,
|
||||
// one for when the sub-pattern is an expression, and one for when it is not.
|
||||
@@ -499,6 +541,7 @@ maybe_empty_tuple_pattern:
|
||||
| tuple_pattern
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
clause:
|
||||
CASE pattern DOUBLE_ARROW statement
|
||||
{ $$ = Match::Clause($2, $4); }
|
||||
@@ -539,7 +582,14 @@ statement:
|
||||
auto [return_exp, is_omitted_exp] = $2;
|
||||
$$ = arena->New<Return>(context.SourceLoc(), return_exp, is_omitted_exp);
|
||||
}
|
||||
| block
|
||||
// We disallow empty blocks in places where an arbitrary statement can occur
|
||||
// in order to avoid ambiguity with the empty struct literal `{}`. We can
|
||||
// allow non-empty blocks because a non-empty struct literal always starts with
|
||||
// a designator, and a block never does, so one token of lookahead suffices
|
||||
// to disambiguate. As of this writing, the "official" resolution of this
|
||||
// ambiguity is an open question (see
|
||||
// https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#literals)
|
||||
| nonempty_block
|
||||
{ $$ = $1; }
|
||||
| MATCH LEFT_PARENTHESIS expression RIGHT_PARENTHESIS LEFT_CURLY_BRACE
|
||||
clause_list RIGHT_CURLY_BRACE
|
||||
@@ -572,13 +622,20 @@ return_expression:
|
||||
statement_list:
|
||||
// Empty
|
||||
{ $$ = std::nullopt; }
|
||||
| statement statement_list
|
||||
| nonempty_statement_list
|
||||
{ $$ = $1; }
|
||||
;
|
||||
nonempty_statement_list:
|
||||
statement statement_list
|
||||
{ $$ = arena->New<Sequence>(context.SourceLoc(), $1, $2); }
|
||||
;
|
||||
block:
|
||||
LEFT_CURLY_BRACE statement_list RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<Block>(context.SourceLoc(), $2); }
|
||||
;
|
||||
nonempty_block:
|
||||
LEFT_CURLY_BRACE nonempty_statement_list RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<Block>(context.SourceLoc(), $2); }
|
||||
return_type:
|
||||
// Empty
|
||||
{ $$ = {arena->New<TupleLiteral>(context.SourceLoc()), true}; }
|
||||
|
||||
+5
-3
@@ -11,10 +11,12 @@
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn DoNothing() {
|
||||
// Empty block
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
var x: i32 = 0;
|
||||
{
|
||||
// empty block
|
||||
}
|
||||
DoNothing();
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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: auto = {.x = 0, .y = 1};
|
||||
x = {.x = 5, .y = -5};
|
||||
return x.x + x.y;
|
||||
}
|
||||
@@ -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 p1: auto = {.x = 1, .y = 2};
|
||||
var p2: auto = p1;
|
||||
p2.x = 3;
|
||||
return p1.x - 1;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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 empty: {} = {};
|
||||
empty = {};
|
||||
if (not (empty == {})) {
|
||||
return 1;
|
||||
}
|
||||
match (empty) {
|
||||
case {} => {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 t1: {.x: i32,} = {.x = 5,};
|
||||
var t2: {.x: i32, .y: i32} = {.x = 2, .y = 3,};
|
||||
return t1.x - t2.x - t2.y;
|
||||
}
|
||||
@@ -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: 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 t1: {.x: i32, .y: i32} = {.x = 5, .y = 2};
|
||||
var t2: {.x: i32, .y: i32} = {.x = 5, .y = 2};
|
||||
if (t1 == t2) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -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: 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 t1: {.x: i32, .y: i32} = {.x = 5, .y = 2};
|
||||
var t2: {.x: i32, .y: i32} = {.x = 5, .y = 4};
|
||||
if (t1 == t2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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 2>&1 | FileCheck %s
|
||||
// AUTOUPDATE: executable_semantics %s
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/struct/fail_equality_type.carbon:16: type error in ==
|
||||
// CHECK: expected: {.x: i32, .y: i32}
|
||||
// CHECK: actual: {.x: i32}
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn main() -> i32 {
|
||||
var t1: {.x: i32, .y: i32} = {.x = 5, .y = 2};
|
||||
var t2: {.x: i32,} = {.x = 5,};
|
||||
if (t1 == t2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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/struct/fail_field_access_mismatch.carbon:15: struct {.x: i32, .y: i32} does not have a field named z
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
fn main() -> i32 {
|
||||
return {.x = 1, .y = 2}.z - 1;
|
||||
}
|
||||
@@ -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: 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/struct/fail_name_order.carbon:17: Type pattern '{.x: i32, .y: i32}' does not match actual type '{.y: i32, .x: i32}'
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
// Test the that field order matters for structs.
|
||||
|
||||
fn main() -> i32 {
|
||||
var t: {.x: i32, .y: i32} = {.y = 2, .x = 3};
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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 {
|
||||
return {.x = 1, .y = 2}.x - 1;
|
||||
}
|
||||
+17
@@ -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 p: {.x: i32, .y: i32} = {.x = 1, .y = 2};
|
||||
return p.y - p.x - 1;
|
||||
}
|
||||
Reference in New Issue
Block a user