// 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/impl_scope.h" #include "explorer/interpreter/type_checker.h" #include "explorer/interpreter/value.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" using llvm::cast; using llvm::dyn_cast; namespace Carbon { void ImplScope::Add(Nonnull iface, Nonnull type, Nonnull impl, const TypeChecker& type_checker) { Add(iface, {}, type, {}, impl, type_checker); } void ImplScope::Add(Nonnull iface, llvm::ArrayRef> deduced, Nonnull type, llvm::ArrayRef> impl_bindings, Nonnull impl_expr, const TypeChecker& type_checker) { if (auto* orig_constraint = dyn_cast(iface)) { BindingMap map; map[orig_constraint->self_binding()] = type; const ConstraintType* constraint = cast(type_checker.Substitute(map, orig_constraint)); for (size_t i = 0; i != constraint->impl_constraints().size(); ++i) { ConstraintType::ImplConstraint impl = constraint->impl_constraints()[i]; Add(impl.interface, deduced, impl.type, impl_bindings, type_checker.MakeConstraintWitnessAccess(impl_expr, i), type_checker); } // A paremterized impl declaration doesn't contribute any equality // constraints to the scope. Instead, we'll resolve the equality // constraints by resolving a witness when needed. if (deduced.empty()) { for (auto& equality_constraint : constraint->equality_constraints()) { equalities_.push_back(&equality_constraint); } } return; } impls_.push_back({.interface = cast(iface), .deduced = deduced, .type = type, .impl_bindings = impl_bindings, .impl = impl_expr}); } void ImplScope::AddParent(Nonnull parent) { parent_scopes_.push_back(parent); } auto ImplScope::Resolve(Nonnull constraint_type, Nonnull impl_type, SourceLocation source_loc, const TypeChecker& type_checker) const -> ErrorOr> { if (const auto* iface_type = dyn_cast(constraint_type)) { return ResolveInterface(iface_type, impl_type, source_loc, type_checker); } if (const auto* constraint = dyn_cast(constraint_type)) { std::vector> witnesses; BindingMap map; map[constraint->self_binding()] = impl_type; for (auto impl : constraint->impl_constraints()) { CARBON_ASSIGN_OR_RETURN( Nonnull result, ResolveInterface( cast(type_checker.Substitute(map, impl.interface)), type_checker.Substitute(map, impl.type), source_loc, type_checker)); witnesses.push_back(result); } // TODO: Check satisfaction of same-type constraints. return type_checker.MakeConstraintWitness(*constraint, std::move(witnesses), source_loc); } CARBON_FATAL() << "expected a constraint, not " << *constraint_type; } auto ImplScope::VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool { for (Nonnull eq : equalities_) { if (!eq->VisitEqualValues(value, visitor)) { return false; } } for (Nonnull parent : parent_scopes_) { if (!parent->VisitEqualValues(value, visitor)) { return false; } } return true; } auto ImplScope::ResolveInterface(Nonnull iface_type, Nonnull type, SourceLocation source_loc, const TypeChecker& type_checker) const -> ErrorOr> { CARBON_ASSIGN_OR_RETURN( std::optional> result, TryResolve(iface_type, type, source_loc, *this, type_checker)); if (!result.has_value()) { return CompilationError(source_loc) << "could not find implementation of " << *iface_type << " for " << *type; } return *result; } auto ImplScope::TryResolve(Nonnull iface_type, Nonnull type, SourceLocation source_loc, const ImplScope& original_scope, const TypeChecker& type_checker) const -> ErrorOr>> { CARBON_ASSIGN_OR_RETURN( std::optional> result, ResolveHere(iface_type, type, source_loc, original_scope, type_checker)); for (Nonnull parent : parent_scopes_) { CARBON_ASSIGN_OR_RETURN(std::optional> parent_result, parent->TryResolve(iface_type, type, source_loc, original_scope, type_checker)); if (parent_result.has_value()) { if (result.has_value()) { return CompilationError(source_loc) << "ambiguous implementations of " << *iface_type << " for " << *type; } else { result = *parent_result; } } } return result; } auto ImplScope::ResolveHere(Nonnull iface_type, Nonnull impl_type, SourceLocation source_loc, const ImplScope& original_scope, const TypeChecker& type_checker) const -> ErrorOr>> { std::optional> result = std::nullopt; for (const Impl& impl : impls_) { std::optional> m = type_checker.MatchImpl( *iface_type, impl_type, impl, original_scope, source_loc); if (m.has_value()) { if (result.has_value()) { return CompilationError(source_loc) << "ambiguous implementations of " << *iface_type << " for " << *impl_type; } else { result = *m; } } } return result; } // TODO: Add indentation when printing the parents. void ImplScope::Print(llvm::raw_ostream& out) const { out << "impls: "; llvm::ListSeparator sep; for (const Impl& impl : impls_) { out << sep << *(impl.type) << " as " << *(impl.interface); } for (Nonnull eq : equalities_) { out << sep; llvm::ListSeparator equal(" == "); for (Nonnull value : eq->values) { out << equal << *value; } } out << "\n"; for (const Nonnull& parent : parent_scopes_) { out << *parent; } } } // namespace Carbon