diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 53e8b60d0664..d200036a3f51 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -386,44 +386,41 @@ auto TypeChecker::ExpectType(SourceLocation source_loc, auto TypeChecker::ArgumentDeduction( SourceLocation source_loc, const std::string& context, - llvm::ArrayRef> type_params, - BindingMap& deduced, Nonnull param_type, - Nonnull arg_type, bool allow_implicit_conversion) const - -> ErrorOr { + llvm::ArrayRef> bindings_to_deduce, + BindingMap& deduced, Nonnull param, Nonnull arg, + bool allow_implicit_conversion) const -> ErrorOr { if (trace_stream_) { - **trace_stream_ << "deducing " << *param_type << " from " << *arg_type - << "\n"; + **trace_stream_ << "deducing " << *param << " from " << *arg << "\n"; } // Handle the case where we can't perform deduction, either because the // parameter is a primitive type or because the parameter and argument have // different forms. In this case, we require an implicit conversion to exist, // or for an exact type match if implicit conversions are not permitted. auto handle_non_deduced_type = [&]() -> ErrorOr { - if (!IsConcreteType(param_type)) { + if (!IsConcreteType(param)) { // Parameter type contains a nested `auto` and argument type isn't the // same kind of type. // TODO: This seems like something we should be able to accept. return CompilationError(source_loc) << "type error in " << context << "\n" - << "expected: " << *param_type << "\n" - << "actual: " << *arg_type; + << "expected: " << *param << "\n" + << "actual: " << *arg; } - const Value* subst_param_type = Substitute(deduced, param_type); + const Value* subst_param_type = Substitute(deduced, param); return allow_implicit_conversion - ? ExpectType(source_loc, context, subst_param_type, arg_type) - : ExpectExactType(source_loc, context, subst_param_type, - arg_type); + ? ExpectType(source_loc, context, subst_param_type, arg) + : ExpectExactType(source_loc, context, subst_param_type, arg); }; - switch (param_type->kind()) { + switch (param->kind()) { case Value::Kind::VariableType: { - const auto& var_type = cast(*param_type); - if (std::find(type_params.begin(), type_params.end(), - &var_type.binding()) != type_params.end()) { - auto [it, success] = deduced.insert({&var_type.binding(), arg_type}); + const auto& var_type = cast(*param); + if (std::find(bindings_to_deduce.begin(), bindings_to_deduce.end(), + &var_type.binding()) != bindings_to_deduce.end()) { + auto [it, success] = deduced.insert({&var_type.binding(), arg}); if (!success) { // All deductions are required to produce the same value. CARBON_RETURN_IF_ERROR(ExpectExactType( - source_loc, "repeated argument deduction", it->second, arg_type)); + source_loc, "repeated argument deduction", it->second, arg)); } } else { return handle_non_deduced_type(); @@ -431,11 +428,11 @@ auto TypeChecker::ArgumentDeduction( return Success(); } case Value::Kind::TupleValue: { - if (arg_type->kind() != Value::Kind::TupleValue) { + if (arg->kind() != Value::Kind::TupleValue) { return handle_non_deduced_type(); } - const auto& param_tup = cast(*param_type); - const auto& arg_tup = cast(*arg_type); + const auto& param_tup = cast(*param); + const auto& arg_tup = cast(*arg); if (param_tup.elements().size() != arg_tup.elements().size()) { return CompilationError(source_loc) << "mismatch in tuple sizes, expected " @@ -443,18 +440,19 @@ auto TypeChecker::ArgumentDeduction( << arg_tup.elements().size(); } for (size_t i = 0; i < param_tup.elements().size(); ++i) { - CARBON_RETURN_IF_ERROR(ArgumentDeduction( - source_loc, context, type_params, deduced, param_tup.elements()[i], - arg_tup.elements()[i], allow_implicit_conversion)); + CARBON_RETURN_IF_ERROR( + ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, + param_tup.elements()[i], arg_tup.elements()[i], + allow_implicit_conversion)); } return Success(); } case Value::Kind::StructType: { - if (arg_type->kind() != Value::Kind::StructType) { + if (arg->kind() != Value::Kind::StructType) { return handle_non_deduced_type(); } - const auto& param_struct = cast(*param_type); - const auto& arg_struct = cast(*arg_type); + const auto& param_struct = cast(*param); + const auto& arg_struct = cast(*arg); auto diagnose_missing_field = [&](const StructType& struct_type, const NamedValue& field, bool missing_from_source) -> Error { @@ -489,7 +487,7 @@ auto TypeChecker::ArgumentDeduction( } } CARBON_RETURN_IF_ERROR(ArgumentDeduction( - source_loc, context, type_params, deduced, param_field.value, + source_loc, context, bindings_to_deduce, deduced, param_field.value, arg_field.value, allow_implicit_conversion)); } if (param_struct.fields().size() != arg_struct.fields().size()) { @@ -506,27 +504,29 @@ auto TypeChecker::ArgumentDeduction( return Success(); } case Value::Kind::FunctionType: { - if (arg_type->kind() != Value::Kind::FunctionType) { + if (arg->kind() != Value::Kind::FunctionType) { return handle_non_deduced_type(); } - const auto& param_fn = cast(*param_type); - const auto& arg_fn = cast(*arg_type); + const auto& param_fn = cast(*param); + const auto& arg_fn = cast(*arg); // TODO: handle situation when arg has deduced parameters. - CARBON_RETURN_IF_ERROR(ArgumentDeduction( - source_loc, context, type_params, deduced, ¶m_fn.parameters(), - &arg_fn.parameters(), /*allow_implicit_conversion=*/false)); - CARBON_RETURN_IF_ERROR(ArgumentDeduction( - source_loc, context, type_params, deduced, ¶m_fn.return_type(), - &arg_fn.return_type(), /*allow_implicit_conversion=*/false)); + CARBON_RETURN_IF_ERROR( + ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, + ¶m_fn.parameters(), &arg_fn.parameters(), + /*allow_implicit_conversion=*/false)); + CARBON_RETURN_IF_ERROR( + ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, + ¶m_fn.return_type(), &arg_fn.return_type(), + /*allow_implicit_conversion=*/false)); return Success(); } case Value::Kind::PointerType: { - if (arg_type->kind() != Value::Kind::PointerType) { + if (arg->kind() != Value::Kind::PointerType) { return handle_non_deduced_type(); } - return ArgumentDeduction(source_loc, context, type_params, deduced, - &cast(*param_type).type(), - &cast(*arg_type).type(), + return ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, + &cast(*param).type(), + &cast(*arg).type(), /*allow_implicit_conversion=*/false); } // Nothing to do in the case for `auto`. @@ -534,38 +534,38 @@ auto TypeChecker::ArgumentDeduction( return Success(); } case Value::Kind::NominalClassType: { - const auto& param_class_type = cast(*param_type); - if (arg_type->kind() != Value::Kind::NominalClassType) { + const auto& param_class_type = cast(*param); + if (arg->kind() != Value::Kind::NominalClassType) { // TODO: We could determine the parameters of the class from field // types in a struct argument. return handle_non_deduced_type(); } - const auto& arg_class_type = cast(*arg_type); + const auto& arg_class_type = cast(*arg); if (param_class_type.declaration().name() != arg_class_type.declaration().name()) { return handle_non_deduced_type(); } for (const auto& [ty, param_ty] : param_class_type.type_args()) { CARBON_RETURN_IF_ERROR( - ArgumentDeduction(source_loc, context, type_params, deduced, + ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, param_ty, arg_class_type.type_args().at(ty), /*allow_implicit_conversion=*/false)); } return Success(); } case Value::Kind::InterfaceType: { - const auto& param_iface_type = cast(*param_type); - if (arg_type->kind() != Value::Kind::InterfaceType) { + const auto& param_iface_type = cast(*param); + if (arg->kind() != Value::Kind::InterfaceType) { return handle_non_deduced_type(); } - const auto& arg_iface_type = cast(*arg_type); + const auto& arg_iface_type = cast(*arg); if (param_iface_type.declaration().name() != arg_iface_type.declaration().name()) { return handle_non_deduced_type(); } for (const auto& [ty, param_ty] : param_iface_type.args()) { CARBON_RETURN_IF_ERROR( - ArgumentDeduction(source_loc, context, type_params, deduced, + ArgumentDeduction(source_loc, context, bindings_to_deduce, deduced, param_ty, arg_iface_type.args().at(ty), /*allow_implicit_conversion=*/false)); } @@ -605,10 +605,10 @@ auto TypeChecker::ArgumentDeduction( // Argument deduction within the parameters of a parameterized class type // or interface type can compare values, rather than types. // TODO: Deduce within the values where possible. - if (!ValueEqual(param_type, arg_type)) { + if (!ValueEqual(param, arg)) { return CompilationError(source_loc) - << "mismatch in non-type values, `" << *arg_type << "` != `" - << *param_type << "`"; + << "mismatch in non-type values, `" << *arg << "` != `" << *param + << "`"; } return Success(); } diff --git a/explorer/interpreter/type_checker.h b/explorer/interpreter/type_checker.h index 92d445c1b404..87e2d3389279 100644 --- a/explorer/interpreter/type_checker.h +++ b/explorer/interpreter/type_checker.h @@ -30,17 +30,19 @@ class TypeChecker { // processed. auto TypeCheck(AST& ast) -> ErrorOr; - // Perform type argument deduction, matching the parameter type `param` - // against the argument type `arg`. Whenever there is an VariableType - // in the parameter type, it is deduced to be the corresponding type - // inside the argument type. + // Perform type argument deduction, matching the parameter value `param` + // against the argument value `arg`. Whenever there is an VariableType in the + // parameter, it is deduced to be the corresponding type inside the argument + // type. The argument and parameter will typically be types, but can be + // non-type values when deduction recurses into the arguments of a + // parameterized type. // The `deduced` parameter is an accumulator, that is, it holds the // results so-far. auto ArgumentDeduction( SourceLocation source_loc, const std::string& context, - llvm::ArrayRef> type_params, - BindingMap& deduced, Nonnull param_type, - Nonnull arg_type, bool allow_implicit_conversion) const + llvm::ArrayRef> bindings_to_deduce, + BindingMap& deduced, Nonnull param, + Nonnull arg, bool allow_implicit_conversion) const -> ErrorOr; // If `impl` can be an implementation of interface `iface` for the