gccrs: Add remaining type system transformations

This patch implements multiple transformation performed on the HIR
during type-resolution such as type coercion, casts, auto-dereferencement.

	gcc/rust/
	* typecheck/rust-autoderef.cc: New.
	* typecheck/rust-autoderef.h: New.
	* typecheck/rust-casts.cc: New.
	* typecheck/rust-casts.h: New.
	* typecheck/rust-coercion.cc: New.
	* typecheck/rust-coercion.h: New.
	* typecheck/rust-hir-dot-operator.cc: New.
	* typecheck/rust-hir-dot-operator.h: New.
	* typecheck/rust-hir-inherent-impl-overlap.h: New.
	* typecheck/rust-hir-path-probe.h: New.
	* typecheck/rust-hir-trait-ref.h: New.
	* typecheck/rust-hir-type-bounds.h: New.
	* typecheck/rust-substitution-mapper.cc: New.
	* typecheck/rust-substitution-mapper.h: New.
	* typecheck/rust-tycheck-dump.h: New.
	* typecheck/rust-tyctx.cc: New.
	* typecheck/rust-tyty-bounds.cc: New.
	* typecheck/rust-tyty-call.cc: New.
	* typecheck/rust-tyty-call.h: New.
	* typecheck/rust-tyty-cmp.h: New.
	* typecheck/rust-tyty-rules.h: New.
This commit is contained in:
Philip Herron 2022-10-21 13:55:28 +02:00 committed by Arthur Cohen
parent 24393cb68f
commit 06688fe40a
21 changed files with 7865 additions and 0 deletions

View File

@ -0,0 +1,398 @@
// Copyright (C) 2020-2022 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-autoderef.h"
#include "rust-hir-path-probe.h"
#include "rust-hir-dot-operator.h"
#include "rust-hir-trait-resolve.h"
namespace Rust {
namespace Resolver {
static bool
resolve_operator_overload_fn (
Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
Adjustment::AdjustmentType *requires_ref_adjustment);
TyTy::BaseType *
Adjuster::adjust_type (const std::vector<Adjustment> &adjustments)
{
if (adjustments.size () == 0)
return base->clone ();
return adjustments.back ().get_expected ()->clone ();
}
Adjustment
Adjuster::try_deref_type (const TyTy::BaseType *ty,
Analysis::RustLangItem::ItemType deref_lang_item)
{
HIR::ImplItem *impl_item = nullptr;
TyTy::FnType *fn = nullptr;
Adjustment::AdjustmentType requires_ref_adjustment
= Adjustment::AdjustmentType::ERROR;
bool operator_overloaded
= resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item,
&requires_ref_adjustment);
if (!operator_overloaded)
{
return Adjustment::get_error ();
}
auto resolved_base = fn->get_return_type ()->clone ();
bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF;
if (!is_valid_type)
return Adjustment::get_error ();
TyTy::ReferenceType *ref_base
= static_cast<TyTy::ReferenceType *> (resolved_base);
Adjustment::AdjustmentType adjustment_type
= Adjustment::AdjustmentType::ERROR;
switch (deref_lang_item)
{
case Analysis::RustLangItem::ItemType::DEREF:
adjustment_type = Adjustment::AdjustmentType::DEREF;
break;
case Analysis::RustLangItem::ItemType::DEREF_MUT:
adjustment_type = Adjustment::AdjustmentType::DEREF_MUT;
break;
default:
break;
}
return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty,
ref_base, fn, impl_item,
requires_ref_adjustment);
}
Adjustment
Adjuster::try_raw_deref_type (const TyTy::BaseType *ty)
{
bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF;
if (!is_valid_type)
return Adjustment::get_error ();
const TyTy::ReferenceType *ref_base
= static_cast<const TyTy::ReferenceType *> (ty);
auto infered = ref_base->get_base ()->clone ();
return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
}
Adjustment
Adjuster::try_unsize_type (const TyTy::BaseType *ty)
{
bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY;
if (!is_valid_type)
return Adjustment::get_error ();
auto mappings = Analysis::Mappings::get ();
auto context = TypeCheckContext::get ();
const auto ref_base = static_cast<const TyTy::ArrayType *> (ty);
auto slice_elem = ref_base->get_element_type ();
auto slice
= new TyTy::SliceType (mappings->get_next_hir_id (), ty->get_ident ().locus,
TyTy::TyVar (slice_elem->get_ref ()));
context->insert_implicit_type (slice);
return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice);
}
static bool
resolve_operator_overload_fn (
Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
Adjustment::AdjustmentType *requires_ref_adjustment)
{
auto context = TypeCheckContext::get ();
auto mappings = Analysis::Mappings::get ();
// look up lang item for arithmetic type
std::string associated_item_name
= Analysis::RustLangItem::ToString (lang_item_type);
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
if (!lang_item_defined)
return false;
auto segment = HIR::PathIdentSegment (associated_item_name);
auto candidate
= MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name),
true);
bool have_implementation_for_lang_item = !candidate.is_error ();
if (!have_implementation_for_lang_item)
return false;
// Get the adjusted self
Adjuster adj (ty);
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
// is this the case we are recursive
// handle the case where we are within the impl block for this
// lang_item otherwise we end up with a recursive operator overload
// such as the i32 operator overload trait
TypeCheckContextItem &fn_context = context->peek_context ();
if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
{
auto &impl_item = fn_context.get_impl_item ();
HIR::ImplBlock *parent = impl_item.first;
HIR::Function *fn = impl_item.second;
if (parent->has_trait_ref ()
&& fn->get_function_name ().compare (associated_item_name) == 0)
{
TraitReference *trait_reference
= TraitResolver::Lookup (*parent->get_trait_ref ().get ());
if (!trait_reference->is_error ())
{
TyTy::BaseType *lookup = nullptr;
bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
&lookup);
rust_assert (ok);
rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
rust_assert (fntype->is_method ());
bool is_lang_item_impl
= trait_reference->get_mappings ().get_defid ()
== respective_lang_item_id;
bool self_is_lang_item_self
= fntype->get_self_type ()->is_equal (*adjusted_self);
bool recursive_operator_overload
= is_lang_item_impl && self_is_lang_item_self;
if (recursive_operator_overload)
return false;
}
}
}
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
// rust only support impl item deref operator overloading ie you must have an
// impl block for it
rust_assert (candidate.candidate.type
== PathProbeCandidate::CandidateType::IMPL_FUNC);
*impl_item = candidate.candidate.item.impl.impl_item;
rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::BaseType *lookup = lookup_tyty;
TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
rust_assert (fn->is_method ());
if (fn->needs_substitution ())
{
if (ty->get_kind () == TyTy::TypeKind::ADT)
{
const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
auto s = fn->get_self_type ()->get_root ();
rust_assert (s->can_eq (adt, false));
rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
const TyTy::ADTType *self_adt
= static_cast<const TyTy::ADTType *> (s);
// we need to grab the Self substitutions as the inherit type
// parameters for this
if (self_adt->needs_substitution ())
{
rust_assert (adt->was_substituted ());
TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
= GetUsedSubstArgs::From (adt);
TyTy::SubstitutionArgumentMappings inherit_type_args
= self_adt->solve_mappings_from_receiver_for_self (
used_args_in_prev_segment);
// there may or may not be inherited type arguments
if (!inherit_type_args.is_error ())
{
// need to apply the inherited type arguments to the
// function
lookup = fn->handle_substitions (inherit_type_args);
}
}
}
else
{
rust_assert (candidate.adjustments.size () < 2);
// lets infer the params for this we could probably fix this up by
// actually just performing a substitution of a single param but this
// seems more generic i think.
//
// this is the case where we had say Foo<&Bar>> and we have derefed to
// the &Bar and we are trying to match a method self of Bar which
// requires another deref which is matched to the deref trait impl of
// &&T so this requires another reference and deref call
lookup = fn->infer_substitions (Location ());
rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
fn = static_cast<TyTy::FnType *> (lookup);
fn->get_self_type ()->unify (adjusted_self);
lookup = fn;
}
}
if (candidate.adjustments.size () > 0)
*requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
*resolved_fn = static_cast<TyTy::FnType *> (lookup);
return true;
}
AutoderefCycle::AutoderefCycle (bool autoderef_flag)
: autoderef_flag (autoderef_flag)
{}
AutoderefCycle::~AutoderefCycle () {}
void
AutoderefCycle::try_hook (const TyTy::BaseType &)
{}
bool
AutoderefCycle::cycle (const TyTy::BaseType *receiver)
{
const TyTy::BaseType *r = receiver;
while (true)
{
if (try_autoderefed (r))
return true;
// 4. deref to to 1, if cannot deref then quit
if (autoderef_flag)
return false;
// try unsize
Adjustment unsize = Adjuster::try_unsize_type (r);
if (!unsize.is_error ())
{
adjustments.push_back (unsize);
auto unsize_r = unsize.get_expected ();
if (try_autoderefed (unsize_r))
return true;
adjustments.pop_back ();
}
Adjustment deref
= Adjuster::try_deref_type (r, Analysis::RustLangItem::ItemType::DEREF);
if (!deref.is_error ())
{
auto deref_r = deref.get_expected ();
adjustments.push_back (deref);
if (try_autoderefed (deref_r))
return true;
adjustments.pop_back ();
}
Adjustment deref_mut = Adjuster::try_deref_type (
r, Analysis::RustLangItem::ItemType::DEREF_MUT);
if (!deref_mut.is_error ())
{
auto deref_r = deref_mut.get_expected ();
adjustments.push_back (deref_mut);
if (try_autoderefed (deref_r))
return true;
adjustments.pop_back ();
}
if (!deref_mut.is_error ())
{
auto deref_r = deref_mut.get_expected ();
adjustments.push_back (deref_mut);
Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
adjustments.push_back (raw_deref);
deref_r = raw_deref.get_expected ();
if (try_autoderefed (deref_r))
return true;
adjustments.pop_back ();
adjustments.pop_back ();
}
if (!deref.is_error ())
{
r = deref.get_expected ();
adjustments.push_back (deref);
}
Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
if (raw_deref.is_error ())
return false;
r = raw_deref.get_expected ();
adjustments.push_back (raw_deref);
}
return false;
}
bool
AutoderefCycle::try_autoderefed (const TyTy::BaseType *r)
{
try_hook (*r);
// 1. try raw
if (select (*r))
return true;
// 2. try ref
TyTy::ReferenceType *r1
= new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
Mutability::Imm);
adjustments.push_back (
Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1));
if (select (*r1))
return true;
adjustments.pop_back ();
// 3. try mut ref
TyTy::ReferenceType *r2
= new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
Mutability::Mut);
adjustments.push_back (
Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2));
if (select (*r2))
return true;
adjustments.pop_back ();
return false;
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,178 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_AUTODEREF
#define RUST_AUTODEREF
#include "rust-tyty.h"
namespace Rust {
namespace Resolver {
class Adjustment
{
public:
enum AdjustmentType
{
ERROR,
IMM_REF,
MUT_REF,
DEREF,
DEREF_MUT,
INDIRECTION,
UNSIZE,
};
// ctor for all adjustments except derefs
Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
const TyTy::BaseType *expected)
: Adjustment (type, actual, expected, nullptr, nullptr,
AdjustmentType::ERROR)
{}
static Adjustment get_op_overload_deref_adjustment (
AdjustmentType type, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, TyTy::FnType *fn, HIR::ImplItem *deref_item,
Adjustment::AdjustmentType requires_ref_adjustment)
{
rust_assert (type == DEREF || type == DEREF_MUT);
return Adjustment (type, actual, expected, fn, deref_item,
requires_ref_adjustment);
}
AdjustmentType get_type () const { return type; }
const TyTy::BaseType *get_actual () const { return actual; }
const TyTy::BaseType *get_expected () const { return expected; }
std::string as_string () const
{
return Adjustment::type_string (get_type ()) + "->"
+ get_expected ()->debug_str ();
}
static std::string type_string (AdjustmentType type)
{
switch (type)
{
case AdjustmentType::ERROR:
return "ERROR";
case AdjustmentType::IMM_REF:
return "IMM_REF";
case AdjustmentType::MUT_REF:
return "MUT_REF";
case AdjustmentType::DEREF:
return "DEREF";
case AdjustmentType::DEREF_MUT:
return "DEREF_MUT";
case AdjustmentType::INDIRECTION:
return "INDIRECTION";
case AdjustmentType::UNSIZE:
return "UNSIZE";
}
gcc_unreachable ();
return "";
}
static Adjustment get_error () { return Adjustment{ERROR, nullptr, nullptr}; }
bool is_error () const { return type == ERROR; }
bool is_deref_adjustment () const { return type == DEREF; }
bool is_deref_mut_adjustment () const { return type == DEREF_MUT; }
bool has_operator_overload () const { return deref_operator_fn != nullptr; }
TyTy::FnType *get_deref_operator_fn () const { return deref_operator_fn; }
AdjustmentType get_deref_adjustment_type () const
{
return requires_ref_adjustment;
}
HIR::ImplItem *get_deref_hir_item () const { return deref_item; }
private:
Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn,
HIR::ImplItem *deref_item,
Adjustment::AdjustmentType requires_ref_adjustment)
: type (type), actual (actual), expected (expected),
deref_operator_fn (deref_operator_fn), deref_item (deref_item),
requires_ref_adjustment (requires_ref_adjustment)
{}
AdjustmentType type;
const TyTy::BaseType *actual;
const TyTy::BaseType *expected;
// - only used for deref operator_overloads
//
// the fn that we are calling
TyTy::FnType *deref_operator_fn;
HIR::ImplItem *deref_item;
// operator overloads can requre a reference
Adjustment::AdjustmentType requires_ref_adjustment;
};
class Adjuster
{
public:
Adjuster (const TyTy::BaseType *ty) : base (ty) {}
TyTy::BaseType *adjust_type (const std::vector<Adjustment> &adjustments);
static Adjustment
try_deref_type (const TyTy::BaseType *ty,
Analysis::RustLangItem::ItemType deref_lang_item);
static Adjustment try_raw_deref_type (const TyTy::BaseType *ty);
static Adjustment try_unsize_type (const TyTy::BaseType *ty);
private:
const TyTy::BaseType *base;
};
class AutoderefCycle
{
protected:
AutoderefCycle (bool autoderef_flag);
virtual ~AutoderefCycle ();
virtual bool select (const TyTy::BaseType &autoderefed) = 0;
// optional: this is a chance to hook in to grab predicate items on the raw
// type
virtual void try_hook (const TyTy::BaseType &);
virtual bool cycle (const TyTy::BaseType *receiver);
bool try_autoderefed (const TyTy::BaseType *r);
bool autoderef_flag;
std::vector<Adjustment> adjustments;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_AUTODEREF

View File

@ -0,0 +1,292 @@
// Copyright (C) 2020-2022 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-casts.h"
namespace Rust {
namespace Resolver {
TypeCastRules::TypeCastRules (Location locus, TyTy::TyWithLocation from,
TyTy::TyWithLocation to)
: locus (locus), from (from), to (to)
{}
TypeCoercionRules::CoercionResult
TypeCastRules::resolve (Location locus, TyTy::TyWithLocation from,
TyTy::TyWithLocation to)
{
TypeCastRules cast_rules (locus, from, to);
return cast_rules.check ();
}
TypeCoercionRules::CoercionResult
TypeCastRules::check ()
{
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582
auto possible_coercion
= TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus);
if (!possible_coercion.is_error ())
return possible_coercion;
// try the simple cast rules
auto simple_cast = cast_rules ();
if (!simple_cast.is_error ())
return simple_cast;
// failed to cast
emit_cast_error ();
return TypeCoercionRules::CoercionResult::get_error ();
}
TypeCoercionRules::CoercionResult
TypeCastRules::cast_rules ()
{
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654
rust_debug ("cast_rules from={%s} to={%s}",
from.get_ty ()->debug_str ().c_str (),
to.get_ty ()->debug_str ().c_str ());
switch (from.get_ty ()->get_kind ())
{
case TyTy::TypeKind::INFER: {
TyTy::InferType *from_infer
= static_cast<TyTy::InferType *> (from.get_ty ());
switch (from_infer->get_infer_kind ())
{
case TyTy::InferType::InferTypeKind::GENERAL:
return TypeCoercionRules::CoercionResult{{},
to.get_ty ()->clone ()};
case TyTy::InferType::InferTypeKind::INTEGRAL:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::CHAR:
case TyTy::TypeKind::BOOL:
case TyTy::TypeKind::USIZE:
case TyTy::TypeKind::ISIZE:
case TyTy::TypeKind::UINT:
case TyTy::TypeKind::INT:
case TyTy::TypeKind::POINTER:
return TypeCoercionRules::CoercionResult{
{}, to.get_ty ()->clone ()};
case TyTy::TypeKind::INFER: {
TyTy::InferType *to_infer
= static_cast<TyTy::InferType *> (to.get_ty ());
switch (to_infer->get_infer_kind ())
{
case TyTy::InferType::InferTypeKind::GENERAL:
case TyTy::InferType::InferTypeKind::INTEGRAL:
return TypeCoercionRules::CoercionResult{
{}, to.get_ty ()->clone ()};
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
}
break;
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
case TyTy::InferType::InferTypeKind::FLOAT:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::USIZE:
case TyTy::TypeKind::ISIZE:
case TyTy::TypeKind::UINT:
case TyTy::TypeKind::INT:
return TypeCoercionRules::CoercionResult{
{}, to.get_ty ()->clone ()};
case TyTy::TypeKind::INFER: {
TyTy::InferType *to_infer
= static_cast<TyTy::InferType *> (to.get_ty ());
switch (to_infer->get_infer_kind ())
{
case TyTy::InferType::InferTypeKind::GENERAL:
case TyTy::InferType::InferTypeKind::FLOAT:
return TypeCoercionRules::CoercionResult{
{}, to.get_ty ()->clone ()};
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
}
break;
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
}
}
break;
case TyTy::TypeKind::BOOL:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::INFER:
case TyTy::TypeKind::USIZE:
case TyTy::TypeKind::ISIZE:
case TyTy::TypeKind::UINT:
case TyTy::TypeKind::INT:
return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
case TyTy::TypeKind::CHAR:
case TyTy::TypeKind::USIZE:
case TyTy::TypeKind::ISIZE:
case TyTy::TypeKind::UINT:
case TyTy::TypeKind::INT:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::CHAR: {
// only u8 and char
bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT;
bool was_u8 = was_uint
&& (static_cast<TyTy::UintType *> (from.get_ty ())
->get_uint_kind ()
== TyTy::UintType::UintKind::U8);
if (was_u8)
return TypeCoercionRules::CoercionResult{{},
to.get_ty ()->clone ()};
}
break;
case TyTy::TypeKind::INFER:
case TyTy::TypeKind::USIZE:
case TyTy::TypeKind::ISIZE:
case TyTy::TypeKind::UINT:
case TyTy::TypeKind::INT:
return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
case TyTy::TypeKind::FLOAT:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::FLOAT:
return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
case TyTy::TypeKind::INFER: {
TyTy::InferType *to_infer
= static_cast<TyTy::InferType *> (to.get_ty ());
switch (to_infer->get_infer_kind ())
{
case TyTy::InferType::InferTypeKind::GENERAL:
case TyTy::InferType::InferTypeKind::FLOAT:
return TypeCoercionRules::CoercionResult{
{}, to.get_ty ()->clone ()};
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
}
break;
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
case TyTy::TypeKind::REF:
case TyTy::TypeKind::POINTER:
switch (to.get_ty ()->get_kind ())
{
case TyTy::TypeKind::REF:
case TyTy::TypeKind::POINTER:
return check_ptr_ptr_cast ();
// FIXME can you cast a pointer to a integral type?
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
break;
default:
return TypeCoercionRules::CoercionResult::get_error ();
}
return TypeCoercionRules::CoercionResult::get_error ();
}
TypeCoercionRules::CoercionResult
TypeCastRules::check_ptr_ptr_cast ()
{
rust_debug ("check_ptr_ptr_cast from={%s} to={%s}",
from.get_ty ()->debug_str ().c_str (),
to.get_ty ()->debug_str ().c_str ());
bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF;
bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF;
bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
if (from_is_ptr && to_is_ptr)
{
// mutability is ignored here as all pointer usage requires unsafe
return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
}
else if (from_is_ref && to_is_ref)
{
// mutability must be coercedable
TyTy::ReferenceType &f
= static_cast<TyTy::ReferenceType &> (*from.get_ty ());
TyTy::ReferenceType &t
= static_cast<TyTy::ReferenceType &> (*to.get_ty ());
if (TypeCoercionRules::coerceable_mutability (f.mutability (),
t.mutability ()))
{
return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
}
}
return TypeCoercionRules::CoercionResult::get_error ();
}
void
TypeCastRules::emit_cast_error () const
{
// error[E0604]
RichLocation r (locus);
r.add_range (from.get_locus ());
r.add_range (to.get_locus ());
rust_error_at (r, "invalid cast %<%s%> to %<%s%>",
from.get_ty ()->get_name ().c_str (),
to.get_ty ()->get_name ().c_str ());
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,53 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_CASTS
#define RUST_CASTS
#include "rust-tyty.h"
#include "rust-coercion.h"
namespace Rust {
namespace Resolver {
class TypeCastRules
{
public:
static TypeCoercionRules::CoercionResult
resolve (Location locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to);
protected:
TypeCoercionRules::CoercionResult check ();
TypeCoercionRules::CoercionResult cast_rules ();
TypeCoercionRules::CoercionResult check_ptr_ptr_cast ();
void emit_cast_error () const;
protected:
TypeCastRules (Location locus, TyTy::TyWithLocation from,
TyTy::TyWithLocation to);
Location locus;
TyTy::TyWithLocation from;
TyTy::TyWithLocation to;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_CASTS

View File

@ -0,0 +1,357 @@
// Copyright (C) 2020-2022 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-coercion.h"
namespace Rust {
namespace Resolver {
TypeCoercionRules::CoercionResult
TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
Location locus)
{
TypeCoercionRules resolver (expected, locus, true);
bool ok = resolver.do_coercion (receiver);
return ok ? resolver.try_result : CoercionResult::get_error ();
}
TypeCoercionRules::CoercionResult
TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus)
{
TypeCoercionRules resolver (expected, locus, false);
bool ok = resolver.do_coercion (receiver);
return ok ? resolver.try_result : CoercionResult::get_error ();
}
TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected, Location locus,
bool emit_errors)
: AutoderefCycle (false), mappings (Analysis::Mappings::get ()),
context (TypeCheckContext::get ()), expected (expected), locus (locus),
try_result (CoercionResult::get_error ()), emit_errors (emit_errors)
{}
bool
TypeCoercionRules::do_coercion (TyTy::BaseType *receiver)
{
// FIXME this is not finished and might be super simplified
// see:
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
// unsize
bool unsafe_error = false;
CoercionResult unsize_coercion
= coerce_unsized (receiver, expected, unsafe_error);
bool valid_unsize_coercion = !unsize_coercion.is_error ();
if (valid_unsize_coercion)
{
try_result = unsize_coercion;
return true;
}
else if (unsafe_error)
{
// Location lhs = mappings->lookup_location (receiver->get_ref ());
// Location rhs = mappings->lookup_location (expected->get_ref ());
// object_unsafe_error (locus, lhs, rhs);
return false;
}
// pointers
switch (expected->get_kind ())
{
case TyTy::TypeKind::POINTER: {
TyTy::PointerType *ptr = static_cast<TyTy::PointerType *> (expected);
try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
return !try_result.is_error ();
}
case TyTy::TypeKind::REF: {
TyTy::ReferenceType *ptr
= static_cast<TyTy::ReferenceType *> (expected);
try_result
= coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
return !try_result.is_error ();
}
break;
default:
break;
}
return !try_result.is_error ();
}
TypeCoercionRules::CoercionResult
TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
TyTy::PointerType *expected,
Mutability to_mutbl)
{
rust_debug ("coerce_unsafe_ptr(a={%s}, b={%s})",
receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
Mutability from_mutbl = Mutability::Imm;
TyTy::BaseType *element = nullptr;
switch (receiver->get_kind ())
{
case TyTy::TypeKind::REF: {
TyTy::ReferenceType *ref
= static_cast<TyTy::ReferenceType *> (receiver);
from_mutbl = ref->mutability ();
element = ref->get_base ();
}
break;
case TyTy::TypeKind::POINTER: {
TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
from_mutbl = ref->mutability ();
element = ref->get_base ();
}
break;
default: {
if (receiver->can_eq (expected, false))
return CoercionResult{{}, expected->clone ()};
return CoercionResult::get_error ();
}
}
if (!coerceable_mutability (from_mutbl, to_mutbl))
{
Location lhs = mappings->lookup_location (receiver->get_ref ());
Location rhs = mappings->lookup_location (expected->get_ref ());
mismatched_mutability_error (locus, lhs, rhs);
return TypeCoercionRules::CoercionResult::get_error ();
}
TyTy::PointerType *result
= new TyTy::PointerType (receiver->get_ref (),
TyTy::TyVar (element->get_ref ()), to_mutbl);
if (!result->can_eq (expected, false))
return CoercionResult::get_error ();
return CoercionResult{{}, result};
}
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary.
TypeCoercionRules::CoercionResult
TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
TyTy::ReferenceType *expected,
Mutability to_mutbl)
{
rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
Mutability from_mutbl = Mutability::Imm;
switch (receiver->get_kind ())
{
case TyTy::TypeKind::REF: {
TyTy::ReferenceType *ref
= static_cast<TyTy::ReferenceType *> (receiver);
from_mutbl = ref->mutability ();
}
break;
default: {
TyTy::BaseType *result = receiver->unify (expected);
return CoercionResult{{}, result};
}
}
if (!coerceable_mutability (from_mutbl, to_mutbl))
{
Location lhs = mappings->lookup_location (receiver->get_ref ());
Location rhs = mappings->lookup_location (expected->get_ref ());
mismatched_mutability_error (locus, lhs, rhs);
return TypeCoercionRules::CoercionResult::get_error ();
}
AutoderefCycle::cycle (receiver);
return try_result;
}
// &[T; n] or &mut [T; n] -> &[T]
// or &mut [T; n] -> &mut [T]
// or &Concrete -> &Trait, etc.
TypeCoercionRules::CoercionResult
TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
TyTy::BaseType *target, bool &unsafe_error)
{
rust_debug ("coerce_unsized(source={%s}, target={%s})",
source->debug_str ().c_str (), target->debug_str ().c_str ());
bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
bool needs_reborrow = false;
TyTy::BaseType *ty_a = source;
TyTy::BaseType *ty_b = target;
Mutability expected_mutability = Mutability::Imm;
if (source_is_ref && target_is_ref)
{
TyTy::ReferenceType *source_ref
= static_cast<TyTy::ReferenceType *> (source);
TyTy::ReferenceType *target_ref
= static_cast<TyTy::ReferenceType *> (target);
Mutability from_mutbl = source_ref->mutability ();
Mutability to_mutbl = target_ref->mutability ();
if (!coerceable_mutability (from_mutbl, to_mutbl))
{
unsafe_error = true;
Location lhs = mappings->lookup_location (source->get_ref ());
Location rhs = mappings->lookup_location (target->get_ref ());
mismatched_mutability_error (locus, lhs, rhs);
return TypeCoercionRules::CoercionResult::get_error ();
}
ty_a = source_ref->get_base ();
ty_b = target_ref->get_base ();
needs_reborrow = true;
expected_mutability = to_mutbl;
adjustments.push_back (
Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
}
else if (source_is_ref && target_is_ptr)
{
TyTy::ReferenceType *source_ref
= static_cast<TyTy::ReferenceType *> (source);
TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
Mutability from_mutbl = source_ref->mutability ();
Mutability to_mutbl = target_ref->mutability ();
if (!coerceable_mutability (from_mutbl, to_mutbl))
{
unsafe_error = true;
Location lhs = mappings->lookup_location (source->get_ref ());
Location rhs = mappings->lookup_location (target->get_ref ());
mismatched_mutability_error (locus, lhs, rhs);
return TypeCoercionRules::CoercionResult::get_error ();
}
ty_a = source_ref->get_base ();
ty_b = target_ref->get_base ();
needs_reborrow = true;
expected_mutability = to_mutbl;
adjustments.push_back (
Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
}
// FIXME
// there is a bunch of code to ensure something is coerce able to a dyn trait
// we need to support but we need to support a few more lang items for that
// see:
// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
const auto a = ty_a;
const auto b = ty_b;
bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
if (expect_dyn && need_unsize)
{
bool bounds_compatible = b->bounds_compatible (*a, locus, true);
if (!bounds_compatible)
{
unsafe_error = true;
return TypeCoercionRules::CoercionResult::get_error ();
}
// return the unsize coercion
TyTy::BaseType *result = b->clone ();
// result->set_ref (a->get_ref ());
// append a dyn coercion adjustment
adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result));
// reborrow if needed
if (needs_reborrow)
{
TyTy::ReferenceType *reborrow
= new TyTy::ReferenceType (source->get_ref (),
TyTy::TyVar (result->get_ref ()),
expected_mutability);
Adjustment::AdjustmentType borrow_type
= expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
: Adjustment::MUT_REF;
adjustments.push_back (Adjustment (borrow_type, result, reborrow));
result = reborrow;
}
return CoercionResult{adjustments, result};
}
adjustments.clear ();
return TypeCoercionRules::CoercionResult::get_error ();
}
bool
TypeCoercionRules::select (const TyTy::BaseType &autoderefed)
{
if (autoderefed.can_eq (expected, false))
{
try_result = CoercionResult{adjustments, autoderefed.clone ()};
return true;
}
return false;
}
/// Coercing a mutable reference to an immutable works, while
/// coercing `&T` to `&mut T` should be forbidden.
bool
TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
Mutability to_mutbl)
{
return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl);
}
void
TypeCoercionRules::mismatched_mutability_error (Location expr_locus,
Location lhs, Location rhs)
{
if (!emit_errors)
return;
RichLocation r (expr_locus);
r.add_range (lhs);
r.add_range (rhs);
rust_error_at (r, "mismatched mutability");
}
void
TypeCoercionRules::object_unsafe_error (Location expr_locus, Location lhs,
Location rhs)
{
if (!emit_errors)
return;
RichLocation r (expr_locus);
r.add_range (lhs);
r.add_range (rhs);
rust_error_at (r, "unsafe unsize coercion");
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,93 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_COERCION
#define RUST_COERCION
#include "rust-autoderef.h"
#include "rust-hir-type-check.h"
namespace Rust {
namespace Resolver {
class TypeCoercionRules : protected AutoderefCycle
{
public:
struct CoercionResult
{
std::vector<Adjustment> adjustments;
TyTy::BaseType *tyty;
bool is_error ()
{
return tyty == nullptr || tyty->get_kind () == TyTy::TypeKind::ERROR;
}
static CoercionResult get_error () { return CoercionResult{{}, nullptr}; }
};
static CoercionResult Coerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus);
static CoercionResult TryCoerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus);
CoercionResult coerce_unsafe_ptr (TyTy::BaseType *receiver,
TyTy::PointerType *expected,
Mutability mutability);
CoercionResult coerce_borrowed_pointer (TyTy::BaseType *receiver,
TyTy::ReferenceType *expected,
Mutability mutability);
CoercionResult coerce_unsized (TyTy::BaseType *receiver,
TyTy::BaseType *expected, bool &unsafe_error);
static bool coerceable_mutability (Mutability from_mutbl,
Mutability to_mutbl);
void mismatched_mutability_error (Location expr_locus, Location lhs,
Location rhs);
void object_unsafe_error (Location expr_locus, Location lhs, Location rhs);
protected:
TypeCoercionRules (TyTy::BaseType *expected, Location locus,
bool emit_errors);
bool select (const TyTy::BaseType &autoderefed) override;
bool do_coercion (TyTy::BaseType *receiver);
private:
// context info
Analysis::Mappings *mappings;
TypeCheckContext *context;
// search
TyTy::BaseType *expected;
Location locus;
// mutable fields
CoercionResult try_result;
bool emit_errors;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_COERCION

View File

@ -0,0 +1,263 @@
// Copyright (C) 2020-2022 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-hir-dot-operator.h"
#include "rust-hir-path-probe.h"
#include "rust-hir-trait-resolve.h"
namespace Rust {
namespace Resolver {
MethodResolver::MethodResolver (bool autoderef_flag,
const HIR::PathIdentSegment &segment_name)
: AutoderefCycle (autoderef_flag), mappings (Analysis::Mappings::get ()),
context (TypeCheckContext::get ()), segment_name (segment_name),
try_result (MethodCandidate::get_error ())
{}
MethodCandidate
MethodResolver::Probe (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &segment_name,
bool autoderef_flag)
{
MethodResolver resolver (autoderef_flag, segment_name);
bool ok = resolver.cycle (receiver);
return ok ? resolver.try_result : MethodCandidate::get_error ();
}
void
MethodResolver::try_hook (const TyTy::BaseType &r)
{
const auto &specified_bounds = r.get_specified_bounds ();
predicate_items = get_predicate_items (segment_name, r, specified_bounds);
}
bool
MethodResolver::select (const TyTy::BaseType &receiver)
{
struct impl_item_candidate
{
HIR::Function *item;
HIR::ImplBlock *impl_block;
TyTy::FnType *ty;
};
// assemble inherent impl items
std::vector<impl_item_candidate> inherent_impl_fns;
mappings->iterate_impl_items (
[&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
bool is_trait_impl = impl->has_trait_ref ();
if (is_trait_impl)
return true;
bool is_fn
= item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
if (!is_fn)
return true;
HIR::Function *func = static_cast<HIR::Function *> (item);
if (!func->is_method ())
return true;
bool name_matches
= func->get_function_name ().compare (segment_name.as_string ()) == 0;
if (!name_matches)
return true;
TyTy::BaseType *ty = nullptr;
if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
return true;
if (ty->get_kind () == TyTy::TypeKind::ERROR)
return true;
rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
inherent_impl_fns.push_back ({func, impl, fnty});
return true;
});
struct trait_item_candidate
{
const HIR::TraitItemFunc *item;
const HIR::Trait *trait;
TyTy::FnType *ty;
const TraitReference *reference;
const TraitItemReference *item_ref;
};
std::vector<trait_item_candidate> trait_fns;
mappings->iterate_impl_blocks ([&] (HirId id,
HIR::ImplBlock *impl) mutable -> bool {
bool is_trait_impl = impl->has_trait_ref ();
if (!is_trait_impl)
return true;
// look for impl implementation else lookup the associated trait item
for (auto &impl_item : impl->get_impl_items ())
{
bool is_fn = impl_item->get_impl_item_type ()
== HIR::ImplItem::ImplItemType::FUNCTION;
if (!is_fn)
continue;
HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
if (!func->is_method ())
continue;
bool name_matches
= func->get_function_name ().compare (segment_name.as_string ()) == 0;
if (!name_matches)
continue;
TyTy::BaseType *ty = nullptr;
if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
continue;
if (ty->get_kind () == TyTy::TypeKind::ERROR)
continue;
rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
inherent_impl_fns.push_back ({func, impl, fnty});
return true;
}
TraitReference *trait_ref
= TraitResolver::Resolve (*impl->get_trait_ref ().get ());
rust_assert (!trait_ref->is_error ());
auto item_ref
= trait_ref->lookup_trait_item (segment_name.as_string (),
TraitItemReference::TraitItemType::FN);
if (item_ref->is_error ())
return true;
const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
HIR::TraitItem *item = item_ref->get_hir_trait_item ();
rust_assert (item->get_item_kind () == HIR::TraitItem::TraitItemKind::FUNC);
HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
TyTy::BaseType *ty = item_ref->get_tyty ();
rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
trait_fns.push_back (candidate);
return true;
});
// lookup specified bounds for an associated item
struct precdicate_candidate
{
TyTy::TypeBoundPredicateItem lookup;
TyTy::FnType *fntype;
};
for (auto impl_item : inherent_impl_fns)
{
TyTy::FnType *fn = impl_item.ty;
rust_assert (fn->is_method ());
TyTy::BaseType *fn_self = fn->get_self_type ();
if (fn_self->can_eq (&receiver, false))
{
PathProbeCandidate::ImplItemCandidate c{impl_item.item,
impl_item.impl_block};
try_result = MethodCandidate{
PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
fn, impl_item.item->get_locus (), c),
adjustments};
return true;
}
}
for (auto trait_item : trait_fns)
{
TyTy::FnType *fn = trait_item.ty;
rust_assert (fn->is_method ());
TyTy::BaseType *fn_self = fn->get_self_type ();
if (fn_self->can_eq (&receiver, false))
{
PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
trait_item.item_ref,
nullptr};
try_result = MethodCandidate{
PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
fn, trait_item.item->get_locus (), c),
adjustments};
return true;
}
}
for (const auto &predicate : predicate_items)
{
const TyTy::FnType *fn = predicate.fntype;
rust_assert (fn->is_method ());
TyTy::BaseType *fn_self = fn->get_self_type ();
if (fn_self->can_eq (&receiver, false))
{
const TraitReference *trait_ref
= predicate.lookup.get_parent ()->get ();
const TraitItemReference *trait_item
= predicate.lookup.get_raw_item ();
PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
nullptr};
try_result = MethodCandidate{
PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
fn->clone (), trait_item->get_locus (), c),
adjustments};
return true;
}
}
return false;
}
std::vector<MethodResolver::predicate_candidate>
MethodResolver::get_predicate_items (
const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
{
std::vector<predicate_candidate> predicate_items;
for (auto &bound : specified_bounds)
{
TyTy::TypeBoundPredicateItem lookup
= bound.lookup_associated_item (segment_name.as_string ());
if (lookup.is_error ())
continue;
TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
if (ty->get_kind () == TyTy::TypeKind::FNDEF)
{
TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
predicate_candidate candidate{lookup, fnty};
predicate_items.push_back (candidate);
}
}
return predicate_items;
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,81 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_HIR_DOT_OPERATOR
#define RUST_HIR_DOT_OPERATOR
#include "rust-hir-path-probe.h"
namespace Rust {
namespace Resolver {
struct MethodCandidate
{
PathProbeCandidate candidate;
std::vector<Adjustment> adjustments;
static MethodCandidate get_error ()
{
return {PathProbeCandidate::get_error (), {}};
}
bool is_error () const { return candidate.is_error (); }
};
class MethodResolver : protected AutoderefCycle
{
public:
struct predicate_candidate
{
TyTy::TypeBoundPredicateItem lookup;
TyTy::FnType *fntype;
};
static MethodCandidate Probe (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &segment_name,
bool autoderef_flag = false);
static std::vector<predicate_candidate> get_predicate_items (
const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);
protected:
MethodResolver (bool autoderef_flag,
const HIR::PathIdentSegment &segment_name);
void try_hook (const TyTy::BaseType &r) override;
bool select (const TyTy::BaseType &receiver) override;
private:
// context info
Analysis::Mappings *mappings;
TypeCheckContext *context;
// search
const HIR::PathIdentSegment &segment_name;
std::vector<MethodResolver::predicate_candidate> predicate_items;
// mutable fields
MethodCandidate try_result;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_HIR_DOT_OPERATOR

View File

@ -0,0 +1,186 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
#define RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
namespace Rust {
namespace Resolver {
class ImplItemToName : private TypeCheckBase, private HIR::HIRImplVisitor
{
public:
static bool resolve (HIR::ImplItem *item, std::string &name_result)
{
ImplItemToName resolver (name_result);
item->accept_vis (resolver);
return resolver.ok;
}
void visit (HIR::TypeAlias &alias) override
{
ok = true;
result.assign (alias.get_new_type_name ());
}
void visit (HIR::Function &function) override
{
ok = true;
result.assign (function.get_function_name ());
}
void visit (HIR::ConstantItem &constant) override
{
ok = true;
result.assign (constant.get_identifier ());
}
private:
ImplItemToName (std::string &result)
: TypeCheckBase (), ok (false), result (result)
{}
bool ok;
std::string &result;
};
class OverlappingImplItemPass : public TypeCheckBase
{
public:
static void go ()
{
OverlappingImplItemPass pass;
// generate mappings
pass.mappings->iterate_impl_items (
[&] (HirId id, HIR::ImplItem *impl_item, HIR::ImplBlock *impl) -> bool {
// ignoring trait-impls might need thought later on
if (impl->has_trait_ref ())
return true;
pass.process_impl_item (id, impl_item, impl);
return true;
});
pass.scan ();
}
void process_impl_item (HirId id, HIR::ImplItem *impl_item,
HIR::ImplBlock *impl)
{
// lets make a mapping of impl-item Self type to (impl-item,name):
// {
// impl-type -> [ (item, name), ... ]
// }
HirId impl_type_id = impl->get_type ()->get_mappings ().get_hirid ();
TyTy::BaseType *impl_type = nullptr;
bool ok = context->lookup_type (impl_type_id, &impl_type);
rust_assert (ok);
std::string impl_item_name;
ok = ImplItemToName::resolve (impl_item, impl_item_name);
rust_assert (ok);
std::pair<HIR::ImplItem *, std::string> elem (impl_item, impl_item_name);
impl_mappings[impl_type].insert (std::move (elem));
}
void scan ()
{
// we can now brute force the map looking for can_eq on each of the
// impl_items_types to look for possible colliding impl blocks;
for (auto it = impl_mappings.begin (); it != impl_mappings.end (); it++)
{
TyTy::BaseType *query = it->first;
for (auto iy = impl_mappings.begin (); iy != impl_mappings.end (); iy++)
{
TyTy::BaseType *candidate = iy->first;
if (query == candidate)
continue;
if (query->can_eq (candidate, false))
{
// we might be in the case that we have:
//
// *const T vs *const [T]
//
// so lets use an equality check when the
// candidates are both generic to be sure we dont emit a false
// positive
bool a = query->is_concrete ();
bool b = candidate->is_concrete ();
bool both_generic = !a && !b;
if (both_generic)
{
if (!query->is_equal (*candidate))
continue;
}
possible_collision (it->second, iy->second);
}
}
}
}
void possible_collision (
std::set<std::pair<HIR::ImplItem *, std::string> > query,
std::set<std::pair<HIR::ImplItem *, std::string> > candidate)
{
for (auto &q : query)
{
HIR::ImplItem *query_impl_item = q.first;
std::string query_impl_item_name = q.second;
for (auto &c : candidate)
{
HIR::ImplItem *candidate_impl_item = c.first;
std::string candidate_impl_item_name = c.second;
if (query_impl_item_name.compare (candidate_impl_item_name) == 0)
collision_detected (query_impl_item, candidate_impl_item,
candidate_impl_item_name);
}
}
}
void collision_detected (HIR::ImplItem *query, HIR::ImplItem *dup,
const std::string &name)
{
RichLocation r (dup->get_locus ());
r.add_range (query->get_locus ());
rust_error_at (r, "duplicate definitions with name %s", name.c_str ());
}
private:
OverlappingImplItemPass () : TypeCheckBase () {}
std::map<TyTy::BaseType *,
std::set<std::pair<HIR::ImplItem *, std::string> > >
impl_mappings;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H

View File

@ -0,0 +1,540 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_HIR_PATH_PROBE_H
#define RUST_HIR_PATH_PROBE_H
#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
#include "rust-tyty.h"
#include "rust-substitution-mapper.h"
#include "rust-hir-type-bounds.h"
namespace Rust {
namespace Resolver {
struct PathProbeCandidate
{
enum CandidateType
{
ERROR,
ENUM_VARIANT,
IMPL_CONST,
IMPL_TYPE_ALIAS,
IMPL_FUNC,
TRAIT_ITEM_CONST,
TRAIT_TYPE_ALIAS,
TRAIT_FUNC,
};
struct EnumItemCandidate
{
const TyTy::ADTType *parent;
const TyTy::VariantDef *variant;
};
struct ImplItemCandidate
{
HIR::ImplItem *impl_item;
HIR::ImplBlock *parent;
};
struct TraitItemCandidate
{
const TraitReference *trait_ref;
const TraitItemReference *item_ref;
HIR::ImplBlock *impl;
};
CandidateType type;
TyTy::BaseType *ty;
Location locus;
union Candidate
{
EnumItemCandidate enum_field;
ImplItemCandidate impl;
TraitItemCandidate trait;
Candidate (EnumItemCandidate enum_field) : enum_field (enum_field) {}
Candidate (ImplItemCandidate impl) : impl (impl) {}
Candidate (TraitItemCandidate trait) : trait (trait) {}
} item;
PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
EnumItemCandidate enum_field)
: type (type), ty (ty), item (enum_field)
{}
PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
ImplItemCandidate impl)
: type (type), ty (ty), item (impl)
{}
PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
TraitItemCandidate trait)
: type (type), ty (ty), item (trait)
{}
std::string as_string () const
{
return "PathProbe candidate TODO - as_string";
}
bool is_enum_candidate () const { return type == ENUM_VARIANT; }
bool is_impl_candidate () const
{
return type == IMPL_CONST || type == IMPL_TYPE_ALIAS || type == IMPL_FUNC;
}
bool is_trait_candidate () const
{
return type == TRAIT_ITEM_CONST || type == TRAIT_TYPE_ALIAS
|| type == TRAIT_FUNC;
}
bool is_full_trait_item_candidate () const
{
return is_trait_candidate () && item.trait.impl == nullptr;
}
static PathProbeCandidate get_error ()
{
return PathProbeCandidate (ERROR, nullptr, Location (),
ImplItemCandidate{nullptr, nullptr});
}
bool is_error () const { return type == ERROR; }
};
class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor
{
public:
static std::vector<PathProbeCandidate>
Probe (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &segment_name, bool probe_impls,
bool probe_bounds, bool ignore_mandatory_trait_items,
DefId specific_trait_id = UNKNOWN_DEFID)
{
PathProbeType probe (receiver, segment_name, specific_trait_id);
if (probe_impls)
{
if (receiver->get_kind () == TyTy::TypeKind::ADT)
{
const TyTy::ADTType *adt
= static_cast<const TyTy::ADTType *> (receiver);
if (adt->is_enum ())
probe.process_enum_item_for_candiates (adt);
}
probe.process_impl_items_for_candidates ();
}
if (!probe_bounds)
return probe.candidates;
if (!probe.is_reciever_generic ())
{
std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> probed_bounds
= TypeBoundsProbe::Probe (receiver);
for (auto &candidate : probed_bounds)
{
const TraitReference *trait_ref = candidate.first;
if (specific_trait_id != UNKNOWN_DEFID)
{
if (trait_ref->get_mappings ().get_defid ()
!= specific_trait_id)
continue;
}
HIR::ImplBlock *impl = candidate.second;
probe.process_associated_trait_for_candidates (
trait_ref, impl, ignore_mandatory_trait_items);
}
}
for (const TyTy::TypeBoundPredicate &predicate :
receiver->get_specified_bounds ())
{
const TraitReference *trait_ref = predicate.get ();
if (specific_trait_id != UNKNOWN_DEFID)
{
if (trait_ref->get_mappings ().get_defid () != specific_trait_id)
continue;
}
probe.process_predicate_for_candidates (predicate,
ignore_mandatory_trait_items);
}
return probe.candidates;
}
void visit (HIR::TypeAlias &alias) override
{
Identifier name = alias.get_new_type_name ();
if (search.as_string ().compare (name) == 0)
{
HirId tyid = alias.get_mappings ().get_hirid ();
TyTy::BaseType *ty = nullptr;
bool ok = context->lookup_type (tyid, &ty);
rust_assert (ok);
PathProbeCandidate::ImplItemCandidate impl_item_candidate{&alias,
current_impl};
PathProbeCandidate candidate{
PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS, ty,
alias.get_locus (), impl_item_candidate};
candidates.push_back (std::move (candidate));
}
}
void visit (HIR::ConstantItem &constant) override
{
Identifier name = constant.get_identifier ();
if (search.as_string ().compare (name) == 0)
{
HirId tyid = constant.get_mappings ().get_hirid ();
TyTy::BaseType *ty = nullptr;
bool ok = context->lookup_type (tyid, &ty);
rust_assert (ok);
PathProbeCandidate::ImplItemCandidate impl_item_candidate{&constant,
current_impl};
PathProbeCandidate candidate{
PathProbeCandidate::CandidateType::IMPL_CONST, ty,
constant.get_locus (), impl_item_candidate};
candidates.push_back (std::move (candidate));
}
}
void visit (HIR::Function &function) override
{
Identifier name = function.get_function_name ();
if (search.as_string ().compare (name) == 0)
{
HirId tyid = function.get_mappings ().get_hirid ();
TyTy::BaseType *ty = nullptr;
bool ok = context->lookup_type (tyid, &ty);
rust_assert (ok);
PathProbeCandidate::ImplItemCandidate impl_item_candidate{&function,
current_impl};
PathProbeCandidate candidate{
PathProbeCandidate::CandidateType::IMPL_FUNC, ty,
function.get_locus (), impl_item_candidate};
candidates.push_back (std::move (candidate));
}
}
protected:
void process_enum_item_for_candiates (const TyTy::ADTType *adt)
{
if (specific_trait_id != UNKNOWN_DEFID)
return;
TyTy::VariantDef *v;
if (!adt->lookup_variant (search.as_string (), &v))
return;
PathProbeCandidate::EnumItemCandidate enum_item_candidate{adt, v};
PathProbeCandidate candidate{
PathProbeCandidate::CandidateType::ENUM_VARIANT, receiver->clone (),
mappings->lookup_location (adt->get_ty_ref ()), enum_item_candidate};
candidates.push_back (std::move (candidate));
}
void process_impl_items_for_candidates ()
{
mappings->iterate_impl_items ([&] (HirId id, HIR::ImplItem *item,
HIR::ImplBlock *impl) mutable -> bool {
process_impl_item_candidate (id, item, impl);
return true;
});
}
void process_impl_item_candidate (HirId id, HIR::ImplItem *item,
HIR::ImplBlock *impl)
{
current_impl = impl;
HirId impl_ty_id = impl->get_type ()->get_mappings ().get_hirid ();
TyTy::BaseType *impl_block_ty = nullptr;
if (!context->lookup_type (impl_ty_id, &impl_block_ty))
return;
if (!receiver->can_eq (impl_block_ty, false))
{
if (!impl_block_ty->can_eq (receiver, false))
return;
}
// lets visit the impl_item
item->accept_vis (*this);
}
void
process_associated_trait_for_candidates (const TraitReference *trait_ref,
HIR::ImplBlock *impl,
bool ignore_mandatory_trait_items)
{
const TraitItemReference *trait_item_ref = nullptr;
if (!trait_ref->lookup_trait_item (search.as_string (), &trait_item_ref))
return;
bool trait_item_needs_implementation = !trait_item_ref->is_optional ();
if (ignore_mandatory_trait_items && trait_item_needs_implementation)
return;
PathProbeCandidate::CandidateType candidate_type;
switch (trait_item_ref->get_trait_item_type ())
{
case TraitItemReference::TraitItemType::FN:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
break;
case TraitItemReference::TraitItemType::CONST:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
break;
case TraitItemReference::TraitItemType::TYPE:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
break;
case TraitItemReference::TraitItemType::ERROR:
default:
gcc_unreachable ();
break;
}
TyTy::BaseType *trait_item_tyty = trait_item_ref->get_tyty ();
// we can substitute the Self with the receiver here
if (trait_item_tyty->get_kind () == TyTy::TypeKind::FNDEF)
{
TyTy::FnType *fn = static_cast<TyTy::FnType *> (trait_item_tyty);
TyTy::SubstitutionParamMapping *param = nullptr;
for (auto &param_mapping : fn->get_substs ())
{
const HIR::TypeParam &type_param
= param_mapping.get_generic_param ();
if (type_param.get_type_representation ().compare ("Self") == 0)
{
param = &param_mapping;
break;
}
}
rust_assert (param != nullptr);
std::vector<TyTy::SubstitutionArg> mappings;
mappings.push_back (TyTy::SubstitutionArg (param, receiver->clone ()));
Location locus; // FIXME
TyTy::SubstitutionArgumentMappings args (std::move (mappings), locus);
trait_item_tyty = SubstMapperInternal::Resolve (trait_item_tyty, args);
}
PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
trait_item_ref,
impl};
PathProbeCandidate candidate{candidate_type, trait_item_tyty,
trait_ref->get_locus (), trait_item_candidate};
candidates.push_back (std::move (candidate));
}
void
process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate,
bool ignore_mandatory_trait_items)
{
const TraitReference *trait_ref = predicate.get ();
TyTy::TypeBoundPredicateItem item
= predicate.lookup_associated_item (search.as_string ());
if (item.is_error ())
return;
if (ignore_mandatory_trait_items && item.needs_implementation ())
return;
const TraitItemReference *trait_item_ref = item.get_raw_item ();
PathProbeCandidate::CandidateType candidate_type;
switch (trait_item_ref->get_trait_item_type ())
{
case TraitItemReference::TraitItemType::FN:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
break;
case TraitItemReference::TraitItemType::CONST:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
break;
case TraitItemReference::TraitItemType::TYPE:
candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
break;
case TraitItemReference::TraitItemType::ERROR:
default:
gcc_unreachable ();
break;
}
TyTy::BaseType *trait_item_tyty = item.get_tyty_for_receiver (receiver);
PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
trait_item_ref,
nullptr};
PathProbeCandidate candidate{candidate_type, trait_item_tyty,
trait_item_ref->get_locus (),
trait_item_candidate};
candidates.push_back (std::move (candidate));
}
protected:
PathProbeType (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &query, DefId specific_trait_id)
: TypeCheckBase (), receiver (receiver), search (query),
current_impl (nullptr), specific_trait_id (specific_trait_id)
{}
std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>>
union_bounds (
const std::vector<std::pair</*const*/ TraitReference *, HIR::ImplBlock *>>
a,
const std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> b)
const
{
std::map<DefId, std::pair<const TraitReference *, HIR::ImplBlock *>> mapper;
for (auto &ref : a)
{
mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
}
for (auto &ref : b)
{
mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
}
std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> union_set;
for (auto it = mapper.begin (); it != mapper.end (); it++)
{
union_set.push_back ({it->second.first, it->second.second});
}
return union_set;
}
bool is_reciever_generic () const
{
const TyTy::BaseType *root = receiver->get_root ();
bool receiver_is_type_param = root->get_kind () == TyTy::TypeKind::PARAM;
bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC;
return receiver_is_type_param || receiver_is_dyn;
}
const TyTy::BaseType *receiver;
const HIR::PathIdentSegment &search;
std::vector<PathProbeCandidate> candidates;
HIR::ImplBlock *current_impl;
DefId specific_trait_id;
};
class ReportMultipleCandidateError : private TypeCheckBase,
private HIR::HIRImplVisitor
{
public:
static void Report (std::vector<PathProbeCandidate> &candidates,
const HIR::PathIdentSegment &query, Location query_locus)
{
RichLocation r (query_locus);
ReportMultipleCandidateError visitor (r);
for (auto &c : candidates)
{
switch (c.type)
{
case PathProbeCandidate::CandidateType::ERROR:
case PathProbeCandidate::CandidateType::ENUM_VARIANT:
gcc_unreachable ();
break;
case PathProbeCandidate::CandidateType::IMPL_CONST:
case PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS:
case PathProbeCandidate::CandidateType::IMPL_FUNC:
c.item.impl.impl_item->accept_vis (visitor);
break;
case PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST:
case PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS:
case PathProbeCandidate::CandidateType::TRAIT_FUNC:
r.add_range (c.item.trait.item_ref->get_locus ());
break;
}
}
rust_error_at (r, "multiple applicable items in scope for: %s",
query.as_string ().c_str ());
}
void visit (HIR::TypeAlias &alias) override
{
r.add_range (alias.get_locus ());
}
void visit (HIR::ConstantItem &constant) override
{
r.add_range (constant.get_locus ());
}
void visit (HIR::Function &function) override
{
r.add_range (function.get_locus ());
}
private:
ReportMultipleCandidateError (RichLocation &r) : TypeCheckBase (), r (r) {}
RichLocation &r;
};
class PathProbeImplTrait : public PathProbeType
{
public:
static std::vector<PathProbeCandidate>
Probe (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &segment_name,
const TraitReference *trait_reference)
{
PathProbeImplTrait probe (receiver, segment_name, trait_reference);
// iterate all impls for this trait and receiver
// then search for possible candidates using base class behaviours
probe.process_trait_impl_items_for_candidates ();
return probe.candidates;
}
private:
void process_trait_impl_items_for_candidates ();
PathProbeImplTrait (const TyTy::BaseType *receiver,
const HIR::PathIdentSegment &query,
const TraitReference *trait_reference)
: PathProbeType (receiver, query, UNKNOWN_DEFID),
trait_reference (trait_reference)
{}
const TraitReference *trait_reference;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_HIR_PATH_PROBE_H

View File

@ -0,0 +1,472 @@
// Copyright (C) 2021-2022 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/>.
#ifndef RUST_HIR_TRAIT_REF_H
#define RUST_HIR_TRAIT_REF_H
#include "rust-hir-full.h"
#include "rust-tyty-visitor.h"
#include "rust-hir-type-check-util.h"
namespace Rust {
namespace Resolver {
// Data Objects for the associated trait items in a structure we can work with
// https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html
class TypeCheckContext;
class TraitItemReference
{
public:
enum TraitItemType
{
FN,
CONST,
TYPE,
ERROR
};
TraitItemReference (std::string identifier, bool optional, TraitItemType type,
HIR::TraitItem *hir_trait_item, TyTy::BaseType *self,
std::vector<TyTy::SubstitutionParamMapping> substitutions,
Location locus);
TraitItemReference (TraitItemReference const &other);
TraitItemReference &operator= (TraitItemReference const &other);
static TraitItemReference error ()
{
return TraitItemReference ("", false, ERROR, nullptr, nullptr, {},
Location ());
}
static TraitItemReference &error_node ()
{
static TraitItemReference error = TraitItemReference::error ();
return error;
}
bool is_error () const { return type == ERROR; }
std::string as_string () const
{
return "(" + trait_item_type_as_string (type) + " " + identifier + " "
+ ")";
}
static std::string trait_item_type_as_string (TraitItemType ty)
{
switch (ty)
{
case FN:
return "FN";
case CONST:
return "CONST";
case TYPE:
return "TYPE";
case ERROR:
return "ERROR";
}
return "ERROR";
}
bool is_optional () const { return optional_flag; }
std::string get_identifier () const { return identifier; }
TraitItemType get_trait_item_type () const { return type; }
HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; }
Location get_locus () const { return locus; }
const Analysis::NodeMapping get_mappings () const
{
return hir_trait_item->get_mappings ();
}
TyTy::BaseType *get_tyty () const
{
rust_assert (hir_trait_item != nullptr);
switch (type)
{
case CONST:
return get_type_from_constant (
static_cast</*const*/ HIR::TraitItemConst &> (*hir_trait_item));
break;
case TYPE:
return get_type_from_typealias (
static_cast</*const*/ HIR::TraitItemType &> (*hir_trait_item));
case FN:
return get_type_from_fn (
static_cast</*const*/ HIR::TraitItemFunc &> (*hir_trait_item));
break;
default:
return get_error ();
}
gcc_unreachable ();
return get_error ();
}
Analysis::NodeMapping get_parent_trait_mappings () const;
// this is called when the trait is completed resolution and gives the items a
// chance to run their specific type resolution passes. If we call their
// resolution on construction it can lead to a case where the trait being
// resolved recursively trying to resolve the trait itself infinitely since
// the trait will not be stored in its own map yet
void on_resolved ();
void associated_type_set (TyTy::BaseType *ty) const;
void associated_type_reset () const;
bool is_object_safe () const;
private:
TyTy::ErrorType *get_error () const
{
return new TyTy::ErrorType (get_mappings ().get_hirid ());
}
TyTy::BaseType *get_type_from_typealias (/*const*/
HIR::TraitItemType &type) const;
TyTy::BaseType *
get_type_from_constant (/*const*/ HIR::TraitItemConst &constant) const;
TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const;
bool is_item_resolved () const;
void resolve_item (HIR::TraitItemType &type);
void resolve_item (HIR::TraitItemConst &constant);
void resolve_item (HIR::TraitItemFunc &func);
std::string identifier;
bool optional_flag;
TraitItemType type;
HIR::TraitItem *hir_trait_item;
std::vector<TyTy::SubstitutionParamMapping> inherited_substitutions;
Location locus;
TyTy::BaseType
*self; // this is the implict Self TypeParam required for methods
Resolver::TypeCheckContext *context;
};
// this wraps up the HIR::Trait so we can do analysis on it
class TraitReference
{
public:
TraitReference (const HIR::Trait *hir_trait_ref,
std::vector<TraitItemReference> item_refs,
std::vector<const TraitReference *> super_traits,
std::vector<TyTy::SubstitutionParamMapping> substs)
: hir_trait_ref (hir_trait_ref), item_refs (item_refs),
super_traits (super_traits)
{
trait_substs.clear ();
trait_substs.reserve (substs.size ());
for (const auto &p : substs)
trait_substs.push_back (p.clone ());
}
TraitReference (TraitReference const &other)
: hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs),
super_traits (other.super_traits)
{
trait_substs.clear ();
trait_substs.reserve (other.trait_substs.size ());
for (const auto &p : other.trait_substs)
trait_substs.push_back (p.clone ());
}
TraitReference &operator= (TraitReference const &other)
{
hir_trait_ref = other.hir_trait_ref;
item_refs = other.item_refs;
super_traits = other.super_traits;
trait_substs.clear ();
trait_substs.reserve (other.trait_substs.size ());
for (const auto &p : other.trait_substs)
trait_substs.push_back (p.clone ());
return *this;
}
TraitReference (TraitReference &&other) = default;
TraitReference &operator= (TraitReference &&other) = default;
static TraitReference error ()
{
return TraitReference (nullptr, {}, {}, {});
}
bool is_error () const { return hir_trait_ref == nullptr; }
static TraitReference &error_node ()
{
static TraitReference trait_error_node = TraitReference::error ();
return trait_error_node;
}
Location get_locus () const { return hir_trait_ref->get_locus (); }
std::string get_name () const
{
rust_assert (!is_error ());
return hir_trait_ref->get_name ();
}
std::string as_string () const
{
if (is_error ())
return "<trait-ref-error-node>";
std::string item_buf;
for (auto &item : item_refs)
{
item_buf += item.as_string () + ", ";
}
return "HIR Trait: " + get_name () + "->"
+ hir_trait_ref->get_mappings ().as_string () + " [" + item_buf
+ "]";
}
const HIR::Trait *get_hir_trait_ref () const { return hir_trait_ref; }
const Analysis::NodeMapping &get_mappings () const
{
return hir_trait_ref->get_mappings ();
}
DefId get_defid () const { return get_mappings ().get_defid (); }
bool lookup_hir_trait_item (const HIR::TraitItem &item,
TraitItemReference **ref)
{
return lookup_trait_item (item.trait_identifier (), ref);
}
bool lookup_trait_item (const std::string &ident, TraitItemReference **ref)
{
for (auto &item : item_refs)
{
if (ident.compare (item.get_identifier ()) == 0)
{
*ref = &item;
return true;
}
}
return false;
}
bool lookup_trait_item_by_type (const std::string &ident,
TraitItemReference::TraitItemType type,
TraitItemReference **ref)
{
for (auto &item : item_refs)
{
if (item.get_trait_item_type () != type)
continue;
if (ident.compare (item.get_identifier ()) == 0)
{
*ref = &item;
return true;
}
}
return false;
}
bool lookup_trait_item_by_type (const std::string &ident,
TraitItemReference::TraitItemType type,
const TraitItemReference **ref) const
{
for (auto &item : item_refs)
{
if (item.get_trait_item_type () != type)
continue;
if (ident.compare (item.get_identifier ()) == 0)
{
*ref = &item;
return true;
}
}
return false;
}
bool lookup_hir_trait_item (const HIR::TraitItem &item,
const TraitItemReference **ref) const
{
return lookup_trait_item (item.trait_identifier (), ref);
}
bool lookup_trait_item (const std::string &ident,
const TraitItemReference **ref) const
{
for (auto &item : item_refs)
{
if (ident.compare (item.get_identifier ()) == 0)
{
*ref = &item;
return true;
}
}
return false;
}
const TraitItemReference *
lookup_trait_item (const std::string &ident,
TraitItemReference::TraitItemType type) const
{
for (auto &item : item_refs)
{
if (item.get_trait_item_type () != type)
continue;
if (ident.compare (item.get_identifier ()) == 0)
return &item;
}
return &TraitItemReference::error_node ();
}
size_t size () const { return item_refs.size (); }
const std::vector<TraitItemReference> &get_trait_items () const
{
return item_refs;
}
void on_resolved ()
{
for (auto &item : item_refs)
{
item.on_resolved ();
}
}
void clear_associated_types ()
{
for (auto &item : item_refs)
{
bool is_assoc_type = item.get_trait_item_type ()
== TraitItemReference::TraitItemType::TYPE;
if (is_assoc_type)
item.associated_type_reset ();
}
}
bool is_equal (const TraitReference &other) const
{
DefId this_id = get_mappings ().get_defid ();
DefId other_id = other.get_mappings ().get_defid ();
return this_id == other_id;
}
const std::vector<const TraitReference *> get_super_traits () const
{
return super_traits;
}
bool is_object_safe (bool emit_error, Location locus) const
{
// https: // doc.rust-lang.org/reference/items/traits.html#object-safety
std::vector<const TraitReference *> non_object_super_traits;
for (auto &item : super_traits)
{
if (!item->is_object_safe (false, Location ()))
non_object_super_traits.push_back (item);
}
std::vector<const Resolver::TraitItemReference *> non_object_safe_items;
for (auto &item : get_trait_items ())
{
if (!item.is_object_safe ())
non_object_safe_items.push_back (&item);
}
bool is_safe
= non_object_super_traits.empty () && non_object_safe_items.empty ();
if (emit_error && !is_safe)
{
RichLocation r (locus);
for (auto &item : non_object_super_traits)
r.add_range (item->get_locus ());
for (auto &item : non_object_safe_items)
r.add_range (item->get_locus ());
rust_error_at (r, "trait bound is not object safe");
}
return is_safe;
}
bool trait_has_generics () const { return !trait_substs.empty (); }
std::vector<TyTy::SubstitutionParamMapping> get_trait_substs () const
{
return trait_substs;
}
private:
const HIR::Trait *hir_trait_ref;
std::vector<TraitItemReference> item_refs;
std::vector<const TraitReference *> super_traits;
std::vector<TyTy::SubstitutionParamMapping> trait_substs;
};
class AssociatedImplTrait
{
public:
AssociatedImplTrait (TraitReference *trait, HIR::ImplBlock *impl,
TyTy::BaseType *self,
Resolver::TypeCheckContext *context)
: trait (trait), impl (impl), self (self), context (context)
{}
TraitReference *get_trait () { return trait; }
HIR::ImplBlock *get_impl_block () { return impl; }
TyTy::BaseType *get_self () { return self; }
void setup_associated_types (const TyTy::BaseType *self,
const TyTy::TypeBoundPredicate &bound);
void reset_associated_types ();
private:
TraitReference *trait;
HIR::ImplBlock *impl;
TyTy::BaseType *self;
Resolver::TypeCheckContext *context;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_HIR_TRAIT_REF_H

View File

@ -0,0 +1,77 @@
// Copyright (C) 2021-2022 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/>.
#ifndef RUST_HIR_TYPE_BOUNDS_H
#define RUST_HIR_TYPE_BOUNDS_H
#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
#include "rust-tyty.h"
namespace Rust {
namespace Resolver {
class TypeBoundsProbe : public TypeCheckBase
{
public:
static std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
Probe (const TyTy::BaseType *receiver)
{
TypeBoundsProbe probe (receiver);
probe.scan ();
return probe.trait_references;
}
static bool is_bound_satisfied_for_type (TyTy::BaseType *receiver,
TraitReference *ref)
{
for (auto &bound : receiver->get_specified_bounds ())
{
const TraitReference *b = bound.get ();
if (b->is_equal (*ref))
return true;
}
std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
= Probe (receiver);
for (auto &bound : bounds)
{
const TraitReference *b = bound.first;
if (b->is_equal (*ref))
return true;
}
return false;
}
private:
void scan ();
private:
TypeBoundsProbe (const TyTy::BaseType *receiver)
: TypeCheckBase (), receiver (receiver)
{}
const TyTy::BaseType *receiver;
std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> trait_references;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_HIR_TYPE_BOUNDS_H

View File

@ -0,0 +1,77 @@
// Copyright (C) 2020-2022 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 {
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;
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,394 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_SUBSTITUTION_MAPPER_H
#define RUST_SUBSTITUTION_MAPPER_H
#include "rust-tyty.h"
#include "rust-tyty-visitor.h"
namespace Rust {
namespace Resolver {
class SubstMapper : public TyTy::TyVisitor
{
public:
static TyTy::BaseType *Resolve (TyTy::BaseType *base, Location locus,
HIR::GenericArgs *generics = nullptr)
{
SubstMapper mapper (base->get_ref (), generics, locus);
base->accept_vis (mapper);
rust_assert (mapper.resolved != nullptr);
return mapper.resolved;
}
static TyTy::BaseType *InferSubst (TyTy::BaseType *base, Location locus)
{
return SubstMapper::Resolve (base, locus, nullptr);
}
bool have_generic_args () const { return generics != nullptr; }
void visit (TyTy::FnType &type) override
{
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 visit (TyTy::ADTType &type) override
{
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 visit (TyTy::PlaceholderType &type) override
{
rust_assert (type.can_resolve ());
resolved = SubstMapper::Resolve (type.resolve (), locus, generics);
}
void visit (TyTy::ProjectionType &type) override
{
TyTy::ProjectionType *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::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;
}
// nothing to do for these
void visit (TyTy::InferType &) override { gcc_unreachable (); }
void visit (TyTy::TupleType &) override { gcc_unreachable (); }
void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
void visit (TyTy::SliceType &) override { gcc_unreachable (); }
void visit (TyTy::BoolType &) override { gcc_unreachable (); }
void visit (TyTy::IntType &) override { gcc_unreachable (); }
void visit (TyTy::UintType &) override { gcc_unreachable (); }
void visit (TyTy::FloatType &) override { gcc_unreachable (); }
void visit (TyTy::USizeType &) override { gcc_unreachable (); }
void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
void visit (TyTy::CharType &) override { gcc_unreachable (); }
void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
void visit (TyTy::PointerType &) override { gcc_unreachable (); }
void visit (TyTy::ParamType &) override { gcc_unreachable (); }
void visit (TyTy::StrType &) override { gcc_unreachable (); }
void visit (TyTy::NeverType &) override { gcc_unreachable (); }
void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
void visit (TyTy::ClosureType &) override { gcc_unreachable (); }
private:
SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus)
: resolved (new TyTy::ErrorType (ref)), generics (generics), locus (locus)
{}
TyTy::BaseType *resolved;
HIR::GenericArgs *generics;
Location locus;
};
class SubstMapperInternal : public TyTy::TyVisitor
{
public:
static TyTy::BaseType *Resolve (TyTy::BaseType *base,
TyTy::SubstitutionArgumentMappings &mappings);
static bool mappings_are_bound (TyTy::BaseType *ty,
TyTy::SubstitutionArgumentMappings &mappings);
void visit (TyTy::FnType &type) override
{
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 visit (TyTy::ADTType &type) override
{
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 visit (TyTy::TupleType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::ReferenceType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::PointerType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::ParamType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::PlaceholderType &type) override
{
rust_assert (type.can_resolve ());
if (mappings.trait_item_mode ())
{
resolved = type.resolve ();
}
else
{
resolved = SubstMapperInternal::Resolve (type.resolve (), mappings);
}
}
void visit (TyTy::ProjectionType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::ClosureType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::ArrayType &type) override
{
resolved = type.handle_substitions (mappings);
}
void visit (TyTy::SliceType &type) override
{
resolved = type.handle_substitions (mappings);
}
// nothing to do for these
void visit (TyTy::InferType &type) override { resolved = type.clone (); }
void visit (TyTy::FnPtr &type) override { resolved = type.clone (); }
void visit (TyTy::BoolType &type) override { resolved = type.clone (); }
void visit (TyTy::IntType &type) override { resolved = type.clone (); }
void visit (TyTy::UintType &type) override { resolved = type.clone (); }
void visit (TyTy::FloatType &type) override { resolved = type.clone (); }
void visit (TyTy::USizeType &type) override { resolved = type.clone (); }
void visit (TyTy::ISizeType &type) override { resolved = type.clone (); }
void visit (TyTy::ErrorType &type) override { resolved = type.clone (); }
void visit (TyTy::CharType &type) override { resolved = type.clone (); }
void visit (TyTy::StrType &type) override { resolved = type.clone (); }
void visit (TyTy::NeverType &type) override { resolved = type.clone (); }
void visit (TyTy::DynamicObjectType &type) override
{
resolved = type.clone ();
}
private:
SubstMapperInternal (HirId ref, TyTy::SubstitutionArgumentMappings &mappings)
: resolved (new TyTy::ErrorType (ref)), mappings (mappings)
{}
TyTy::BaseType *resolved;
TyTy::SubstitutionArgumentMappings &mappings;
};
class SubstMapperFromExisting : public TyTy::TyVisitor
{
public:
static TyTy::BaseType *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 visit (TyTy::FnType &type) override
{
rust_assert (type.was_substituted ());
TyTy::FnType *to_sub = static_cast<TyTy::FnType *> (receiver);
resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}
void visit (TyTy::ADTType &type) override
{
rust_assert (type.was_substituted ());
TyTy::ADTType *to_sub = static_cast<TyTy::ADTType *> (receiver);
resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}
void visit (TyTy::ClosureType &type) override
{
rust_assert (type.was_substituted ());
TyTy::ClosureType *to_sub = static_cast<TyTy::ClosureType *> (receiver);
resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
}
void visit (TyTy::InferType &) override { gcc_unreachable (); }
void visit (TyTy::TupleType &) override { gcc_unreachable (); }
void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
void visit (TyTy::SliceType &) override { gcc_unreachable (); }
void visit (TyTy::BoolType &) override { gcc_unreachable (); }
void visit (TyTy::IntType &) override { gcc_unreachable (); }
void visit (TyTy::UintType &) override { gcc_unreachable (); }
void visit (TyTy::FloatType &) override { gcc_unreachable (); }
void visit (TyTy::USizeType &) override { gcc_unreachable (); }
void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
void visit (TyTy::CharType &) override { gcc_unreachable (); }
void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
void visit (TyTy::PointerType &) override { gcc_unreachable (); }
void visit (TyTy::ParamType &) override { gcc_unreachable (); }
void visit (TyTy::StrType &) override { gcc_unreachable (); }
void visit (TyTy::NeverType &) override { gcc_unreachable (); }
void visit (TyTy::PlaceholderType &) override { gcc_unreachable (); }
void visit (TyTy::ProjectionType &) override { gcc_unreachable (); }
void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
private:
SubstMapperFromExisting (TyTy::BaseType *concrete, TyTy::BaseType *receiver)
: concrete (concrete), receiver (receiver), resolved (nullptr)
{}
TyTy::BaseType *concrete;
TyTy::BaseType *receiver;
TyTy::BaseType *resolved;
};
class GetUsedSubstArgs : public TyTy::TyConstVisitor
{
public:
static TyTy::SubstitutionArgumentMappings From (const TyTy::BaseType *from)
{
GetUsedSubstArgs mapper;
from->accept_vis (mapper);
return mapper.args;
}
void visit (const TyTy::FnType &type) override
{
args = type.get_substitution_arguments ();
}
void visit (const TyTy::ADTType &type) override
{
args = type.get_substitution_arguments ();
}
void visit (const TyTy::ClosureType &type) override
{
args = type.get_substitution_arguments ();
}
void visit (const TyTy::InferType &) override {}
void visit (const TyTy::TupleType &) override {}
void visit (const TyTy::FnPtr &) override {}
void visit (const TyTy::ArrayType &) override {}
void visit (const TyTy::SliceType &) override {}
void visit (const TyTy::BoolType &) override {}
void visit (const TyTy::IntType &) override {}
void visit (const TyTy::UintType &) override {}
void visit (const TyTy::FloatType &) override {}
void visit (const TyTy::USizeType &) override {}
void visit (const TyTy::ISizeType &) override {}
void visit (const TyTy::ErrorType &) override {}
void visit (const TyTy::CharType &) override {}
void visit (const TyTy::ReferenceType &) override {}
void visit (const TyTy::PointerType &) override {}
void visit (const TyTy::ParamType &) override {}
void visit (const TyTy::StrType &) override {}
void visit (const TyTy::NeverType &) override {}
void visit (const TyTy::PlaceholderType &) override {}
void visit (const TyTy::ProjectionType &) override {}
void visit (const TyTy::DynamicObjectType &) override {}
private:
GetUsedSubstArgs () : args (TyTy::SubstitutionArgumentMappings::error ()) {}
TyTy::SubstitutionArgumentMappings args;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_SUBSTITUTION_MAPPER_H

View File

@ -0,0 +1,239 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_TYCHECK_DUMP
#define RUST_TYCHECK_DUMP
#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
namespace Rust {
namespace Resolver {
class TypeResolverDump : private TypeCheckBase, private HIR::HIRFullVisitorBase
{
using HIR::HIRFullVisitorBase::visit;
public:
static void go (HIR::Crate &crate, std::ofstream &out)
{
TypeResolverDump dumper;
for (auto &item : crate.items)
{
item->accept_vis (dumper);
dumper.dump += "\n";
}
out << dumper.dump;
}
void visit (HIR::StructStruct &struct_decl) override
{
dump += indent () + "struct " + type_string (struct_decl.get_mappings ())
+ "\n";
}
void visit (HIR::Union &union_decl) override
{
dump
+= indent () + "union " + type_string (union_decl.get_mappings ()) + "\n";
}
void visit (HIR::TupleStruct &struct_decl) override
{
dump += indent () + "struct" + type_string (struct_decl.get_mappings ())
+ "\n";
}
void visit (HIR::ImplBlock &impl_block) override
{
dump += indent () + "impl "
+ type_string (impl_block.get_type ()->get_mappings ()) + " {\n";
indentation_level++;
for (auto &impl_item : impl_block.get_impl_items ())
{
impl_item->accept_vis (*this);
dump += "\n";
}
indentation_level--;
dump += indent () + "}\n";
}
void visit (HIR::ConstantItem &constant) override
{
dump += indent () + "constant " + constant.get_identifier () + ":"
+ type_string (constant.get_mappings ()) + " = ";
constant.get_expr ()->accept_vis (*this);
dump += ";\n";
}
void visit (HIR::Function &function) override
{
dump += indent () + "fn " + function.get_function_name () + " "
+ type_string (function.get_mappings ()) + "\n";
dump += indent () + "{\n";
HIR::BlockExpr *function_body = function.get_definition ().get ();
function_body->accept_vis (*this);
dump += indent () + "}\n";
}
void visit (HIR::BlockExpr &expr) override
{
dump += "{\n";
indentation_level++;
for (auto &s : expr.get_statements ())
{
dump += indent ();
s->accept_vis (*this);
dump += ";\n";
}
if (expr.has_expr ())
{
dump += indent ();
expr.expr->accept_vis (*this);
dump += ";\n";
}
indentation_level--;
dump += "}\n";
}
void visit (HIR::UnsafeBlockExpr &expr) override
{
dump += "unsafe ";
expr.get_block_expr ()->accept_vis (*this);
}
void visit (HIR::LetStmt &stmt) override
{
dump += "let " + stmt.get_pattern ()->as_string () + ":"
+ type_string (stmt.get_pattern ()->get_pattern_mappings ());
if (stmt.has_init_expr ())
{
dump += " = ";
stmt.get_init_expr ()->accept_vis (*this);
}
}
void visit (HIR::ExprStmtWithBlock &stmt) override
{
stmt.get_expr ()->accept_vis (*this);
}
void visit (HIR::ExprStmtWithoutBlock &stmt) override
{
stmt.get_expr ()->accept_vis (*this);
}
void visit (HIR::AssignmentExpr &expr) override
{
expr.get_lhs ()->accept_vis (*this);
dump += " = ";
expr.get_rhs ()->accept_vis (*this);
}
void visit (HIR::LiteralExpr &expr) override
{
dump += expr.get_literal ().as_string () + ":"
+ type_string (expr.get_mappings ());
}
void visit (HIR::ArrayExpr &expr) override
{
dump += type_string (expr.get_mappings ()) + ":[";
HIR::ArrayElems *elements = expr.get_internal_elements ();
elements->accept_vis (*this);
dump += "]";
}
void visit (HIR::ArrayElemsValues &elems) override
{
for (auto &elem : elems.get_values ())
{
elem->accept_vis (*this);
dump += ",";
}
}
void visit (HIR::GroupedExpr &expr) override
{
HIR::Expr *paren_expr = expr.get_expr_in_parens ().get ();
dump += "(";
paren_expr->accept_vis (*this);
dump += ")";
}
void visit (HIR::PathInExpression &expr) override
{
dump += type_string (expr.get_mappings ());
}
void visit (HIR::StructExprStructFields &expr) override
{
dump += "ctor: " + type_string (expr.get_mappings ());
}
protected:
std::string type_string (const Analysis::NodeMapping &mappings)
{
TyTy::BaseType *lookup = nullptr;
if (!context->lookup_type (mappings.get_hirid (), &lookup))
return "<error>";
std::string buf = "[";
for (auto &ref : lookup->get_combined_refs ())
{
buf += std::to_string (ref);
buf += ", ";
}
buf += "]";
std::string repr = lookup->as_string ();
return "<" + repr + " HIRID: " + std::to_string (mappings.get_hirid ())
+ " RF:" + std::to_string (lookup->get_ref ()) + " TF:"
+ std::to_string (lookup->get_ty_ref ()) + +" - " + buf + ">";
}
std::string indent ()
{
std::string buf;
for (size_t i = 0; i < indentation_level; ++i)
buf += " ";
return buf;
}
private:
TypeResolverDump () : TypeCheckBase (), indentation_level (0) {}
std::string dump;
size_t indentation_level;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_TYCHECK_DUMP

View File

@ -0,0 +1,155 @@
// Copyright (C) 2020-2022 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-hir-type-check.h"
namespace Rust {
namespace Resolver {
TypeCheckContext *
TypeCheckContext::get ()
{
static TypeCheckContext *instance;
if (instance == nullptr)
instance = new TypeCheckContext ();
return instance;
}
TypeCheckContext::TypeCheckContext () {}
TypeCheckContext::~TypeCheckContext () {}
bool
TypeCheckContext::lookup_builtin (NodeId id, TyTy::BaseType **type)
{
auto ref_it = node_id_refs.find (id);
if (ref_it == node_id_refs.end ())
return false;
auto it = resolved.find (ref_it->second);
if (it == resolved.end ())
return false;
*type = it->second;
return true;
}
bool
TypeCheckContext::lookup_builtin (std::string name, TyTy::BaseType **type)
{
for (auto &builtin : builtins)
{
if (name.compare (builtin->as_string ()) == 0)
{
*type = builtin.get ();
return true;
}
}
return false;
}
void
TypeCheckContext::insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type)
{
node_id_refs[ref] = id;
resolved[id] = type;
builtins.push_back (std::unique_ptr<TyTy::BaseType> (type));
}
void
TypeCheckContext::insert_type (const Analysis::NodeMapping &mappings,
TyTy::BaseType *type)
{
rust_assert (type != nullptr);
NodeId ref = mappings.get_nodeid ();
HirId id = mappings.get_hirid ();
node_id_refs[ref] = id;
resolved[id] = type;
}
void
TypeCheckContext::insert_implicit_type (TyTy::BaseType *type)
{
rust_assert (type != nullptr);
resolved[type->get_ref ()] = type;
}
void
TypeCheckContext::insert_implicit_type (HirId id, TyTy::BaseType *type)
{
rust_assert (type != nullptr);
resolved[id] = type;
}
bool
TypeCheckContext::lookup_type (HirId id, TyTy::BaseType **type) const
{
auto it = resolved.find (id);
if (it == resolved.end ())
return false;
*type = it->second;
return true;
}
void
TypeCheckContext::insert_type_by_node_id (NodeId ref, HirId id)
{
rust_assert (node_id_refs.find (ref) == node_id_refs.end ());
node_id_refs[ref] = id;
}
bool
TypeCheckContext::lookup_type_by_node_id (NodeId ref, HirId *id)
{
auto it = node_id_refs.find (ref);
if (it == node_id_refs.end ())
return false;
*id = it->second;
return true;
}
TyTy::BaseType *
TypeCheckContext::peek_return_type ()
{
return return_type_stack.back ().second;
}
void
TypeCheckContext::push_return_type (TypeCheckContextItem item,
TyTy::BaseType *return_type)
{
return_type_stack.push_back ({std::move (item), return_type});
}
void
TypeCheckContext::pop_return_type ()
{
return_type_stack.pop_back ();
}
TypeCheckContextItem &
TypeCheckContext::peek_context ()
{
return return_type_stack.back ().first;
}
} // namespace Resolver
} // namespace Rust

View File

@ -0,0 +1,462 @@
// Copyright (C) 2021-2022 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-hir-type-bounds.h"
#include "rust-hir-trait-resolve.h"
namespace Rust {
namespace Resolver {
void
TypeBoundsProbe::scan ()
{
std::vector<std::pair<HIR::TypePath *, HIR::ImplBlock *>>
possible_trait_paths;
mappings->iterate_impl_blocks (
[&] (HirId id, HIR::ImplBlock *impl) mutable -> bool {
// we are filtering for trait-impl-blocks
if (!impl->has_trait_ref ())
return true;
TyTy::BaseType *impl_type = nullptr;
bool ok
= context->lookup_type (impl->get_type ()->get_mappings ().get_hirid (),
&impl_type);
if (!ok)
return true;
if (!receiver->can_eq (impl_type, false))
{
if (!impl_type->can_eq (receiver, false))
return true;
}
possible_trait_paths.push_back ({impl->get_trait_ref ().get (), impl});
return true;
});
for (auto &path : possible_trait_paths)
{
HIR::TypePath *trait_path = path.first;
TraitReference *trait_ref = TraitResolver::Resolve (*trait_path);
if (!trait_ref->is_error ())
trait_references.push_back ({trait_ref, path.second});
}
}
TraitReference *
TypeCheckBase::resolve_trait_path (HIR::TypePath &path)
{
return TraitResolver::Resolve (path);
}
TyTy::TypeBoundPredicate
TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path)
{
TraitReference *trait = resolve_trait_path (type_path);
if (trait->is_error ())
return TyTy::TypeBoundPredicate::error ();
TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ());
HIR::GenericArgs args
= HIR::GenericArgs::create_empty (type_path.get_locus ());
auto &final_seg = type_path.get_final_segment ();
if (final_seg->is_generic_segment ())
{
auto final_generic_seg
= static_cast<HIR::TypePathSegmentGeneric *> (final_seg.get ());
if (final_generic_seg->has_generic_args ())
{
args = final_generic_seg->get_generic_args ();
}
}
if (predicate.requires_generic_args ())
{
// this is applying generic arguments to a trait reference
predicate.apply_generic_arguments (&args);
}
return predicate;
}
} // namespace Resolver
namespace TyTy {
TypeBoundPredicate::TypeBoundPredicate (
const Resolver::TraitReference &trait_reference, Location locus)
: SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
reference (trait_reference.get_mappings ().get_defid ()), locus (locus),
error_flag (false)
{
substitutions.clear ();
for (const auto &p : trait_reference.get_trait_substs ())
substitutions.push_back (p.clone ());
// we setup a dummy implict self argument
SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
used_arguments.get_mappings ().push_back (placeholder_self);
}
TypeBoundPredicate::TypeBoundPredicate (
DefId reference, std::vector<SubstitutionParamMapping> subst, Location locus)
: SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
reference (reference), locus (locus), error_flag (false)
{
substitutions.clear ();
for (const auto &p : subst)
substitutions.push_back (p.clone ());
// we setup a dummy implict self argument
SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
used_arguments.get_mappings ().push_back (placeholder_self);
}
TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other)
: SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
reference (other.reference), locus (other.locus),
error_flag (other.error_flag)
{
substitutions.clear ();
for (const auto &p : other.get_substs ())
substitutions.push_back (p.clone ());
std::vector<SubstitutionArg> mappings;
for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
{
const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
SubstitutionArg arg (oa);
mappings.push_back (std::move (arg));
}
// we need to remap the argument mappings based on this copied constructor
std::vector<SubstitutionArg> copied_arg_mappings;
size_t i = 0;
for (const auto &m : other.used_arguments.get_mappings ())
{
TyTy::BaseType *argument
= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
SubstitutionArg c (&substitutions.at (i++), argument);
copied_arg_mappings.push_back (std::move (c));
}
used_arguments
= SubstitutionArgumentMappings (copied_arg_mappings,
other.used_arguments.get_locus ());
}
TypeBoundPredicate &
TypeBoundPredicate::operator= (const TypeBoundPredicate &other)
{
reference = other.reference;
locus = other.locus;
error_flag = other.error_flag;
used_arguments = SubstitutionArgumentMappings::error ();
substitutions.clear ();
for (const auto &p : other.get_substs ())
substitutions.push_back (p.clone ());
std::vector<SubstitutionArg> mappings;
for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
{
const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
SubstitutionArg arg (oa);
mappings.push_back (std::move (arg));
}
// we need to remap the argument mappings based on this copied constructor
std::vector<SubstitutionArg> copied_arg_mappings;
size_t i = 0;
for (const auto &m : other.used_arguments.get_mappings ())
{
TyTy::BaseType *argument
= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
SubstitutionArg c (&substitutions.at (i++), argument);
copied_arg_mappings.push_back (std::move (c));
}
used_arguments
= SubstitutionArgumentMappings (copied_arg_mappings,
other.used_arguments.get_locus ());
return *this;
}
TypeBoundPredicate
TypeBoundPredicate::error ()
{
auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ());
p.error_flag = true;
return p;
}
std::string
TypeBoundPredicate::as_string () const
{
return get ()->as_string () + subst_as_string ();
}
std::string
TypeBoundPredicate::as_name () const
{
return get ()->get_name () + subst_as_string ();
}
const Resolver::TraitReference *
TypeBoundPredicate::get () const
{
auto context = Resolver::TypeCheckContext::get ();
Resolver::TraitReference *ref = nullptr;
bool ok = context->lookup_trait_reference (reference, &ref);
rust_assert (ok);
return ref;
}
std::string
TypeBoundPredicate::get_name () const
{
return get ()->get_name ();
}
bool
TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const
{
const Resolver::TraitReference *trait = get ();
rust_assert (trait != nullptr);
return trait->is_object_safe (emit_error, locus);
}
void
TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args)
{
// we need to get the substitutions argument mappings but also remember that
// we have an implicit Self argument which we must be careful to respect
rust_assert (!used_arguments.is_empty ());
rust_assert (!substitutions.empty ());
// now actually perform a substitution
used_arguments = get_mappings_from_generic_args (*generic_args);
error_flag |= used_arguments.is_error ();
auto &subst_mappings = used_arguments;
for (auto &sub : get_substs ())
{
SubstitutionArg arg = SubstitutionArg::error ();
bool ok
= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
if (ok && arg.get_tyty () != nullptr)
sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
}
}
bool
TypeBoundPredicate::contains_item (const std::string &search) const
{
auto trait_ref = get ();
const Resolver::TraitItemReference *trait_item_ref = nullptr;
return trait_ref->lookup_trait_item (search, &trait_item_ref);
}
TypeBoundPredicateItem
TypeBoundPredicate::lookup_associated_item (const std::string &search) const
{
auto trait_ref = get ();
const Resolver::TraitItemReference *trait_item_ref = nullptr;
if (!trait_ref->lookup_trait_item (search, &trait_item_ref))
return TypeBoundPredicateItem::error ();
return TypeBoundPredicateItem (this, trait_item_ref);
}
TypeBoundPredicateItem
TypeBoundPredicate::lookup_associated_item (
const Resolver::TraitItemReference *ref) const
{
return lookup_associated_item (ref->get_identifier ());
}
BaseType *
TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
{
TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty ();
if (parent->get_substitution_arguments ().is_empty ())
return trait_item_tyty;
const Resolver::TraitItemReference *tref = get_raw_item ();
bool is_associated_type = tref->get_trait_item_type ();
if (is_associated_type)
return trait_item_tyty;
// set up the self mapping
SubstitutionArgumentMappings gargs = parent->get_substitution_arguments ();
rust_assert (!gargs.is_empty ());
// setup the adjusted mappings
std::vector<SubstitutionArg> adjusted_mappings;
for (size_t i = 0; i < gargs.get_mappings ().size (); i++)
{
auto &mapping = gargs.get_mappings ().at (i);
bool is_implicit_self = i == 0;
TyTy::BaseType *argument
= is_implicit_self ? receiver->clone () : mapping.get_tyty ();
SubstitutionArg arg (mapping.get_param_mapping (), argument);
adjusted_mappings.push_back (std::move (arg));
}
SubstitutionArgumentMappings adjusted (adjusted_mappings, gargs.get_locus (),
gargs.get_subst_cb (),
true /* trait-mode-flag */);
return Resolver::SubstMapperInternal::Resolve (trait_item_tyty, adjusted);
}
bool
TypeBoundPredicate::is_error () const
{
auto context = Resolver::TypeCheckContext::get ();
Resolver::TraitReference *ref = nullptr;
bool ok = context->lookup_trait_reference (reference, &ref);
return !ok || error_flag;
}
BaseType *
TypeBoundPredicate::handle_substitions (
SubstitutionArgumentMappings subst_mappings)
{
for (auto &sub : get_substs ())
{
if (sub.get_param_ty () == nullptr)
continue;
ParamType *p = sub.get_param_ty ();
BaseType *r = p->resolve ();
BaseType *s = Resolver::SubstMapperInternal::Resolve (r, subst_mappings);
p->set_ty_ref (s->get_ty_ref ());
}
// FIXME more error handling at some point
// used_arguments = subst_mappings;
// error_flag |= used_arguments.is_error ();
return nullptr;
}
bool
TypeBoundPredicate::requires_generic_args () const
{
if (is_error ())
return false;
return substitutions.size () > 1;
}
// trait item reference
const Resolver::TraitItemReference *
TypeBoundPredicateItem::get_raw_item () const
{
return trait_item_ref;
}
bool
TypeBoundPredicateItem::needs_implementation () const
{
return !get_raw_item ()->is_optional ();
}
Location
TypeBoundPredicateItem::get_locus () const
{
return get_raw_item ()->get_locus ();
}
// TypeBoundsMappings
TypeBoundsMappings::TypeBoundsMappings (
std::vector<TypeBoundPredicate> specified_bounds)
: specified_bounds (specified_bounds)
{}
std::vector<TypeBoundPredicate> &
TypeBoundsMappings::get_specified_bounds ()
{
return specified_bounds;
}
const std::vector<TypeBoundPredicate> &
TypeBoundsMappings::get_specified_bounds () const
{
return specified_bounds;
}
size_t
TypeBoundsMappings::num_specified_bounds () const
{
return specified_bounds.size ();
}
std::string
TypeBoundsMappings::raw_bounds_as_string () const
{
std::string buf;
for (size_t i = 0; i < specified_bounds.size (); i++)
{
const TypeBoundPredicate &b = specified_bounds.at (i);
bool has_next = (i + 1) < specified_bounds.size ();
buf += b.as_string () + (has_next ? " + " : "");
}
return buf;
}
std::string
TypeBoundsMappings::bounds_as_string () const
{
return "bounds:[" + raw_bounds_as_string () + "]";
}
std::string
TypeBoundsMappings::raw_bounds_as_name () const
{
std::string buf;
for (size_t i = 0; i < specified_bounds.size (); i++)
{
const TypeBoundPredicate &b = specified_bounds.at (i);
bool has_next = (i + 1) < specified_bounds.size ();
buf += b.as_name () + (has_next ? " + " : "");
}
return buf;
}
void
TypeBoundsMappings::add_bound (TypeBoundPredicate predicate)
{
specified_bounds.push_back (predicate);
}
} // namespace TyTy
} // namespace Rust

View File

@ -0,0 +1,263 @@
// Copyright (C) 2020-2022 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-tyty-call.h"
#include "rust-hir-type-check-expr.h"
namespace Rust {
namespace TyTy {
void
TypeCheckCallExpr::visit (ADTType &type)
{
rust_assert (!variant.is_error ());
if (variant.get_variant_type () != TyTy::VariantDef::VariantType::TUPLE)
{
rust_error_at (
call.get_locus (),
"expected function, tuple struct or tuple variant, found struct %<%s%>",
type.get_name ().c_str ());
return;
}
if (call.num_params () != variant.num_fields ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) call.num_params (),
(unsigned long) variant.num_fields ());
return;
}
size_t i = 0;
for (auto &argument : call.get_arguments ())
{
StructFieldType *field = variant.get_field_at_index (i);
BaseType *field_tyty = field->get_field_type ();
BaseType *arg = Resolver::TypeCheckExpr::Resolve (argument.get ());
if (arg->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
"failed to resolve argument type");
return;
}
auto res = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), field_tyty, arg,
argument->get_locus ());
if (res->get_kind () == TyTy::TypeKind::ERROR)
{
return;
}
delete res;
i++;
}
if (i != call.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) i, (unsigned long) call.num_params ());
return;
}
resolved = type.clone ();
}
void
TypeCheckCallExpr::visit (FnType &type)
{
type.monomorphize ();
if (call.num_params () != type.num_params ())
{
if (type.is_varadic ())
{
if (call.num_params () < type.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) call.num_params (),
(unsigned long) type.num_params ());
return;
}
}
else
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) call.num_params (),
(unsigned long) type.num_params ());
return;
}
}
size_t i = 0;
for (auto &argument : call.get_arguments ())
{
auto argument_expr_tyty
= Resolver::TypeCheckExpr::Resolve (argument.get ());
if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (
argument->get_locus (),
"failed to resolve type for argument expr in CallExpr");
return;
}
// it might be a varadic function
if (i < type.num_params ())
{
auto fnparam = type.param_at (i);
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam.second,
argument_expr_tyty, argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
"Type Resolution failure on parameter");
return;
}
}
i++;
}
if (i < call.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) i, (unsigned long) call.num_params ());
return;
}
type.monomorphize ();
resolved = type.get_return_type ()->clone ();
}
void
TypeCheckCallExpr::visit (FnPtr &type)
{
if (call.num_params () != type.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) call.num_params (),
(unsigned long) type.num_params ());
return;
}
size_t i = 0;
for (auto &argument : call.get_arguments ())
{
auto fnparam = type.param_at (i);
auto argument_expr_tyty
= Resolver::TypeCheckExpr::Resolve (argument.get ());
if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (
argument->get_locus (),
"failed to resolve type for argument expr in CallExpr");
return;
}
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam, argument_expr_tyty,
argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
"Type Resolution failure on parameter");
return;
}
i++;
}
if (i != call.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) i, (unsigned long) call.num_params ());
return;
}
resolved = type.get_return_type ()->monomorphized_clone ();
}
// method call checker
void
TypeCheckMethodCallExpr::visit (FnType &type)
{
type.get_self_type ()->unify (adjusted_self);
// +1 for the receiver self
size_t num_args_to_call = call.num_params () + 1;
if (num_args_to_call != type.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) call.num_params (),
(unsigned long) type.num_params ());
return;
}
size_t i = 1;
for (auto &argument : call.get_arguments ())
{
auto fnparam = type.param_at (i);
auto argument_expr_tyty
= Resolver::TypeCheckExpr::Resolve (argument.get ());
if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (
argument->get_locus (),
"failed to resolve type for argument expr in CallExpr");
return;
}
auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
argument->get_mappings ().get_hirid (), fnparam.second,
argument_expr_tyty, argument->get_locus ());
if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (argument->get_locus (),
"Type Resolution failure on parameter");
return;
}
i++;
}
if (i != num_args_to_call)
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
(unsigned long) i, (unsigned long) call.num_params ());
return;
}
type.monomorphize ();
resolved = type.get_return_type ()->monomorphized_clone ();
}
} // namespace TyTy
} // namespace Rust

View File

@ -0,0 +1,147 @@
// Copyright (C) 2020-2022 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/>.
#ifndef RUST_TYTY_CALL
#define RUST_TYTY_CALL
#include "rust-diagnostics.h"
#include "rust-hir-full.h"
#include "rust-tyty-visitor.h"
#include "rust-tyty.h"
#include "rust-hir-type-check.h"
namespace Rust {
namespace TyTy {
class TypeCheckCallExpr : private TyVisitor
{
public:
static BaseType *go (BaseType *ref, HIR::CallExpr &call,
TyTy::VariantDef &variant,
Resolver::TypeCheckContext *context)
{
TypeCheckCallExpr checker (call, variant, context);
ref->accept_vis (checker);
return checker.resolved;
}
void visit (InferType &) override { gcc_unreachable (); }
void visit (TupleType &) override { gcc_unreachable (); }
void visit (ArrayType &) override { gcc_unreachable (); }
void visit (SliceType &) override { gcc_unreachable (); }
void visit (BoolType &) override { gcc_unreachable (); }
void visit (IntType &) override { gcc_unreachable (); }
void visit (UintType &) override { gcc_unreachable (); }
void visit (FloatType &) override { gcc_unreachable (); }
void visit (USizeType &) override { gcc_unreachable (); }
void visit (ISizeType &) override { gcc_unreachable (); }
void visit (ErrorType &) override { gcc_unreachable (); }
void visit (CharType &) override { gcc_unreachable (); }
void visit (ReferenceType &) override { gcc_unreachable (); }
void visit (PointerType &) override { gcc_unreachable (); }
void visit (ParamType &) override { gcc_unreachable (); }
void visit (StrType &) override { gcc_unreachable (); }
void visit (NeverType &) override { gcc_unreachable (); }
void visit (PlaceholderType &) override { gcc_unreachable (); }
void visit (ProjectionType &) override { gcc_unreachable (); }
void visit (DynamicObjectType &) override { gcc_unreachable (); }
void visit (ClosureType &type) override { gcc_unreachable (); }
// tuple-structs
void visit (ADTType &type) override;
// call fns
void visit (FnType &type) override;
void visit (FnPtr &type) override;
private:
TypeCheckCallExpr (HIR::CallExpr &c, TyTy::VariantDef &variant,
Resolver::TypeCheckContext *context)
: resolved (new TyTy::ErrorType (c.get_mappings ().get_hirid ())), call (c),
variant (variant), context (context),
mappings (Analysis::Mappings::get ())
{}
BaseType *resolved;
HIR::CallExpr &call;
TyTy::VariantDef &variant;
Resolver::TypeCheckContext *context;
Analysis::Mappings *mappings;
};
class TypeCheckMethodCallExpr : private TyVisitor
{
public:
// Resolve the Method parameters and return back the return type
static BaseType *go (BaseType *ref, HIR::MethodCallExpr &call,
TyTy::BaseType *adjusted_self,
Resolver::TypeCheckContext *context)
{
TypeCheckMethodCallExpr checker (call, adjusted_self, context);
ref->accept_vis (checker);
return checker.resolved;
}
void visit (InferType &) override { gcc_unreachable (); }
void visit (TupleType &) override { gcc_unreachable (); }
void visit (ArrayType &) override { gcc_unreachable (); }
void visit (SliceType &) override { gcc_unreachable (); }
void visit (BoolType &) override { gcc_unreachable (); }
void visit (IntType &) override { gcc_unreachable (); }
void visit (UintType &) override { gcc_unreachable (); }
void visit (FloatType &) override { gcc_unreachable (); }
void visit (USizeType &) override { gcc_unreachable (); }
void visit (ISizeType &) override { gcc_unreachable (); }
void visit (ErrorType &) override { gcc_unreachable (); }
void visit (ADTType &) override { gcc_unreachable (); };
void visit (CharType &) override { gcc_unreachable (); }
void visit (ReferenceType &) override { gcc_unreachable (); }
void visit (PointerType &) override { gcc_unreachable (); }
void visit (ParamType &) override { gcc_unreachable (); }
void visit (StrType &) override { gcc_unreachable (); }
void visit (NeverType &) override { gcc_unreachable (); }
void visit (PlaceholderType &) override { gcc_unreachable (); }
void visit (ProjectionType &) override { gcc_unreachable (); }
void visit (DynamicObjectType &) override { gcc_unreachable (); }
// FIXME
void visit (FnPtr &type) override { gcc_unreachable (); }
// call fns
void visit (FnType &type) override;
void visit (ClosureType &type) override { gcc_unreachable (); }
private:
TypeCheckMethodCallExpr (HIR::MethodCallExpr &c,
TyTy::BaseType *adjusted_self,
Resolver::TypeCheckContext *context)
: resolved (nullptr), call (c), adjusted_self (adjusted_self),
context (context), mappings (Analysis::Mappings::get ())
{}
BaseType *resolved;
HIR::MethodCallExpr &call;
TyTy::BaseType *adjusted_self;
Resolver::TypeCheckContext *context;
Analysis::Mappings *mappings;
};
} // namespace TyTy
} // namespace Rust
#endif // RUST_TYTY_CALL

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff