// 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 "explorer/interpreter/value.h" #include #include #include #include "common/check.h" #include "common/error.h" #include "explorer/ast/declaration.h" #include "explorer/ast/element.h" #include "explorer/common/arena.h" #include "explorer/common/error_builders.h" #include "explorer/interpreter/action.h" #include "explorer/interpreter/element_path.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" namespace Carbon { using llvm::cast; using llvm::dyn_cast; using llvm::dyn_cast_or_null; using llvm::isa; auto StructValue::FindField(std::string_view name) const -> std::optional> { for (const NamedValue& element : elements_) { if (element.name == name) { return element.value; } } return std::nullopt; } NominalClassValue::NominalClassValue( Nonnull type, Nonnull inits, std::optional> base, Nonnull class_value_ptr) : Value(Kind::NominalClassValue), type_(type), inits_(inits), base_(base), class_value_ptr_(class_value_ptr) { // Update ancestors's class value to point to latest child. *class_value_ptr_ = this; } static auto FindClassField(Nonnull object, std::string_view name) -> std::optional> { if (auto field = cast(object->inits()).FindField(name)) { return field; } if (object->base().has_value()) { return FindClassField(object->base().value(), name); } return std::nullopt; } static auto GetBaseElement(Nonnull class_value, SourceLocation source_loc) -> ErrorOr> { const auto base = cast(class_value)->base(); if (!base.has_value()) { return ProgramError(source_loc) << "Non-existent base class for " << *class_value; } return base.value(); } static auto GetPositionalElement(Nonnull tuple, const ElementPath::Component& path_comp, SourceLocation source_loc) -> ErrorOr> { CARBON_CHECK(path_comp.element()->kind() == ElementKind::PositionalElement) << "Invalid non-tuple member"; const auto* tuple_element = cast(path_comp.element()); const size_t index = tuple_element->index(); if (index < 0 || index >= tuple->elements().size()) { return ProgramError(source_loc) << "index " << index << " out of range for " << *tuple; } return tuple->elements()[index]; } static auto GetNamedElement(Nonnull arena, Nonnull v, const ElementPath::Component& field, SourceLocation source_loc, Nonnull me_value) -> ErrorOr> { CARBON_CHECK(field.element()->kind() == ElementKind::NamedElement) << "Invalid element, expecting NamedElement"; const auto* member = cast(field.element()); const auto f = member->name(); if (field.witness().has_value()) { const auto* witness = cast(*field.witness()); // Associated constants. if (const auto* assoc_const = dyn_cast_or_null( member->declaration().value_or(nullptr))) { CARBON_CHECK(field.interface()) << "have witness but no interface"; // TODO: Use witness to find the value of the constant. return arena->New(v, *field.interface(), assoc_const, witness); } // Associated functions. if (const auto* impl_witness = dyn_cast(witness)) { if (std::optional> mem_decl = FindMember(f, impl_witness->declaration().members()); mem_decl.has_value()) { const auto& fun_decl = cast(**mem_decl); if (fun_decl.is_method()) { return arena->New(&fun_decl, me_value, &impl_witness->bindings()); } else { // Class function. const auto* fun = cast(*fun_decl.constant_value()); return arena->New(&fun->declaration(), &impl_witness->bindings()); } } else { return ProgramError(source_loc) << "member " << f << " not in " << *witness; } } else { return ProgramError(source_loc) << "member lookup for " << f << " in symbolic " << *witness; } } switch (v->kind()) { case Value::Kind::StructValue: { std::optional> field = cast(*v).FindField(f); if (field == std::nullopt) { return ProgramError(source_loc) << "member " << f << " not in " << *v; } return *field; } case Value::Kind::NominalClassValue: { const auto& object = cast(*v); // Look for a field. if (std::optional> field = FindClassField(&object, f)) { return *field; } else { // Look for a method in the object's class const auto& class_type = cast(object.type()); std::optional> func = FindFunctionWithParents(f, class_type.declaration()); if (!func) { return ProgramError(source_loc) << "member " << f << " not in " << *v << " or its " << class_type; } else if ((*func)->declaration().is_method()) { // Found a method. Turn it into a bound method. const auto& m = cast(**func); if (m.declaration().virt_override() == VirtualOverride::None) { return arena->New(&m.declaration(), me_value, &class_type.bindings()); } // Method is virtual, get child-most class value and perform vtable // lookup. const auto& last_child_value = **object.class_value_ptr(); const auto& last_child_type = cast(last_child_value.type()); const auto res = last_child_type.vtable().find(f); CARBON_CHECK(res != last_child_type.vtable().end()); const auto [virtual_method, level] = res->second; const auto level_diff = last_child_type.hierarchy_level() - level; const auto* m_class_value = &last_child_value; // Get class value matching the virtual method, and turn it into a // bound method. for (int i = 0; i < level_diff; ++i) { CARBON_CHECK(m_class_value->base()) << "Error trying to access function class value"; m_class_value = *m_class_value->base(); } return arena->New( cast(virtual_method), m_class_value, &class_type.bindings()); } else { // Found a class function // TODO: This should not be reachable. return arena->New(&(*func)->declaration(), &class_type.bindings()); } } } case Value::Kind::ChoiceType: { const auto& choice = cast(*v); auto alt = choice.declaration().FindAlternative(f); if (!alt) { return ProgramError(source_loc) << "alternative " << f << " not in " << *v; } if ((*alt)->parameters()) { return arena->New(&choice, *alt); } return arena->New(&choice, *alt, std::nullopt); } case Value::Kind::NominalClassType: { // Access a class function. const auto& class_type = cast(*v); std::optional> fun = FindFunctionWithParents(f, class_type.declaration()); if (fun == std::nullopt) { return ProgramError(source_loc) << "class function " << f << " not in " << *v; } return arena->New(&(*fun)->declaration(), &class_type.bindings()); } default: CARBON_FATAL() << "named element access not supported for value " << *v; } } static auto GetElement(Nonnull arena, Nonnull v, const ElementPath::Component& path_comp, SourceLocation source_loc, Nonnull me_value) -> ErrorOr> { switch (path_comp.element()->kind()) { case ElementKind::NamedElement: return GetNamedElement(arena, v, path_comp, source_loc, me_value); case ElementKind::PositionalElement: { if (const auto* tuple = dyn_cast(v)) { return GetPositionalElement(tuple, path_comp, source_loc); } else { CARBON_FATAL() << "Invalid value for positional element"; } } case ElementKind::BaseElement: switch (v->kind()) { case Value::Kind::NominalClassValue: return GetBaseElement(cast(v), source_loc); case Value::Kind::PointerValue: { const auto* ptr = cast(v); return arena->New( ptr->address().ElementAddress(path_comp.element())); } default: CARBON_FATAL() << "Invalid value for base element"; } } } auto Value::GetElement(Nonnull arena, const ElementPath& path, SourceLocation source_loc, Nonnull me_value) const -> ErrorOr> { Nonnull value(this); for (const ElementPath::Component& field : path.components_) { CARBON_ASSIGN_OR_RETURN( value, Carbon::GetElement(arena, value, field, source_loc, me_value)); } return value; } static auto SetFieldImpl( Nonnull arena, Nonnull value, std::vector::const_iterator path_begin, std::vector::const_iterator path_end, Nonnull field_value, SourceLocation source_loc) -> ErrorOr> { if (path_begin == path_end) { return field_value; } switch (value->kind()) { case Value::Kind::StructValue: { std::vector elements = cast(*value).elements(); auto it = llvm::find_if(elements, [path_begin](const NamedValue& element) { return (*path_begin).IsNamed(element.name); }); if (it == elements.end()) { return ProgramError(source_loc) << "field " << *path_begin << " not in " << *value; } CARBON_ASSIGN_OR_RETURN( it->value, SetFieldImpl(arena, it->value, path_begin + 1, path_end, field_value, source_loc)); return arena->New(elements); } case Value::Kind::NominalClassValue: { const auto& object = cast(*value); if (auto inits = SetFieldImpl(arena, &object.inits(), path_begin, path_end, field_value, source_loc); inits.ok()) { return arena->New( &object.type(), *inits, object.base(), object.class_value_ptr()); } else if (object.base().has_value()) { auto new_base = SetFieldImpl(arena, object.base().value(), path_begin, path_end, field_value, source_loc); if (new_base.ok()) { return arena->New( &object.type(), &object.inits(), cast(*new_base), object.class_value_ptr()); } } // Failed to match, show full object content return ProgramError(source_loc) << "field " << *path_begin << " not in " << *value; } case Value::Kind::TupleType: case Value::Kind::TupleValue: { CARBON_CHECK((*path_begin).element()->kind() == ElementKind::PositionalElement) << "Invalid non-positional member for tuple"; std::vector> elements = cast(*value).elements(); const size_t index = cast((*path_begin).element())->index(); if (index < 0 || index >= elements.size()) { return ProgramError(source_loc) << "index " << index << " out of range in " << *value; } CARBON_ASSIGN_OR_RETURN( elements[index], SetFieldImpl(arena, elements[index], path_begin + 1, path_end, field_value, source_loc)); if (isa(value)) { return arena->New(elements); } else { return arena->New(elements); } } default: CARBON_FATAL() << "field access not allowed for value " << *value; } } auto Value::SetField(Nonnull arena, const ElementPath& path, Nonnull field_value, SourceLocation source_loc) const -> ErrorOr> { return SetFieldImpl(arena, static_cast>(this), path.components_.begin(), path.components_.end(), field_value, source_loc); } static auto PrintNameWithBindings(llvm::raw_ostream& out, Nonnull declaration, const BindingMap& args) { out << GetName(*declaration).value_or("(anonymous)"); // TODO: Print '()' if declaration is parameterized but no args are provided. if (!args.empty()) { out << "("; llvm::ListSeparator sep; for (const auto& [bind, val] : args) { out << sep << bind->name() << " = " << *val; } out << ")"; } } void Value::Print(llvm::raw_ostream& out) const { switch (kind()) { case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*this); out << alt.choice().declaration().name() << "." << alt.alternative().name(); break; } case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*this); out << "Placeholder<"; if (placeholder.value_node().has_value()) { out << (*placeholder.value_node()); } else { out << "_"; } out << ">"; break; } case Value::Kind::AddrValue: { const auto& addr = cast(*this); out << "Addr<" << addr.pattern() << ">"; break; } case Value::Kind::AlternativeValue: { const auto& alt = cast(*this); out << alt.choice().declaration().name() << "." << alt.alternative().name(); if (auto arg = alt.argument()) { out << **arg; } 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()).declaration().name() << s.inits(); if (s.base().has_value()) { out << " base " << *s.base().value(); } break; } case Value::Kind::TupleType: 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::DestructorValue: { const auto& destructor = cast(*this); out << "destructor [ "; out << destructor.declaration().self_pattern(); out << " ]"; break; } case Value::Kind::FunctionValue: { const auto& fun = cast(*this); out << "fun<" << fun.declaration().name() << ">"; if (!fun.type_args().empty()) { out << "["; llvm::ListSeparator sep; for (const auto& [ty_var, ty_arg] : fun.type_args()) { out << sep << *ty_var << "=" << *ty_arg; } out << "]"; } if (!fun.witnesses().empty()) { out << "{|"; llvm::ListSeparator sep; for (const auto& [impl_bind, witness] : fun.witnesses()) { out << sep << *witness; } out << "|}"; } break; } case Value::Kind::BoundMethodValue: { const auto& method = cast(*this); out << "bound_method<" << method.declaration().name() << ">"; if (!method.type_args().empty()) { out << "["; llvm::ListSeparator sep; for (const auto& [ty_var, ty_arg] : method.type_args()) { out << sep << *ty_var << "=" << *ty_arg; } out << "]"; } if (!method.witnesses().empty()) { out << "{|"; llvm::ListSeparator sep; for (const auto& [impl_bind, witness] : method.witnesses()) { out << sep << *witness; } out << "|}"; } break; } case Value::Kind::PointerValue: out << "ptr<" << cast(*this).address() << ">"; break; case Value::Kind::LValue: out << "lval<" << 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).pointee_type() << "*"; break; case Value::Kind::FunctionType: { const auto& fn_type = cast(*this); out << "fn "; if (!fn_type.deduced_bindings().empty()) { out << "["; llvm::ListSeparator sep; for (Nonnull deduced : fn_type.deduced_bindings()) { out << sep << *deduced; } 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::UninitializedValue: { const auto& uninit = cast(*this); out << "Uninit<" << uninit.pattern() << ">"; break; } case Value::Kind::NominalClassType: { const auto& class_type = cast(*this); out << "class "; PrintNameWithBindings(out, &class_type.declaration(), class_type.type_args()); if (!class_type.witnesses().empty()) { out << " witnesses "; llvm::ListSeparator sep; for (const auto& [impl_bind, witness] : class_type.witnesses()) { out << sep << *witness; } } break; } case Value::Kind::ChoiceType: { const auto& choice_type = cast(*this); out << "choice "; PrintNameWithBindings(out, &choice_type.declaration(), choice_type.type_args()); break; } case Value::Kind::MixinPseudoType: { const auto& mixin_type = cast(*this); out << "mixin "; PrintNameWithBindings(out, &mixin_type.declaration(), mixin_type.args()); if (!mixin_type.witnesses().empty()) { out << " witnesses "; llvm::ListSeparator sep; for (const auto& [impl_bind, witness] : mixin_type.witnesses()) { out << sep << *witness; } } // TODO: print the import interface break; } case Value::Kind::InterfaceType: { const auto& iface_type = cast(*this); out << "interface "; PrintNameWithBindings(out, &iface_type.declaration(), iface_type.bindings().args()); break; } case Value::Kind::NamedConstraintType: { const auto& constraint_type = cast(*this); out << "constraint "; PrintNameWithBindings(out, &constraint_type.declaration(), constraint_type.bindings().args()); break; } case Value::Kind::ConstraintType: { const auto& constraint = cast(*this); llvm::ListSeparator combine(" & "); for (const LookupContext& ctx : constraint.lookup_contexts()) { out << combine << *ctx.context; } if (constraint.lookup_contexts().empty()) { out << "type"; } out << " where "; llvm::ListSeparator sep(" and "); for (const RewriteConstraint& rewrite : constraint.rewrite_constraints()) { out << sep << ".("; PrintNameWithBindings(out, &rewrite.constant->interface().declaration(), rewrite.constant->interface().args()); out << "." << *GetName(rewrite.constant->constant()) << ") = " << *rewrite.unconverted_replacement; } for (const ImplConstraint& impl : constraint.impl_constraints()) { // TODO: Skip cases where `impl.type` is `.Self` and the interface is // in `lookup_contexts()`. out << sep << *impl.type << " is " << *impl.interface; } for (const EqualityConstraint& equality : constraint.equality_constraints()) { out << sep; llvm::ListSeparator equal(" == "); for (Nonnull value : equality.values) { out << equal << *value; } } break; } case Value::Kind::ImplWitness: { const auto& witness = cast(*this); out << "witness for impl " << *witness.declaration().impl_type() << " as " << witness.declaration().interface(); break; } case Value::Kind::BindingWitness: { const auto& witness = cast(*this); out << "witness for " << *witness.binding()->type_var(); break; } case Value::Kind::ConstraintWitness: { const auto& witness = cast(*this); out << "("; llvm::ListSeparator sep; for (const auto* elem : witness.witnesses()) { out << sep << *elem; } out << ")"; break; } case Value::Kind::ConstraintImplWitness: { const auto& witness = cast(*this); out << "witness " << witness.index() << " of " << *witness.constraint_witness(); break; } case Value::Kind::ParameterizedEntityName: out << *GetName(cast(*this).declaration()); break; case Value::Kind::MemberName: { const auto& member_name = cast(*this); if (member_name.base_type().has_value()) { out << *member_name.base_type().value(); } if (member_name.base_type().has_value() && member_name.interface().has_value()) { out << "("; } if (member_name.interface().has_value()) { out << *member_name.interface().value(); } out << "." << member_name; if (member_name.base_type().has_value() && member_name.interface().has_value()) { out << ")"; } break; } case Value::Kind::VariableType: out << cast(*this).binding().name(); break; case Value::Kind::AssociatedConstant: { const auto& assoc = cast(*this); out << "(" << assoc.base() << ").("; PrintNameWithBindings(out, &assoc.interface().declaration(), assoc.interface().args()); out << "." << *GetName(assoc.constant()) << ")"; 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::TypeOfMixinPseudoType: out << "typeof(" << cast(*this) .mixin_type() .declaration() .name() << ")"; break; case Value::Kind::TypeOfParameterizedEntityName: out << "parameterized entity name " << cast(*this).name(); break; case Value::Kind::TypeOfMemberName: { out << "member name " << cast(*this).member(); break; } case Value::Kind::TypeOfNamespaceName: { cast(*this).namespace_decl()->PrintID(out); break; } case Value::Kind::StaticArrayType: { const auto& array_type = cast(*this); out << "[" << array_type.element_type() << "; " << array_type.size() << "]"; break; } } } void IntrinsicConstraint::Print(llvm::raw_ostream& out) const { out << *type << " is "; switch (kind) { case IntrinsicConstraint::ImplicitAs: out << "__intrinsic_implicit_as"; break; } if (!arguments.empty()) { out << "("; llvm::ListSeparator comma; for (Nonnull argument : arguments) { out << comma << *argument; } out << ")"; } } ContinuationValue::StackFragment::~StackFragment() { CARBON_CHECK(reversed_todo_.empty()) << "All StackFragments must be empty before the Carbon program ends."; } void ContinuationValue::StackFragment::StoreReversed( std::vector> reversed_todo) { CARBON_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 << "}"; } // Check whether two binding maps, which are assumed to have the same keys, are // equal. static auto BindingMapEqual( const BindingMap& map1, const BindingMap& map2, std::optional> equality_ctx) -> bool { CARBON_CHECK(map1.size() == map2.size()) << "maps should have same keys"; for (const auto& [key, value] : map1) { if (!ValueEqual(value, map2.at(key), equality_ctx)) { return false; } } return true; } auto TypeEqual(Nonnull t1, Nonnull t2, std::optional> equality_ctx) -> bool { if (t1 == t2) { return true; } if (t1->kind() != t2->kind()) { if (IsValueKindDependent(t1) || IsValueKindDependent(t2)) { return ValueEqual(t1, t2, equality_ctx); } return false; } switch (t1->kind()) { case Value::Kind::PointerType: return TypeEqual(&cast(*t1).pointee_type(), &cast(*t2).pointee_type(), equality_ctx); case Value::Kind::FunctionType: { const auto& fn1 = cast(*t1); const auto& fn2 = cast(*t2); return TypeEqual(&fn1.parameters(), &fn2.parameters(), equality_ctx) && TypeEqual(&fn1.return_type(), &fn2.return_type(), equality_ctx); } 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, equality_ctx)) { return false; } } return true; } case Value::Kind::NominalClassType: { const auto& class1 = cast(*t1); const auto& class2 = cast(*t2); return DeclaresSameEntity(class1.declaration(), class2.declaration()) && BindingMapEqual(class1.bindings().args(), class2.bindings().args(), equality_ctx); } case Value::Kind::InterfaceType: { const auto& iface1 = cast(*t1); const auto& iface2 = cast(*t2); return DeclaresSameEntity(iface1.declaration(), iface2.declaration()) && BindingMapEqual(iface1.bindings().args(), iface2.bindings().args(), equality_ctx); } case Value::Kind::NamedConstraintType: { const auto& constraint1 = cast(*t1); const auto& constraint2 = cast(*t2); return DeclaresSameEntity(constraint1.declaration(), constraint2.declaration()) && BindingMapEqual(constraint1.bindings().args(), constraint2.bindings().args(), equality_ctx); } case Value::Kind::AssociatedConstant: // Associated constants are sometimes types. return ValueEqual(t1, t2, equality_ctx); case Value::Kind::ConstraintType: { const auto& constraint1 = cast(*t1); const auto& constraint2 = cast(*t2); if (constraint1.impl_constraints().size() != constraint2.impl_constraints().size() || constraint1.equality_constraints().size() != constraint2.equality_constraints().size() || constraint1.lookup_contexts().size() != constraint2.lookup_contexts().size()) { return false; } for (size_t i = 0; i < constraint1.impl_constraints().size(); ++i) { const auto& impl1 = constraint1.impl_constraints()[i]; const auto& impl2 = constraint2.impl_constraints()[i]; if (!TypeEqual(impl1.type, impl2.type, equality_ctx) || !TypeEqual(impl1.interface, impl2.interface, equality_ctx)) { return false; } } for (size_t i = 0; i < constraint1.equality_constraints().size(); ++i) { const auto& equality1 = constraint1.equality_constraints()[i]; const auto& equality2 = constraint2.equality_constraints()[i]; if (equality1.values.size() != equality2.values.size()) { return false; } for (size_t j = 0; j < equality1.values.size(); ++j) { if (!ValueEqual(equality1.values[i], equality2.values[i], equality_ctx)) { return false; } } } for (size_t i = 0; i < constraint1.lookup_contexts().size(); ++i) { const auto& context1 = constraint1.lookup_contexts()[i]; const auto& context2 = constraint2.lookup_contexts()[i]; if (!TypeEqual(context1.context, context2.context, equality_ctx)) { return false; } } return true; } case Value::Kind::ChoiceType: { const auto& choice1 = cast(*t1); const auto& choice2 = cast(*t2); return DeclaresSameEntity(choice1.declaration(), choice2.declaration()) && BindingMapEqual(choice1.type_args(), choice2.type_args(), equality_ctx); } case Value::Kind::TupleType: 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], equality_ctx)) { 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::StaticArrayType: { const auto& array1 = cast(*t1); const auto& array2 = cast(*t2); return TypeEqual(&array1.element_type(), &array2.element_type(), equality_ctx) && array1.size() == array2.size(); } case Value::Kind::IntValue: case Value::Kind::BoolValue: case Value::Kind::DestructorValue: case Value::Kind::FunctionValue: case Value::Kind::BoundMethodValue: case Value::Kind::StructValue: case Value::Kind::NominalClassValue: case Value::Kind::AlternativeValue: case Value::Kind::AlternativeConstructorValue: case Value::Kind::StringValue: case Value::Kind::PointerValue: case Value::Kind::LValue: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AddrValue: case Value::Kind::ContinuationValue: case Value::Kind::UninitializedValue: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::TypeOfParameterizedEntityName: case Value::Kind::TypeOfMemberName: case Value::Kind::MixinPseudoType: case Value::Kind::TypeOfMixinPseudoType: case Value::Kind::TypeOfNamespaceName: CARBON_FATAL() << "TypeEqual used to compare non-type values\n" << *t1 << "\n" << *t2; case Value::Kind::ImplWitness: case Value::Kind::BindingWitness: case Value::Kind::ConstraintWitness: case Value::Kind::ConstraintImplWitness: CARBON_FATAL() << "TypeEqual: unexpected Witness"; break; case Value::Kind::AutoType: CARBON_FATAL() << "TypeEqual: unexpected AutoType"; break; } } // Returns true if the two values are known to be equal and are written in the // same way at the top level. auto ValueStructurallyEqual( Nonnull v1, Nonnull v2, std::optional> equality_ctx) -> bool { if (v1 == v2) { return true; } 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::DestructorValue: return false; case Value::Kind::BoundMethodValue: { const auto& m1 = cast(*v1); const auto& m2 = cast(*v2); std::optional> body1 = m1.declaration().body(); std::optional> body2 = m2.declaration().body(); return ValueEqual(m1.receiver(), m2.receiver(), equality_ctx) && body1.has_value() == body2.has_value() && (!body1.has_value() || *body1 == *body2); } case Value::Kind::TupleType: 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], equality_ctx)) { return false; } } return true; } case Value::Kind::StructValue: { const auto& struct_v1 = cast(*v1); const auto& struct_v2 = cast(*v2); CARBON_CHECK(struct_v1.elements().size() == struct_v2.elements().size()); for (size_t i = 0; i < struct_v1.elements().size(); ++i) { CARBON_CHECK(struct_v1.elements()[i].name == struct_v2.elements()[i].name); if (!ValueEqual(struct_v1.elements()[i].value, struct_v2.elements()[i].value, equality_ctx)) { return false; } } return true; } case Value::Kind::AlternativeValue: { const auto& alt1 = cast(*v1); const auto& alt2 = cast(*v2); if (!TypeEqual(&alt1.choice(), &alt2.choice(), equality_ctx) || &alt1.alternative() != &alt2.alternative()) { return false; } CARBON_CHECK(alt1.argument().has_value() == alt2.argument().has_value()); return !alt1.argument().has_value() || ValueEqual(*alt1.argument(), *alt2.argument(), equality_ctx); } case Value::Kind::StringValue: return cast(*v1).value() == cast(*v2).value(); case Value::Kind::ParameterizedEntityName: { std::optional name1 = GetName(cast(v1)->declaration()); std::optional name2 = GetName(cast(v2)->declaration()); CARBON_CHECK(name1.has_value() && name2.has_value()) << "parameterized name refers to unnamed declaration"; return *name1 == *name2; } case Value::Kind::AssociatedConstant: { // The witness value is not part of determining value equality. const auto& assoc1 = cast(*v1); const auto& assoc2 = cast(*v2); return DeclaresSameEntity(assoc1.constant(), assoc2.constant()) && TypeEqual(&assoc1.base(), &assoc2.base(), equality_ctx) && TypeEqual(&assoc1.interface(), &assoc2.interface(), equality_ctx); } 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::MixinPseudoType: case Value::Kind::InterfaceType: case Value::Kind::NamedConstraintType: case Value::Kind::ConstraintType: case Value::Kind::ImplWitness: case Value::Kind::BindingWitness: case Value::Kind::ConstraintWitness: case Value::Kind::ConstraintImplWitness: case Value::Kind::ChoiceType: case Value::Kind::ContinuationType: case Value::Kind::VariableType: case Value::Kind::StringType: case Value::Kind::TypeOfMixinPseudoType: case Value::Kind::TypeOfParameterizedEntityName: case Value::Kind::TypeOfMemberName: case Value::Kind::TypeOfNamespaceName: case Value::Kind::StaticArrayType: return TypeEqual(v1, v2, equality_ctx); case Value::Kind::NominalClassValue: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AddrValue: case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: case Value::Kind::PointerValue: case Value::Kind::LValue: case Value::Kind::UninitializedValue: case Value::Kind::MemberName: // TODO: support pointer comparisons once we have a clearer distinction // between pointers and lvalues. CARBON_FATAL() << "ValueEqual does not support this kind of value: " << *v1; } } // 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, std::optional> equality_ctx) -> bool { if (v1 == v2) { return true; } // If we're given an equality context, check to see if it knows these values // are equal. Only perform the check if one or the other value is an // associated constant; otherwise we should be able to do better by looking // at the structures of the values. if (equality_ctx) { if (IsValueKindDependent(v1)) { auto visitor = [&](Nonnull maybe_v2) { return !ValueStructurallyEqual(v2, maybe_v2, equality_ctx); }; if (!(*equality_ctx)->VisitEqualValues(v1, visitor)) { return true; } } if (IsValueKindDependent(v2)) { auto visitor = [&](Nonnull maybe_v1) { return !ValueStructurallyEqual(v1, maybe_v1, equality_ctx); }; if (!(*equality_ctx)->VisitEqualValues(v2, visitor)) { return true; } } } return ValueStructurallyEqual(v1, v2, equality_ctx); } auto EqualityConstraint::VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool { // See if the given value is part of this constraint. auto first_equal = llvm::find_if(values, [value](Nonnull val) { return ValueEqual(value, val, std::nullopt); }); if (first_equal == values.end()) { return true; } // The value is in this group; pass all non-identical values in the group // to the visitor. First visit the values we already compared. for (const auto* val : llvm::make_range(values.begin(), first_equal)) { if (!visitor(val)) { return false; } } // Then visit any remaining non-identical values, skipping the one we already // found was identical. ++first_equal; for (const auto* val : llvm::make_range(first_equal, values.end())) { if (!ValueEqual(value, val, std::nullopt) && !visitor(val)) { return false; } } return true; } auto ConstraintType::VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool { for (const auto& eq : equality_constraints()) { if (!eq.VisitEqualValues(value, visitor)) { return false; } } return true; } auto FindFunction(std::string_view name, llvm::ArrayRef> members) -> std::optional> { for (const auto& member : members) { switch (member->kind()) { case DeclarationKind::MixDeclaration: { const auto& mix_decl = cast(*member); Nonnull mixin = &mix_decl.mixin_value(); const auto res = mixin->FindFunction(name); if (res.has_value()) { return res; } break; } case DeclarationKind::FunctionDeclaration: { const auto& fun = cast(*member); if (fun.name().inner_name() == name) { return &cast(**fun.constant_value()); } break; } default: break; } } return std::nullopt; } // TODO: Find out a way to remove code duplication auto MixinPseudoType::FindFunction(const std::string_view& name) const -> std::optional> { for (const auto& member : declaration().members()) { switch (member->kind()) { case DeclarationKind::MixDeclaration: { const auto& mix_decl = cast(*member); Nonnull mixin = &mix_decl.mixin_value(); const auto res = mixin->FindFunction(name); if (res.has_value()) { return res; } break; } case DeclarationKind::FunctionDeclaration: { const auto& fun = cast(*member); if (fun.name().inner_name() == name) { return &cast(**fun.constant_value()); } break; } default: break; } } return std::nullopt; } auto FindFunctionWithParents(std::string_view name, const ClassDeclaration& class_decl) -> std::optional> { if (auto fun = FindFunction(name, class_decl.members()); fun.has_value()) { return fun; } if (const auto base_type = class_decl.base_type(); base_type.has_value()) { return FindFunctionWithParents(name, base_type.value()->declaration()); } return std::nullopt; } auto FindMember(std::string_view name, llvm::ArrayRef> members) -> std::optional> { for (Nonnull member : members) { if (std::optional mem_name = GetName(*member); mem_name.has_value()) { if (*mem_name == name) { return member; } } } return std::nullopt; } void ImplBinding::Print(llvm::raw_ostream& out) const { out << "impl binding " << *type_var_ << " as " << **iface_; } void ImplBinding::PrintID(llvm::raw_ostream& out) const { out << *type_var_ << " as " << **iface_; } auto NominalClassType::InheritsClass(Nonnull other) const -> bool { const auto* other_class = dyn_cast(other); if (!other_class) { return false; } std::optional> ancestor_class = this; while (ancestor_class) { if (TypeEqual(*ancestor_class, other_class, std::nullopt)) { return true; } ancestor_class = (*ancestor_class)->base(); } return false; } } // namespace Carbon