// 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 StructValue::FindField(const std::string& name) const -> std::optional> { for (const StructElement& 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 (!FindInVarValues(f, choice.alternatives())) { 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 StructElement& 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); 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.choice_name() << "." << alt.alt_name() << " " << alt.argument(); break; } case Value::Kind::StructValue: { const auto& struct_val = cast(*this); out << "{"; llvm::ListSeparator sep; for (const StructElement& 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::PointerValue: out << "ptr<" << cast(*this).value() << ">"; 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.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).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).value()); out << "\""; break; } } 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].first != struct2.fields()[i].first || !TypeEqual(struct1.fields()[i].second, struct2.fields()[i].second)) { 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).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 source_loc) -> bool { if (ts1.size() != ts2.size()) { return false; } for (const StructElement& element : ts1) { auto iter = std::find_if( ts2.begin(), ts2.end(), [&](const StructElement& e2) { return e2.name == element.name; }); if (iter == ts2.end()) { return false; } if (!ValueEqual(element.value, iter->value, source_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 source_loc) -> 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::PointerValue: 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], source_loc)) { return false; } } return true; } case Value::Kind::StructValue: return FieldsValueEqual(cast(*v1).elements(), cast(*v2).elements(), source_loc); 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: return TypeEqual(v1, v2); case Value::Kind::NominalClassValue: 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