blob: 612d9edb07f73a3aa3a601ac1be31f44992c4dda [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/torque/type-inference.h"
namespace v8 {
namespace internal {
namespace torque {
TypeArgumentInference::TypeArgumentInference(
const GenericParameters& type_parameters,
const TypeVector& explicit_type_arguments,
const std::vector<TypeExpression*>& term_parameters,
const std::vector<base::Optional<const Type*>>& term_argument_types)
: num_explicit_(explicit_type_arguments.size()),
type_parameter_from_name_(type_parameters.size()),
inferred_(type_parameters.size()) {
if (num_explicit_ > type_parameters.size()) {
Fail("more explicit type arguments than expected");
return;
}
if (term_argument_types.size() > term_parameters.size()) {
Fail("more arguments than expected");
return;
}
for (size_t i = 0; i < type_parameters.size(); i++) {
type_parameter_from_name_[type_parameters[i].name->value] = i;
}
for (size_t i = 0; i < num_explicit_; i++) {
inferred_[i] = {explicit_type_arguments[i]};
}
for (size_t i = 0; i < term_argument_types.size(); i++) {
if (term_argument_types[i])
Match(term_parameters[i], *term_argument_types[i]);
if (HasFailed()) return;
}
for (size_t i = 0; i < type_parameters.size(); i++) {
if (!inferred_[i]) {
Fail("failed to infer arguments for all type parameters");
return;
}
}
}
TypeVector TypeArgumentInference::GetResult() const {
CHECK(!HasFailed());
TypeVector result(inferred_.size());
std::transform(
inferred_.begin(), inferred_.end(), result.begin(),
[](base::Optional<const Type*> maybe_type) { return *maybe_type; });
return result;
}
void TypeArgumentInference::Match(TypeExpression* parameter,
const Type* argument_type) {
if (BasicTypeExpression* basic =
BasicTypeExpression::DynamicCast(parameter)) {
// If the parameter is referring to one of the type parameters, substitute
if (basic->namespace_qualification.empty() && !basic->is_constexpr) {
auto result = type_parameter_from_name_.find(basic->name);
if (result != type_parameter_from_name_.end()) {
size_t type_parameter_index = result->second;
if (type_parameter_index < num_explicit_) {
return;
}
base::Optional<const Type*>& maybe_inferred =
inferred_[type_parameter_index];
if (maybe_inferred && *maybe_inferred != argument_type) {
Fail("found conflicting types for generic parameter");
} else {
inferred_[type_parameter_index] = {argument_type};
}
return;
}
}
// Try to recurse in case of generic types
if (!basic->generic_arguments.empty()) {
MatchGeneric(basic, argument_type);
}
// NOTE: We could also check whether ground parameter types match the
// argument types, but we are only interested in inferring type arguments
// here
} else {
// TODO(gsps): Perform inference on function and union types
}
}
void TypeArgumentInference::MatchGeneric(BasicTypeExpression* parameter,
const Type* argument_type) {
QualifiedName qualified_name{parameter->namespace_qualification,
parameter->name};
GenericType* generic_type =
Declarations::LookupUniqueGenericType(qualified_name);
auto& specialized_from = argument_type->GetSpecializedFrom();
if (!specialized_from || specialized_from->generic != generic_type) {
return Fail("found conflicting generic type constructors");
}
auto& parameters = parameter->generic_arguments;
auto& argument_types = specialized_from->specialized_types;
if (parameters.size() != argument_types.size()) {
Error(
"cannot infer types from generic-struct-typed parameter with "
"incompatible number of arguments")
.Position(parameter->pos)
.Throw();
}
for (size_t i = 0; i < parameters.size(); i++) {
Match(parameters[i], argument_types[i]);
if (HasFailed()) return;
}
}
} // namespace torque
} // namespace internal
} // namespace v8