// 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.

#ifndef V8_TORQUE_TYPE_INFERENCE_H_
#define V8_TORQUE_TYPE_INFERENCE_H_

#include <string>
#include <unordered_map>

#include "src/base/optional.h"
#include "src/torque/ast.h"
#include "src/torque/declarations.h"
#include "src/torque/types.h"

namespace v8 {
namespace internal {
namespace torque {

// Type argument inference computes a potential instantiation of a generic
// callable given some concrete argument types. As an example, consider the
// generic macro
//
//   macro Pick<T: type>(x: T, y: T): T
//
// along with a given call site, such as
//
//   Pick(1, 2);
//
// The inference proceeds by matching the term argument types (`constexpr
// int31`, in case of `1` and `2`) against the formal parameter types (`T` in
// both cases). During this matching we discover that `T` must equal `constexpr
// int31`.
//
// The inference will not perform any comprehensive type checking of its own,
// but *does* fail if type parameters cannot be soundly instantiated given the
// call site. For instance, for the following call site
//
//   const aSmi: Smi = ...;
//   Pick(1, aSmi);  // inference fails
//
// inference would fail, since `constexpr int31` is distinct from `Smi`. To
// allow for implicit conversions to be tried in a separate step after type
// argument inference, a number of type arguments may be given explicitly:
//
//   Pick<Smi>(1, aSmi);  // inference succeeds (doing nothing)
//
// In the above case the inference simply ignores inconsistent constraints on
// `T`. Similarly, we ignore all constraints arising from formal parameters
// that are function- or union-typed.
//
// Finally, note that term parameters are passed as type expressions, since
// we have no way of expressing a reference to type parameter as a Type. These
// type expressions are resolved during matching, so TypeArgumentInference
// should be instantiated in the appropriate scope.
class TypeArgumentInference {
 public:
  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);

  bool HasFailed() const { return failure_reason_.has_value(); }
  const std::string& GetFailureReason() { return *failure_reason_; }
  TypeVector GetResult() const;
  void Fail(std::string reason) { failure_reason_ = {reason}; }

 private:
  void Match(TypeExpression* parameter, const Type* argument_type);
  void MatchGeneric(BasicTypeExpression* parameter, const Type* argument_type);

  size_t num_explicit_;
  std::unordered_map<std::string, size_t> type_parameter_from_name_;
  std::vector<base::Optional<const Type*>> inferred_;
  base::Optional<std::string> failure_reason_;
};

}  // namespace torque
}  // namespace internal
}  // namespace v8

#endif  // V8_TORQUE_TYPE_INFERENCE_H_