// 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 #include "executable_semantics/interpreter/value.h" #include #include "common/check.h" #include "executable_semantics/common/arena.h" #include "executable_semantics/common/error.h" #include "executable_semantics/interpreter/frame.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" namespace Carbon { using llvm::cast; auto FindInVarValues(const std::string& field, const VarValues& inits) -> std::optional> { for (auto& i : inits) { if (i.first == field) { return i.second; } } return std::nullopt; } auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool { if (ts1.size() == ts2.size()) { for (auto& iter1 : ts1) { auto t2 = FindInVarValues(iter1.first, ts2); if (!t2) { return false; } if (!TypeEqual(iter1.second, *t2)) { return false; } } return true; } else { return false; } } auto TupleValue::FindField(const std::string& name) const -> std::optional> { for (const TupleElement& element : elements) { if (element.name == name) { return element.value; } } return std::nullopt; } namespace { auto GetMember(Nonnull arena, Nonnull v, const std::string& f, SourceLocation loc) -> Nonnull { switch (v->Tag()) { case Value::Kind::StructValue: { std::optional> field = cast(*cast(*v).Inits()).FindField(f); if (field == std::nullopt) { FATAL_RUNTIME_ERROR(loc) << "member " << f << " not in " << *v; } return *field; } case Value::Kind::TupleValue: { std::optional> field = cast(*v).FindField(f); if (!field) { FATAL_RUNTIME_ERROR(loc) << "field " << f << " not in " << *v; } return *field; } case Value::Kind::ChoiceType: { const auto& choice = cast(*v); if (!FindInVarValues(f, choice.Alternatives())) { FATAL_RUNTIME_ERROR(loc) << "alternative " << f << " not in " << *v; } return arena->New(f, choice.Name()); } default: FATAL() << "field access not allowed for value " << *v; } } } // namespace auto Value::GetField(Nonnull arena, const FieldPath& path, SourceLocation loc) const -> Nonnull { Nonnull value(this); for (const std::string& field : path.components) { value = GetMember(arena, value, field, loc); } return value; } namespace { auto SetFieldImpl(Nonnull arena, Nonnull value, std::vector::const_iterator path_begin, std::vector::const_iterator path_end, Nonnull field_value, SourceLocation loc) -> Nonnull { if (path_begin == path_end) { return field_value; } switch (value->Tag()) { case Value::Kind::StructValue: { return SetFieldImpl(arena, cast(*value).Inits(), path_begin, path_end, field_value, loc); } case Value::Kind::TupleValue: { std::vector elements = cast(*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(elements); } default: FATAL() << "field access not allowed for value " << *value; } } } // namespace auto Value::SetField(Nonnull arena, const FieldPath& path, Nonnull field_value, SourceLocation loc) const -> Nonnull { return SetFieldImpl(arena, Nonnull(this), path.components.begin(), path.components.end(), field_value, loc); } void Value::Print(llvm::raw_ostream& out) const { switch (Tag()) { case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*this); out << alt.ChoiceName() << "." << alt.AltName(); break; } case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*this); if (placeholder.Name().has_value()) { out << *placeholder.Name(); } else { out << "_"; } out << ": " << *placeholder.Type(); break; } case Value::Kind::AlternativeValue: { const auto& alt = cast(*this); out << "alt " << alt.ChoiceName() << "." << alt.AltName() << " " << *alt.Argument(); break; } case Value::Kind::StructValue: { const auto& s = cast(*this); out << cast(*s.Type()).Name() << *s.Inits(); break; } case Value::Kind::TupleValue: { out << "("; llvm::ListSeparator sep; for (const TupleElement& element : cast(*this).Elements()) { out << sep << element.name << " = " << *element.value; } out << ")"; break; } case Value::Kind::IntValue: out << cast(*this).Val(); break; case Value::Kind::BoolValue: out << (cast(*this).Val() ? "true" : "false"); break; case Value::Kind::FunctionValue: out << "fun<" << cast(*this).Name() << ">"; break; case Value::Kind::PointerValue: out << "ptr<" << cast(*this).Val() << ">"; break; case Value::Kind::BoolType: out << "Bool"; break; case Value::Kind::IntType: out << "i32"; break; case Value::Kind::TypeType: out << "Type"; break; case Value::Kind::AutoType: out << "auto"; break; case Value::Kind::ContinuationType: out << "Continuation"; break; case Value::Kind::PointerType: out << *cast(*this).Type() << "*"; break; case Value::Kind::FunctionType: { const auto& fn_type = cast(*this); out << "fn "; if (fn_type.Deduced().size() > 0) { out << "["; unsigned int i = 0; for (const auto& deduced : fn_type.Deduced()) { if (i != 0) { out << ", "; } out << deduced.name << ":! " << *deduced.type; ++i; } out << "]"; } out << *fn_type.Param() << " -> " << *fn_type.Ret(); break; } case Value::Kind::ClassType: out << "struct " << cast(*this).Name(); break; case Value::Kind::ChoiceType: out << "choice " << cast(*this).Name(); break; case Value::Kind::VariableType: out << cast(*this).Name(); break; case Value::Kind::ContinuationValue: { out << "{"; llvm::ListSeparator sep(" :: "); for (Nonnull frame : cast(*this).Stack()) { out << sep << *frame; } out << "}"; break; } case Value::Kind::StringType: out << "String"; break; case Value::Kind::StringValue: out << "\""; out.write_escaped(cast(*this).Val()); out << "\""; break; } } auto CopyVal(Nonnull arena, Nonnull val, SourceLocation loc) -> Nonnull { switch (val->Tag()) { case Value::Kind::TupleValue: { std::vector elements; for (const TupleElement& element : cast(*val).Elements()) { elements.push_back({.name = element.name, .value = CopyVal(arena, element.value, loc)}); } return arena->New(std::move(elements)); } case Value::Kind::AlternativeValue: { const auto& alt = cast(*val); Nonnull arg = CopyVal(arena, alt.Argument(), loc); return arena->New(alt.AltName(), alt.ChoiceName(), arg); } case Value::Kind::StructValue: { const auto& s = cast(*val); Nonnull inits = CopyVal(arena, s.Inits(), loc); return arena->New(s.Type(), inits); } case Value::Kind::IntValue: return arena->New(cast(*val).Val()); case Value::Kind::BoolValue: return arena->New(cast(*val).Val()); case Value::Kind::FunctionValue: { const auto& fn_value = cast(*val); return arena->New(fn_value.Name(), fn_value.Param(), fn_value.Body()); } case Value::Kind::PointerValue: return arena->New(cast(*val).Val()); case Value::Kind::ContinuationValue: // Copying a continuation is "shallow". return val; case Value::Kind::FunctionType: { const auto& fn_type = cast(*val); return arena->New(fn_type.Deduced(), CopyVal(arena, fn_type.Param(), loc), CopyVal(arena, fn_type.Ret(), loc)); } case Value::Kind::PointerType: return arena->New( CopyVal(arena, cast(*val).Type(), loc)); case Value::Kind::IntType: return arena->New(); case Value::Kind::BoolType: return arena->New(); case Value::Kind::TypeType: return arena->New(); case Value::Kind::AutoType: return arena->New(); case Value::Kind::ContinuationType: return arena->New(); case Value::Kind::StringType: return arena->New(); case Value::Kind::StringValue: return arena->New(cast(*val).Val()); case Value::Kind::VariableType: case Value::Kind::ClassType: case Value::Kind::ChoiceType: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AlternativeConstructorValue: // TODO: These should be copied so that they don't get destructed. return val; } } auto TypeEqual(Nonnull t1, Nonnull t2) -> bool { if (t1->Tag() != t2->Tag()) { return false; } switch (t1->Tag()) { case Value::Kind::PointerType: return TypeEqual(cast(*t1).Type(), cast(*t2).Type()); case Value::Kind::FunctionType: { const auto& fn1 = cast(*t1); const auto& fn2 = cast(*t2); return TypeEqual(fn1.Param(), fn2.Param()) && TypeEqual(fn1.Ret(), fn2.Ret()); } case Value::Kind::ClassType: return cast(*t1).Name() == cast(*t2).Name(); case Value::Kind::ChoiceType: return cast(*t1).Name() == cast(*t2).Name(); case Value::Kind::TupleValue: { const auto& tup1 = cast(*t1); const auto& tup2 = cast(*t2); if (tup1.Elements().size() != tup2.Elements().size()) { return false; } for (size_t i = 0; i < tup1.Elements().size(); ++i) { if (tup1.Elements()[i].name != tup2.Elements()[i].name || !TypeEqual(tup1.Elements()[i].value, tup2.Elements()[i].value)) { return false; } } return true; } case Value::Kind::IntType: case Value::Kind::BoolType: case Value::Kind::ContinuationType: case Value::Kind::TypeType: case Value::Kind::StringType: return true; case Value::Kind::VariableType: return cast(*t1).Name() == cast(*t2).Name(); default: FATAL() << "TypeEqual used to compare non-type values\n" << *t1 << "\n" << *t2; } } // Returns true if all the fields of the two tuples contain equal values // and returns false otherwise. static auto FieldsValueEqual(const std::vector& ts1, const std::vector& ts2, SourceLocation loc) -> bool { if (ts1.size() != ts2.size()) { return false; } for (const TupleElement& element : ts1) { auto iter = std::find_if( ts2.begin(), ts2.end(), [&](const TupleElement& e2) { return e2.name == element.name; }); if (iter == ts2.end()) { return false; } if (!ValueEqual(element.value, iter->value, loc)) { return false; } } return true; } // Returns true if the two values are equal and returns false otherwise. // // This function implements the `==` operator of Carbon. auto ValueEqual(Nonnull v1, Nonnull v2, SourceLocation loc) -> bool { if (v1->Tag() != v2->Tag()) { return false; } switch (v1->Tag()) { case Value::Kind::IntValue: return cast(*v1).Val() == cast(*v2).Val(); case Value::Kind::BoolValue: return cast(*v1).Val() == cast(*v2).Val(); case Value::Kind::PointerValue: return cast(*v1).Val() == cast(*v2).Val(); case Value::Kind::FunctionValue: { std::optional> body1 = cast(*v1).Body(); std::optional> body2 = cast(*v2).Body(); return body1.has_value() == body2.has_value() && (!body1.has_value() || *body1 == *body2); } case Value::Kind::TupleValue: return FieldsValueEqual(cast(*v1).Elements(), cast(*v2).Elements(), loc); case Value::Kind::StringValue: return cast(*v1).Val() == cast(*v2).Val(); case Value::Kind::IntType: case Value::Kind::BoolType: case Value::Kind::TypeType: case Value::Kind::FunctionType: case Value::Kind::PointerType: case Value::Kind::AutoType: case Value::Kind::ClassType: 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::AlternativeValue: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: FATAL() << "ValueEqual does not support this kind of value: " << *v1; } } } // namespace Carbon