// 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/action.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" namespace Carbon { using llvm::cast; auto StructValue::FindField(const std::string& name) const -> std::optional> { for (const NamedValue& element : elements_) { if (element.name == name) { return element.value; } } return std::nullopt; } namespace { auto GetMember(Nonnull arena, Nonnull v, const std::string& f, SourceLocation source_loc) -> Nonnull { switch (v->kind()) { case Value::Kind::StructValue: { std::optional> field = cast(*v).FindField(f); if (field == std::nullopt) { FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v; } return *field; } case Value::Kind::NominalClassValue: { std::optional> field = cast(cast(*v).inits()).FindField(f); if (field == std::nullopt) { FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v; } return *field; } case Value::Kind::ChoiceType: { const auto& choice = cast(*v); if (!choice.FindAlternative(f)) { FATAL_RUNTIME_ERROR(source_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 source_loc) const -> Nonnull { Nonnull value(this); for (const std::string& field : path.components_) { value = GetMember(arena, value, field, source_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 source_loc) -> Nonnull { if (path_begin == path_end) { return field_value; } switch (value->kind()) { case Value::Kind::StructValue: { std::vector elements = cast(*value).elements(); auto it = std::find_if(elements.begin(), elements.end(), [path_begin](const NamedValue& element) { return element.name == *path_begin; }); if (it == elements.end()) { FATAL_RUNTIME_ERROR(source_loc) << "field " << *path_begin << " not in " << *value; } it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end, field_value, source_loc); return arena->New(elements); } case Value::Kind::NominalClassValue: { return SetFieldImpl(arena, &cast(*value).inits(), path_begin, path_end, field_value, source_loc); } case Value::Kind::TupleValue: { std::vector> elements = cast(*value).elements(); // TODO(geoffromer): update FieldPath to hold integers as well as strings. int index = std::stoi(*path_begin); if (index < 0 || static_cast(index) >= elements.size()) { FATAL_RUNTIME_ERROR(source_loc) << "index " << *path_begin << " out of range in " << *value; } elements[index] = SetFieldImpl(arena, elements[index], path_begin + 1, path_end, field_value, source_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 source_loc) const -> Nonnull { return SetFieldImpl(arena, Nonnull(this), path.components_.begin(), path.components_.end(), field_value, source_loc); } void Value::Print(llvm::raw_ostream& out) const { switch (kind()) { case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*this); out << alt.choice_name() << "." << alt.alt_name(); break; } case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*this); out << "Placeholder<"; if (placeholder.named_entity().has_value()) { out << (*placeholder.named_entity()).name(); } else { out << "_"; } out << ">"; break; } case Value::Kind::AlternativeValue: { const auto& alt = cast(*this); out << "alt " << alt.choice_name() << "." << alt.alt_name() << " " << alt.argument(); break; } case Value::Kind::StructValue: { const auto& struct_val = cast(*this); out << "{"; llvm::ListSeparator sep; for (const NamedValue& element : struct_val.elements()) { out << sep << "." << element.name << " = " << *element.value; } out << "}"; break; } case Value::Kind::NominalClassValue: { const auto& s = cast(*this); out << cast(s.type()).name() << s.inits(); break; } case Value::Kind::TupleValue: { out << "("; llvm::ListSeparator sep; for (Nonnull element : cast(*this).elements()) { out << sep << *element; } out << ")"; break; } case Value::Kind::IntValue: out << cast(*this).value(); break; case Value::Kind::BoolValue: out << (cast(*this).value() ? "true" : "false"); break; case Value::Kind::FunctionValue: out << "fun<" << cast(*this).declaration().name() << ">"; break; case Value::Kind::LValue: out << "ptr<" << cast(*this).address() << ">"; 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 (Nonnull deduced : fn_type.deduced()) { if (i != 0) { out << ", "; } out << deduced->name() << ":! " << deduced->type(); ++i; } out << "]"; } out << fn_type.parameters() << " -> " << fn_type.return_type(); break; } case Value::Kind::StructType: { out << "{"; llvm::ListSeparator sep; for (const auto& [name, type] : cast(*this).fields()) { out << sep << "." << name << ": " << *type; } out << "}"; break; } case Value::Kind::NominalClassType: out << "class " << cast(*this).name(); break; case Value::Kind::ChoiceType: out << "choice " << cast(*this).name(); break; case Value::Kind::VariableType: out << cast(*this).binding().name(); break; case Value::Kind::ContinuationValue: { out << cast(*this).stack(); break; } case Value::Kind::StringType: out << "String"; break; case Value::Kind::StringValue: out << "\""; out.write_escaped(cast(*this).value()); out << "\""; break; case Value::Kind::TypeOfClassType: out << "typeof(" << cast(*this).class_type().name() << ")"; break; case Value::Kind::TypeOfChoiceType: out << "typeof(" << cast(*this).choice_type().name() << ")"; break; } } ContinuationValue::StackFragment::~StackFragment() { CHECK(reversed_todo_.empty()) << "All StackFragments must be empty before the Carbon program ends."; } void ContinuationValue::StackFragment::StoreReversed( std::vector> reversed_todo) { CHECK(reversed_todo_.empty()); reversed_todo_ = std::move(reversed_todo); } void ContinuationValue::StackFragment::RestoreTo( Stack>& todo) { while (!reversed_todo_.empty()) { todo.Push(std::move(reversed_todo_.back())); reversed_todo_.pop_back(); } } void ContinuationValue::StackFragment::Clear() { // We destroy the underlying Actions explicitly to ensure they're // destroyed in the correct order. for (auto& action : reversed_todo_) { action.reset(); } reversed_todo_.clear(); } void ContinuationValue::StackFragment::Print(llvm::raw_ostream& out) const { out << "{"; llvm::ListSeparator sep(" :: "); for (const std::unique_ptr& action : reversed_todo_) { out << sep << *action; } out << "}"; } auto TypeEqual(Nonnull t1, Nonnull t2) -> bool { if (t1->kind() != t2->kind()) { return false; } switch (t1->kind()) { 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.parameters(), &fn2.parameters()) && TypeEqual(&fn1.return_type(), &fn2.return_type()); } case Value::Kind::StructType: { const auto& struct1 = cast(*t1); const auto& struct2 = cast(*t2); if (struct1.fields().size() != struct2.fields().size()) { return false; } for (size_t i = 0; i < struct1.fields().size(); ++i) { if (struct1.fields()[i].name != struct2.fields()[i].name || !TypeEqual(struct1.fields()[i].value, struct2.fields()[i].value)) { return false; } } return true; } case Value::Kind::NominalClassType: 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 (!TypeEqual(tup1.elements()[i], tup2.elements()[i])) { 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).binding() == &cast(*t2).binding(); case Value::Kind::TypeOfClassType: return TypeEqual(&cast(*t1).class_type(), &cast(*t2).class_type()); case Value::Kind::TypeOfChoiceType: return TypeEqual(&cast(*t1).choice_type(), &cast(*t2).choice_type()); default: FATAL() << "TypeEqual used to compare non-type values\n" << *t1 << "\n" << *t2; } } // 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) -> bool { if (v1->kind() != v2->kind()) { return false; } switch (v1->kind()) { case Value::Kind::IntValue: return cast(*v1).value() == cast(*v2).value(); case Value::Kind::BoolValue: return cast(*v1).value() == cast(*v2).value(); case Value::Kind::FunctionValue: { std::optional> body1 = cast(*v1).declaration().body(); std::optional> body2 = cast(*v2).declaration().body(); return body1.has_value() == body2.has_value() && (!body1.has_value() || *body1 == *body2); } case Value::Kind::TupleValue: { const std::vector>& elements1 = cast(*v1).elements(); const std::vector>& elements2 = cast(*v2).elements(); if (elements1.size() != elements2.size()) { return false; } for (size_t i = 0; i < elements1.size(); ++i) { if (!ValueEqual(elements1[i], elements2[i])) { return false; } } return true; } case Value::Kind::StructValue: { const auto& struct_v1 = cast(*v1); const auto& struct_v2 = cast(*v2); CHECK(struct_v1.elements().size() == struct_v2.elements().size()); for (size_t i = 0; i < struct_v1.elements().size(); ++i) { CHECK(struct_v1.elements()[i].name == struct_v2.elements()[i].name); if (!ValueEqual(struct_v1.elements()[i].value, struct_v2.elements()[i].value)) { return false; } } return true; } case Value::Kind::StringValue: return cast(*v1).value() == cast(*v2).value(); 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::StructType: case Value::Kind::NominalClassType: case Value::Kind::ChoiceType: case Value::Kind::ContinuationType: case Value::Kind::VariableType: case Value::Kind::StringType: case Value::Kind::TypeOfClassType: case Value::Kind::TypeOfChoiceType: return TypeEqual(v1, v2); case Value::Kind::NominalClassValue: case Value::Kind::AlternativeValue: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: case Value::Kind::LValue: // TODO: support pointer comparisons once we have a clearer distinction // between pointers and lvalues. FATAL() << "ValueEqual does not support this kind of value: " << *v1; } } auto ChoiceType::FindAlternative(std::string_view name) const -> std::optional> { for (const NamedValue& alternative : alternatives_) { if (alternative.name == name) { return alternative.value; } } return std::nullopt; } } // namespace Carbon