mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
We don't seem to have any need for `TypeOf*Type` types, and having them introduces the temptation to use them during type-checking, which would lead to types having different behavior when their type-of-type is `Type` versus when it's a more precise type. Remove these types for now. If we later decide that we want each type literal to have a unique type, as we do for value literals, we can introduce a single value kind for that rather than one for each kind of type. No changes to `TypeOfMemberName`, `TypeOfParameterizedEntityName`, and `TypeOfMixinPseudoType`, which are placeholders, not real types.
1060 lines
38 KiB
C++
1060 lines
38 KiB
C++
// 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 <algorithm>
|
|
|
|
#include "common/check.h"
|
|
#include "explorer/common/arena.h"
|
|
#include "explorer/common/error_builders.h"
|
|
#include "explorer/interpreter/action.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<Nonnull<const Value*>> {
|
|
for (const NamedValue& element : elements_) {
|
|
if (element.name == name) {
|
|
return element.value;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
|
const FieldPath::Component& field,
|
|
SourceLocation source_loc, Nonnull<const Value*> me_value)
|
|
-> ErrorOr<Nonnull<const Value*>> {
|
|
std::string_view f = field.name();
|
|
|
|
if (field.witness().has_value()) {
|
|
Nonnull<const Witness*> witness = cast<Witness>(*field.witness());
|
|
|
|
// Associated constants.
|
|
if (auto* assoc_const = dyn_cast_or_null<AssociatedConstantDeclaration>(
|
|
field.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<AssociatedConstant>(v, *field.interface(), assoc_const,
|
|
witness);
|
|
}
|
|
|
|
// Associated functions.
|
|
if (auto* impl_witness = dyn_cast<ImplWitness>(witness)) {
|
|
if (std::optional<Nonnull<const Declaration*>> mem_decl =
|
|
FindMember(f, impl_witness->declaration().members());
|
|
mem_decl.has_value()) {
|
|
const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
|
|
if (fun_decl.is_method()) {
|
|
return arena->New<BoundMethodValue>(&fun_decl, v,
|
|
&impl_witness->bindings());
|
|
} else {
|
|
// Class function.
|
|
auto* fun = cast<FunctionValue>(*fun_decl.constant_value());
|
|
return arena->New<FunctionValue>(&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<Nonnull<const Value*>> field =
|
|
cast<StructValue>(*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<NominalClassValue>(*v);
|
|
// Look for a field.
|
|
// Note that the value representation of an empty class is a
|
|
// `StructType`, not a `StructValue`.
|
|
std::optional<Nonnull<const Value*>> field;
|
|
if (auto* struct_value = dyn_cast<StructValue>(&object.inits())) {
|
|
field = struct_value->FindField(f);
|
|
}
|
|
if (field.has_value()) {
|
|
return *field;
|
|
} else {
|
|
// Look for a method in the object's class
|
|
const auto& class_type = cast<NominalClassType>(object.type());
|
|
std::optional<Nonnull<const FunctionValue*>> func =
|
|
class_type.FindFunction(f);
|
|
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 FunctionValue& m = cast<FunctionValue>(**func);
|
|
return arena->New<BoundMethodValue>(&m.declaration(), me_value,
|
|
&class_type.bindings());
|
|
} else {
|
|
// Found a class function
|
|
// TODO: This should not be reachable.
|
|
return arena->New<FunctionValue>(&(*func)->declaration(),
|
|
&class_type.bindings());
|
|
}
|
|
}
|
|
}
|
|
case Value::Kind::ChoiceType: {
|
|
const auto& choice = cast<ChoiceType>(*v);
|
|
if (!choice.FindAlternative(f)) {
|
|
return ProgramError(source_loc)
|
|
<< "alternative " << f << " not in " << *v;
|
|
}
|
|
return arena->New<AlternativeConstructorValue>(f, choice.name());
|
|
}
|
|
case Value::Kind::NominalClassType: {
|
|
// Access a class function.
|
|
const NominalClassType& class_type = cast<NominalClassType>(*v);
|
|
std::optional<Nonnull<const FunctionValue*>> fun =
|
|
class_type.FindFunction(f);
|
|
if (fun == std::nullopt) {
|
|
return ProgramError(source_loc)
|
|
<< "class function " << f << " not in " << *v;
|
|
}
|
|
return arena->New<FunctionValue>(&(*fun)->declaration(),
|
|
&class_type.bindings());
|
|
}
|
|
default:
|
|
CARBON_FATAL() << "field access not allowed for value " << *v;
|
|
}
|
|
}
|
|
|
|
auto Value::GetMember(Nonnull<Arena*> arena, const FieldPath& path,
|
|
SourceLocation source_loc,
|
|
Nonnull<const Value*> me_value) const
|
|
-> ErrorOr<Nonnull<const Value*>> {
|
|
Nonnull<const Value*> value(this);
|
|
for (const FieldPath::Component& field : path.components_) {
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
value, Carbon::GetMember(arena, value, field, source_loc, me_value));
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static auto SetFieldImpl(
|
|
Nonnull<Arena*> arena, Nonnull<const Value*> value,
|
|
std::vector<FieldPath::Component>::const_iterator path_begin,
|
|
std::vector<FieldPath::Component>::const_iterator path_end,
|
|
Nonnull<const Value*> field_value, SourceLocation source_loc)
|
|
-> ErrorOr<Nonnull<const Value*>> {
|
|
if (path_begin == path_end) {
|
|
return field_value;
|
|
}
|
|
switch (value->kind()) {
|
|
case Value::Kind::StructValue: {
|
|
std::vector<NamedValue> elements = cast<StructValue>(*value).elements();
|
|
auto it =
|
|
llvm::find_if(elements, [path_begin](const NamedValue& element) {
|
|
return element.name == (*path_begin).name();
|
|
});
|
|
if (it == elements.end()) {
|
|
return ProgramError(source_loc)
|
|
<< "field " << (*path_begin).name() << " 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<StructValue>(elements);
|
|
}
|
|
case Value::Kind::NominalClassValue: {
|
|
const NominalClassValue& object = cast<NominalClassValue>(*value);
|
|
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inits,
|
|
SetFieldImpl(arena, &object.inits(), path_begin,
|
|
path_end, field_value, source_loc));
|
|
return arena->New<NominalClassValue>(&object.type(), inits);
|
|
}
|
|
case Value::Kind::TupleValue: {
|
|
std::vector<Nonnull<const Value*>> elements =
|
|
cast<TupleValue>(*value).elements();
|
|
// TODO(geoffromer): update FieldPath to hold integers as well as strings.
|
|
int index = std::stoi(std::string((*path_begin).name()));
|
|
if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
|
|
return ProgramError(source_loc) << "index " << (*path_begin).name()
|
|
<< " out of range in " << *value;
|
|
}
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
elements[index], SetFieldImpl(arena, elements[index], path_begin + 1,
|
|
path_end, field_value, source_loc));
|
|
return arena->New<TupleValue>(elements);
|
|
}
|
|
default:
|
|
CARBON_FATAL() << "field access not allowed for value " << *value;
|
|
}
|
|
}
|
|
|
|
auto Value::SetField(Nonnull<Arena*> arena, const FieldPath& path,
|
|
Nonnull<const Value*> field_value,
|
|
SourceLocation source_loc) const
|
|
-> ErrorOr<Nonnull<const Value*>> {
|
|
return SetFieldImpl(arena, Nonnull<const Value*>(this),
|
|
path.components_.begin(), path.components_.end(),
|
|
field_value, source_loc);
|
|
}
|
|
|
|
static auto PrintNameWithBindings(llvm::raw_ostream& out,
|
|
Nonnull<const Declaration*> 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<AlternativeConstructorValue>(*this);
|
|
out << alt.choice_name() << "." << alt.alt_name();
|
|
break;
|
|
}
|
|
case Value::Kind::BindingPlaceholderValue: {
|
|
const auto& placeholder = cast<BindingPlaceholderValue>(*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<AddrValue>(*this);
|
|
out << "Addr<" << addr.pattern() << ">";
|
|
break;
|
|
}
|
|
case Value::Kind::AlternativeValue: {
|
|
const auto& alt = cast<AlternativeValue>(*this);
|
|
out << "alt " << alt.choice_name() << "." << alt.alt_name() << " "
|
|
<< alt.argument();
|
|
break;
|
|
}
|
|
case Value::Kind::StructValue: {
|
|
const auto& struct_val = cast<StructValue>(*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<NominalClassValue>(*this);
|
|
out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
|
|
break;
|
|
}
|
|
case Value::Kind::TupleValue: {
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (Nonnull<const Value*> element : cast<TupleValue>(*this).elements()) {
|
|
out << sep << *element;
|
|
}
|
|
out << ")";
|
|
break;
|
|
}
|
|
case Value::Kind::IntValue:
|
|
out << cast<IntValue>(*this).value();
|
|
break;
|
|
case Value::Kind::BoolValue:
|
|
out << (cast<BoolValue>(*this).value() ? "true" : "false");
|
|
break;
|
|
case Value::Kind::DestructorValue: {
|
|
const DestructorValue& destructor = cast<DestructorValue>(*this);
|
|
out << "destructor [ ";
|
|
out << destructor.declaration().me_pattern();
|
|
out << " ]";
|
|
break;
|
|
}
|
|
case Value::Kind::FunctionValue: {
|
|
const FunctionValue& fun = cast<FunctionValue>(*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 BoundMethodValue& method = cast<BoundMethodValue>(*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<PointerValue>(*this).address() << ">";
|
|
break;
|
|
case Value::Kind::LValue:
|
|
out << "lval<" << cast<LValue>(*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<PointerType>(*this).type() << "*";
|
|
break;
|
|
case Value::Kind::FunctionType: {
|
|
const auto& fn_type = cast<FunctionType>(*this);
|
|
out << "fn ";
|
|
if (!fn_type.deduced_bindings().empty()) {
|
|
out << "[";
|
|
llvm::ListSeparator sep;
|
|
for (Nonnull<const GenericBinding*> 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<StructType>(*this).fields()) {
|
|
out << sep << "." << name << ": " << *type;
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
case Value::Kind::UninitializedValue: {
|
|
const auto& uninit = cast<UninitializedValue>(*this);
|
|
out << "Uninit<" << uninit.pattern() << ">";
|
|
break;
|
|
}
|
|
case Value::Kind::NominalClassType: {
|
|
const auto& class_type = cast<NominalClassType>(*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::MixinPseudoType: {
|
|
const auto& mixin_type = cast<MixinPseudoType>(*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<InterfaceType>(*this);
|
|
out << "interface ";
|
|
PrintNameWithBindings(out, &iface_type.declaration(), iface_type.args());
|
|
break;
|
|
}
|
|
case Value::Kind::ConstraintType: {
|
|
const auto& constraint = cast<ConstraintType>(*this);
|
|
out << "constraint ";
|
|
llvm::ListSeparator combine(" & ");
|
|
for (const ConstraintType::LookupContext& ctx :
|
|
constraint.lookup_contexts()) {
|
|
out << combine << *ctx.context;
|
|
}
|
|
out << " where ";
|
|
llvm::ListSeparator sep(" and ");
|
|
for (const ConstraintType::RewriteConstraint& rewrite :
|
|
constraint.rewrite_constraints()) {
|
|
out << sep << ".(" << *rewrite.interface << "."
|
|
<< *GetName(*rewrite.constant)
|
|
<< ") = " << rewrite.replacement->value();
|
|
}
|
|
for (const ConstraintType::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 ConstraintType::EqualityConstraint& equality :
|
|
constraint.equality_constraints()) {
|
|
// TODO: Skip cases matching something in `rewrite_constraints()`.
|
|
out << sep;
|
|
llvm::ListSeparator equal(" == ");
|
|
for (Nonnull<const Value*> value : equality.values) {
|
|
out << equal << *value;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Value::Kind::ImplWitness: {
|
|
const auto& witness = cast<ImplWitness>(*this);
|
|
out << "witness for impl " << *witness.declaration().impl_type() << " as "
|
|
<< witness.declaration().interface();
|
|
break;
|
|
}
|
|
case Value::Kind::BindingWitness: {
|
|
const auto& witness = cast<BindingWitness>(*this);
|
|
out << "witness for " << *witness.binding()->type_var();
|
|
break;
|
|
}
|
|
case Value::Kind::ConstraintWitness: {
|
|
const auto& witness = cast<ConstraintWitness>(*this);
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (auto* elem : witness.witnesses()) {
|
|
out << sep << *elem;
|
|
}
|
|
out << ")";
|
|
break;
|
|
}
|
|
case Value::Kind::ConstraintImplWitness: {
|
|
const auto& witness = cast<ConstraintImplWitness>(*this);
|
|
out << "witness " << witness.index() << " of "
|
|
<< *witness.constraint_witness();
|
|
break;
|
|
}
|
|
case Value::Kind::ParameterizedEntityName:
|
|
out << *GetName(cast<ParameterizedEntityName>(*this).declaration());
|
|
break;
|
|
case Value::Kind::MemberName: {
|
|
const auto& member_name = cast<MemberName>(*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.name();
|
|
if (member_name.base_type().has_value() &&
|
|
member_name.interface().has_value()) {
|
|
out << ")";
|
|
}
|
|
break;
|
|
}
|
|
case Value::Kind::ChoiceType:
|
|
out << "choice " << cast<ChoiceType>(*this).name();
|
|
break;
|
|
case Value::Kind::VariableType:
|
|
out << cast<VariableType>(*this).binding().name();
|
|
break;
|
|
case Value::Kind::AssociatedConstant: {
|
|
const auto& assoc = cast<AssociatedConstant>(*this);
|
|
out << "(" << assoc.base() << ").(";
|
|
PrintNameWithBindings(out, &assoc.interface().declaration(),
|
|
assoc.interface().args());
|
|
out << "." << assoc.constant().binding().name() << ")";
|
|
break;
|
|
}
|
|
case Value::Kind::ContinuationValue: {
|
|
out << cast<ContinuationValue>(*this).stack();
|
|
break;
|
|
}
|
|
case Value::Kind::StringType:
|
|
out << "String";
|
|
break;
|
|
case Value::Kind::StringValue:
|
|
out << "\"";
|
|
out.write_escaped(cast<StringValue>(*this).value());
|
|
out << "\"";
|
|
break;
|
|
case Value::Kind::TypeOfMixinPseudoType:
|
|
out << "typeof("
|
|
<< cast<TypeOfMixinPseudoType>(*this)
|
|
.mixin_type()
|
|
.declaration()
|
|
.name()
|
|
<< ")";
|
|
break;
|
|
case Value::Kind::TypeOfParameterizedEntityName:
|
|
out << "parameterized entity name "
|
|
<< cast<TypeOfParameterizedEntityName>(*this).name();
|
|
break;
|
|
case Value::Kind::TypeOfMemberName: {
|
|
out << "member name " << cast<TypeOfMemberName>(*this).member().name();
|
|
break;
|
|
}
|
|
case Value::Kind::StaticArrayType: {
|
|
const auto& array_type = cast<StaticArrayType>(*this);
|
|
out << "[" << array_type.element_type() << "; " << array_type.size()
|
|
<< "]";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ContinuationValue::StackFragment::~StackFragment() {
|
|
CARBON_CHECK(reversed_todo_.empty())
|
|
<< "All StackFragments must be empty before the Carbon program ends.";
|
|
}
|
|
|
|
void ContinuationValue::StackFragment::StoreReversed(
|
|
std::vector<std::unique_ptr<Action>> reversed_todo) {
|
|
CARBON_CHECK(reversed_todo_.empty());
|
|
reversed_todo_ = std::move(reversed_todo);
|
|
}
|
|
|
|
void ContinuationValue::StackFragment::RestoreTo(
|
|
Stack<std::unique_ptr<Action>>& 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>& 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<Nonnull<const EqualityContext*>> 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<const Value*> t1, Nonnull<const Value*> t2,
|
|
std::optional<Nonnull<const EqualityContext*>> 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<PointerType>(*t1).type(),
|
|
&cast<PointerType>(*t2).type(), equality_ctx);
|
|
case Value::Kind::FunctionType: {
|
|
const auto& fn1 = cast<FunctionType>(*t1);
|
|
const auto& fn2 = cast<FunctionType>(*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<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].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<NominalClassType>(*t1);
|
|
const auto& class2 = cast<NominalClassType>(*t2);
|
|
return class1.declaration().name() == class2.declaration().name() &&
|
|
BindingMapEqual(class1.type_args(), class2.type_args(),
|
|
equality_ctx);
|
|
}
|
|
case Value::Kind::InterfaceType: {
|
|
const auto& iface1 = cast<InterfaceType>(*t1);
|
|
const auto& iface2 = cast<InterfaceType>(*t2);
|
|
return iface1.declaration().name() == iface2.declaration().name() &&
|
|
BindingMapEqual(iface1.args(), iface2.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<ConstraintType>(*t1);
|
|
const auto& constraint2 = cast<ConstraintType>(*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:
|
|
return cast<ChoiceType>(*t1).name() == cast<ChoiceType>(*t2).name();
|
|
case Value::Kind::TupleValue: {
|
|
const auto& tup1 = cast<TupleValue>(*t1);
|
|
const auto& tup2 = cast<TupleValue>(*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<VariableType>(*t1).binding() ==
|
|
&cast<VariableType>(*t2).binding();
|
|
case Value::Kind::StaticArrayType: {
|
|
const auto& array1 = cast<StaticArrayType>(*t1);
|
|
const auto& array2 = cast<StaticArrayType>(*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:
|
|
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<const Value*> v1, Nonnull<const Value*> v2,
|
|
std::optional<Nonnull<const EqualityContext*>> equality_ctx) -> bool {
|
|
if (v1 == v2) {
|
|
return true;
|
|
}
|
|
if (v1->kind() != v2->kind()) {
|
|
return false;
|
|
}
|
|
switch (v1->kind()) {
|
|
case Value::Kind::IntValue:
|
|
return cast<IntValue>(*v1).value() == cast<IntValue>(*v2).value();
|
|
case Value::Kind::BoolValue:
|
|
return cast<BoolValue>(*v1).value() == cast<BoolValue>(*v2).value();
|
|
case Value::Kind::FunctionValue: {
|
|
std::optional<Nonnull<const Statement*>> body1 =
|
|
cast<FunctionValue>(*v1).declaration().body();
|
|
std::optional<Nonnull<const Statement*>> body2 =
|
|
cast<FunctionValue>(*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<BoundMethodValue>(*v1);
|
|
const auto& m2 = cast<BoundMethodValue>(*v2);
|
|
std::optional<Nonnull<const Statement*>> body1 = m1.declaration().body();
|
|
std::optional<Nonnull<const Statement*>> 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::TupleValue: {
|
|
const std::vector<Nonnull<const Value*>>& elements1 =
|
|
cast<TupleValue>(*v1).elements();
|
|
const std::vector<Nonnull<const Value*>>& elements2 =
|
|
cast<TupleValue>(*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<StructValue>(*v1);
|
|
const auto& struct_v2 = cast<StructValue>(*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::StringValue:
|
|
return cast<StringValue>(*v1).value() == cast<StringValue>(*v2).value();
|
|
case Value::Kind::ParameterizedEntityName: {
|
|
std::optional<std::string_view> name1 =
|
|
GetName(cast<ParameterizedEntityName>(v1)->declaration());
|
|
std::optional<std::string_view> name2 =
|
|
GetName(cast<ParameterizedEntityName>(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<AssociatedConstant>(*v1);
|
|
const auto& assoc2 = cast<AssociatedConstant>(*v2);
|
|
return &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::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::StaticArrayType:
|
|
return TypeEqual(v1, v2, equality_ctx);
|
|
case Value::Kind::NominalClassValue:
|
|
case Value::Kind::AlternativeValue:
|
|
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<const Value*> v1, Nonnull<const Value*> v2,
|
|
std::optional<Nonnull<const EqualityContext*>> 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<const Value*> maybe_v2) {
|
|
return !ValueStructurallyEqual(v2, maybe_v2, equality_ctx);
|
|
};
|
|
if (!(*equality_ctx)->VisitEqualValues(v1, visitor)) {
|
|
return true;
|
|
}
|
|
}
|
|
if (IsValueKindDependent(v2)) {
|
|
auto visitor = [&](Nonnull<const Value*> 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<const Value*> value,
|
|
llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
|
|
// See if the given value is part of this constraint.
|
|
auto first_equal = llvm::find_if(values, [value](Nonnull<const Value*> 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 (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 (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<const Value*> value,
|
|
llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
|
|
for (const auto& eq : equality_constraints()) {
|
|
if (!eq.VisitEqualValues(value, visitor)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto ChoiceType::FindAlternative(std::string_view name) const
|
|
-> std::optional<Nonnull<const Value*>> {
|
|
std::vector<NamedValue> alternatives = declaration_->members();
|
|
for (const NamedValue& alternative : alternatives) {
|
|
if (alternative.name == name) {
|
|
return alternative.value;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto NominalClassType::FindFunction(std::string_view name) const
|
|
-> std::optional<Nonnull<const FunctionValue*>> {
|
|
for (const auto& member : declaration().members()) {
|
|
switch (member->kind()) {
|
|
case DeclarationKind::MixDeclaration: {
|
|
const auto& mix_decl = cast<MixDeclaration>(*member);
|
|
Nonnull<const MixinPseudoType*> 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<CallableDeclaration>(*member);
|
|
if (fun.name() == name) {
|
|
return &cast<FunctionValue>(**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<Nonnull<const FunctionValue*>> {
|
|
for (const auto& member : declaration().members()) {
|
|
switch (member->kind()) {
|
|
case DeclarationKind::MixDeclaration: {
|
|
const auto& mix_decl = cast<MixDeclaration>(*member);
|
|
Nonnull<const MixinPseudoType*> 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<CallableDeclaration>(*member);
|
|
if (fun.name() == name) {
|
|
return &cast<FunctionValue>(**fun.constant_value());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto FindMember(std::string_view name,
|
|
llvm::ArrayRef<Nonnull<Declaration*>> members)
|
|
-> std::optional<Nonnull<const Declaration*>> {
|
|
for (Nonnull<const Declaration*> member : members) {
|
|
if (std::optional<std::string_view> 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_;
|
|
}
|
|
|
|
} // namespace Carbon
|