(root)/
gcc-13.2.0/
gcc/
rust/
typecheck/
rust-substitution-mapper.cc
// Copyright (C) 2020-2023 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

#include "rust-substitution-mapper.h"
#include "rust-hir-type-check.h"

namespace Rust {
namespace Resolver {

SubstMapper::SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus)
  : resolved (new TyTy::ErrorType (ref)), generics (generics), locus (locus)
{}

TyTy::BaseType *
SubstMapper::Resolve (TyTy::BaseType *base, Location locus,
		      HIR::GenericArgs *generics)
{
  SubstMapper mapper (base->get_ref (), generics, locus);
  base->accept_vis (mapper);
  rust_assert (mapper.resolved != nullptr);
  return mapper.resolved;
}

TyTy::BaseType *
SubstMapper::InferSubst (TyTy::BaseType *base, Location locus)
{
  return SubstMapper::Resolve (base, locus, nullptr);
}

bool
SubstMapper::have_generic_args () const
{
  return generics != nullptr;
}

void
SubstMapper::visit (TyTy::FnType &type)
{
  TyTy::FnType *concrete = nullptr;
  if (!have_generic_args ())
    {
      TyTy::BaseType *substs = type.infer_substitions (locus);
      rust_assert (substs->get_kind () == TyTy::TypeKind::FNDEF);
      concrete = static_cast<TyTy::FnType *> (substs);
    }
  else
    {
      TyTy::SubstitutionArgumentMappings mappings
	= type.get_mappings_from_generic_args (*generics);
      if (mappings.is_error ())
	return;

      concrete = type.handle_substitions (mappings);
    }

  if (concrete != nullptr)
    resolved = concrete;
}

void
SubstMapper::visit (TyTy::ADTType &type)
{
  TyTy::ADTType *concrete = nullptr;
  if (!have_generic_args ())
    {
      TyTy::BaseType *substs = type.infer_substitions (locus);
      rust_assert (substs->get_kind () == TyTy::TypeKind::ADT);
      concrete = static_cast<TyTy::ADTType *> (substs);
    }
  else
    {
      TyTy::SubstitutionArgumentMappings mappings
	= type.get_mappings_from_generic_args (*generics);
      if (mappings.is_error ())
	return;

      concrete = type.handle_substitions (mappings);
    }

  if (concrete != nullptr)
    resolved = concrete;
}

void
SubstMapper::visit (TyTy::PlaceholderType &type)
{
  rust_assert (type.can_resolve ());
  resolved = SubstMapper::Resolve (type.resolve (), locus, generics);
}

void
SubstMapper::visit (TyTy::ProjectionType &type)
{
  TyTy::ProjectionType *concrete = nullptr;
  if (!have_generic_args ())
    {
      TyTy::BaseType *substs = type.infer_substitions (locus);
      rust_assert (substs->get_kind () == TyTy::TypeKind::PROJECTION);
      concrete = static_cast<TyTy::ProjectionType *> (substs);
    }
  else
    {
      TyTy::SubstitutionArgumentMappings mappings
	= type.get_mappings_from_generic_args (*generics);
      if (mappings.is_error ())
	return;

      concrete = type.handle_substitions (mappings);
    }

  if (concrete != nullptr)
    resolved = concrete;
}

SubstMapperInternal::SubstMapperInternal (
  HirId ref, TyTy::SubstitutionArgumentMappings &mappings)
  : resolved (new TyTy::ErrorType (ref)), mappings (mappings)
{}

TyTy::BaseType *
SubstMapperInternal::Resolve (TyTy::BaseType *base,
			      TyTy::SubstitutionArgumentMappings &mappings)
{
  auto context = TypeCheckContext::get ();

  SubstMapperInternal mapper (base->get_ref (), mappings);
  base->accept_vis (mapper);
  rust_assert (mapper.resolved != nullptr);

  // insert these new implict types into the context
  TyTy::BaseType *unused = nullptr;
  bool is_ty_available
    = context->lookup_type (mapper.resolved->get_ty_ref (), &unused);
  if (!is_ty_available)
    {
      context->insert_type (
	Analysis::NodeMapping (0, 0, mapper.resolved->get_ty_ref (), 0),
	mapper.resolved);
    }
  bool is_ref_available
    = context->lookup_type (mapper.resolved->get_ref (), &unused);
  if (!is_ref_available)
    {
      context->insert_type (Analysis::NodeMapping (0, 0,
						   mapper.resolved->get_ref (),
						   0),
			    mapper.resolved);
    }

  return mapper.resolved;
}

bool
SubstMapperInternal::mappings_are_bound (
  TyTy::BaseType *tyseg, TyTy::SubstitutionArgumentMappings &mappings)
{
  if (tyseg->get_kind () == TyTy::TypeKind::ADT)
    {
      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyseg);
      return adt->are_mappings_bound (mappings);
    }
  else if (tyseg->get_kind () == TyTy::TypeKind::FNDEF)
    {
      TyTy::FnType *fn = static_cast<TyTy::FnType *> (tyseg);
      return fn->are_mappings_bound (mappings);
    }

  return false;
}

void
SubstMapperInternal::visit (TyTy::FnType &type)
{
  TyTy::SubstitutionArgumentMappings adjusted
    = type.adjust_mappings_for_this (mappings);
  if (adjusted.is_error ())
    return;

  TyTy::BaseType *concrete = type.handle_substitions (adjusted);
  if (concrete != nullptr)
    resolved = concrete;
}

void
SubstMapperInternal::visit (TyTy::ADTType &type)
{
  TyTy::SubstitutionArgumentMappings adjusted
    = type.adjust_mappings_for_this (mappings);
  if (adjusted.is_error ())
    return;

  TyTy::BaseType *concrete = type.handle_substitions (adjusted);
  if (concrete != nullptr)
    resolved = concrete;
}

// these don't support generic arguments but might contain a type param
void
SubstMapperInternal::visit (TyTy::TupleType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::ReferenceType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::PointerType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::ParamType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::PlaceholderType &type)
{
  rust_assert (type.can_resolve ());
  if (mappings.trait_item_mode ())
    {
      resolved = type.resolve ();
    }
  else
    {
      resolved = SubstMapperInternal::Resolve (type.resolve (), mappings);
    }
}

void
SubstMapperInternal::visit (TyTy::ProjectionType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::ClosureType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::ArrayType &type)
{
  resolved = type.handle_substitions (mappings);
}

void
SubstMapperInternal::visit (TyTy::SliceType &type)
{
  resolved = type.handle_substitions (mappings);
}

// nothing to do for these
void
SubstMapperInternal::visit (TyTy::InferType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::FnPtr &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::BoolType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::IntType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::UintType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::FloatType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::USizeType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::ISizeType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::ErrorType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::CharType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::StrType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::NeverType &type)
{
  resolved = type.clone ();
}
void
SubstMapperInternal::visit (TyTy::DynamicObjectType &type)
{
  resolved = type.clone ();
}

// SubstMapperFromExisting

SubstMapperFromExisting::SubstMapperFromExisting (TyTy::BaseType *concrete,
						  TyTy::BaseType *receiver)
  : concrete (concrete), receiver (receiver), resolved (nullptr)
{}

TyTy::BaseType *
SubstMapperFromExisting::Resolve (TyTy::BaseType *concrete,
				  TyTy::BaseType *receiver)
{
  rust_assert (concrete->get_kind () == receiver->get_kind ());

  SubstMapperFromExisting mapper (concrete, receiver);
  concrete->accept_vis (mapper);
  return mapper.resolved;
}

void
SubstMapperFromExisting::visit (TyTy::FnType &type)
{
  rust_assert (type.was_substituted ());

  TyTy::FnType *to_sub = static_cast<TyTy::FnType *> (receiver);
  resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}

void
SubstMapperFromExisting::visit (TyTy::ADTType &type)
{
  rust_assert (type.was_substituted ());

  TyTy::ADTType *to_sub = static_cast<TyTy::ADTType *> (receiver);
  resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}

void
SubstMapperFromExisting::visit (TyTy::ClosureType &type)
{
  rust_assert (type.was_substituted ());

  TyTy::ClosureType *to_sub = static_cast<TyTy::ClosureType *> (receiver);
  resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}

// GetUsedSubstArgs

GetUsedSubstArgs::GetUsedSubstArgs ()
  : args (TyTy::SubstitutionArgumentMappings::error ())
{}

TyTy::SubstitutionArgumentMappings
GetUsedSubstArgs::From (const TyTy::BaseType *from)
{
  GetUsedSubstArgs mapper;
  from->accept_vis (mapper);
  return mapper.args;
}

void
GetUsedSubstArgs::visit (const TyTy::FnType &type)
{
  args = type.get_substitution_arguments ();
}

void
GetUsedSubstArgs::visit (const TyTy::ADTType &type)
{
  args = type.get_substitution_arguments ();
}

void
GetUsedSubstArgs::visit (const TyTy::ClosureType &type)
{
  args = type.get_substitution_arguments ();
}

} // namespace Resolver
} // namespace Rust