summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2022-10-21 13:55:28 +0200
committerArthur Cohen <arthur.cohen@embecosm.com>2022-12-13 14:00:06 +0100
commit06688fe40a249a406634d3307f662e2fe2e0c517 (patch)
tree28d9ebdf9c013248c3f88f5f48ed80a31c07c98c
parent24393cb68faadda19c9f0ba12b9bba501e8e4ff8 (diff)
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.
-rw-r--r--gcc/rust/typecheck/rust-autoderef.cc398
-rw-r--r--gcc/rust/typecheck/rust-autoderef.h178
-rw-r--r--gcc/rust/typecheck/rust-casts.cc292
-rw-r--r--gcc/rust/typecheck/rust-casts.h53
-rw-r--r--gcc/rust/typecheck/rust-coercion.cc357
-rw-r--r--gcc/rust/typecheck/rust-coercion.h93
-rw-r--r--gcc/rust/typecheck/rust-hir-dot-operator.cc263
-rw-r--r--gcc/rust/typecheck/rust-hir-dot-operator.h81
-rw-r--r--gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h186
-rw-r--r--gcc/rust/typecheck/rust-hir-path-probe.h540
-rw-r--r--gcc/rust/typecheck/rust-hir-trait-ref.h472
-rw-r--r--gcc/rust/typecheck/rust-hir-type-bounds.h77
-rw-r--r--gcc/rust/typecheck/rust-substitution-mapper.cc77
-rw-r--r--gcc/rust/typecheck/rust-substitution-mapper.h394
-rw-r--r--gcc/rust/typecheck/rust-tycheck-dump.h239
-rw-r--r--gcc/rust/typecheck/rust-tyctx.cc155
-rw-r--r--gcc/rust/typecheck/rust-tyty-bounds.cc462
-rw-r--r--gcc/rust/typecheck/rust-tyty-call.cc263
-rw-r--r--gcc/rust/typecheck/rust-tyty-call.h147
-rw-r--r--gcc/rust/typecheck/rust-tyty-cmp.h1554
-rw-r--r--gcc/rust/typecheck/rust-tyty-rules.h1584
21 files changed, 7865 insertions, 0 deletions
diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc
new file mode 100644
index 00000000000..423f8e4709b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-autoderef.h b/gcc/rust/typecheck/rust-autoderef.h
new file mode 100644
index 00000000000..2f8d64b97e6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-casts.cc b/gcc/rust/typecheck/rust-casts.cc
new file mode 100644
index 00000000000..61004dfabc3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-casts.h b/gcc/rust/typecheck/rust-casts.h
new file mode 100644
index 00000000000..e908f49b656
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc
new file mode 100644
index 00000000000..2ad2b8007ff
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-coercion.h b/gcc/rust/typecheck/rust-coercion.h
new file mode 100644
index 00000000000..da28c7c5e1b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc
new file mode 100644
index 00000000000..d45f0903478
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.h b/gcc/rust/typecheck/rust-hir-dot-operator.h
new file mode 100644
index 00000000000..750601a2d9e
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
new file mode 100644
index 00000000000..2890b54a00d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h
new file mode 100644
index 00000000000..bd4f91e49bf
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-path-probe.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h
new file mode 100644
index 00000000000..6eec461e8a5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-ref.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h
new file mode 100644
index 00000000000..44400efbbf7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-bounds.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.cc b/gcc/rust/typecheck/rust-substitution-mapper.cc
new file mode 100644
index 00000000000..f80368a0339
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.h b/gcc/rust/typecheck/rust-substitution-mapper.h
new file mode 100644
index 00000000000..028e10c0efe
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h
new file mode 100644
index 00000000000..ccf0f625e4b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tycheck-dump.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-tyctx.cc b/gcc/rust/typecheck/rust-tyctx.cc
new file mode 100644
index 00000000000..d8a49e8b9ea
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyctx.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
new file mode 100644
index 00000000000..7a1562ab544
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-tyty-call.cc b/gcc/rust/typecheck/rust-tyty-call.cc
new file mode 100644
index 00000000000..1ce82c943f5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.cc
@@ -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
diff --git a/gcc/rust/typecheck/rust-tyty-call.h b/gcc/rust/typecheck/rust-tyty-call.h
new file mode 100644
index 00000000000..51817e6446d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.h
@@ -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
diff --git a/gcc/rust/typecheck/rust-tyty-cmp.h b/gcc/rust/typecheck/rust-tyty-cmp.h
new file mode 100644
index 00000000000..07d1dea7464
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-cmp.h
@@ -0,0 +1,1554 @@
+// 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_CMP_H
+#define RUST_TYTY_CMP_H
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+class BaseCmp : public TyConstVisitor
+{
+public:
+ virtual bool can_eq (const BaseType *other)
+ {
+ if (other->get_kind () == TypeKind::PARAM)
+ {
+ const ParamType *p = static_cast<const ParamType *> (other);
+ other = p->resolve ();
+ }
+ if (other->get_kind () == TypeKind::PLACEHOLDER)
+ {
+ const PlaceholderType *p = static_cast<const PlaceholderType *> (other);
+ if (p->can_resolve ())
+ {
+ other = p->resolve ();
+ }
+ }
+ if (other->get_kind () == TypeKind::PROJECTION)
+ {
+ const ProjectionType *p = static_cast<const ProjectionType *> (other);
+ other = p->get ();
+ }
+
+ other->accept_vis (*this);
+ return ok;
+ }
+
+ virtual void visit (const TupleType &type) override
+ {
+ ok = false;
+
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ADTType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const InferType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const FnType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const FnPtr &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ArrayType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const SliceType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const BoolType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const IntType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const UintType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const USizeType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ISizeType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const FloatType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ErrorType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const CharType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ReferenceType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const PointerType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const StrType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const NeverType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ProjectionType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const PlaceholderType &type) override
+ {
+ // it is ok for types to can eq to a placeholder
+ ok = true;
+ }
+
+ virtual void visit (const ParamType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const DynamicObjectType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+ virtual void visit (const ClosureType &type) override
+ {
+ ok = false;
+ if (emit_error_flag)
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus
+ = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+ }
+
+protected:
+ BaseCmp (const BaseType *base, bool emit_errors)
+ : mappings (Analysis::Mappings::get ()),
+ context (Resolver::TypeCheckContext::get ()), ok (false),
+ emit_error_flag (emit_errors)
+ {}
+
+ Analysis::Mappings *mappings;
+ Resolver::TypeCheckContext *context;
+
+ bool ok;
+ bool emit_error_flag;
+
+private:
+ /* Returns a pointer to the ty that created this rule. */
+ virtual const BaseType *get_base () const = 0;
+};
+
+class InferCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ InferCmp (const InferType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const BoolType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const IntType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const UintType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const USizeType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const ISizeType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const FloatType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const ArrayType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const SliceType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const ADTType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const TupleType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const InferType &type) override
+ {
+ switch (base->get_infer_kind ())
+ {
+ case InferType::InferTypeKind::GENERAL:
+ ok = true;
+ return;
+
+ case InferType::InferTypeKind::INTEGRAL: {
+ if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+ {
+ ok = true;
+ return;
+ }
+ else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+ {
+ ok = true;
+ return;
+ }
+ }
+ break;
+
+ case InferType::InferTypeKind::FLOAT: {
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ ok = true;
+ return;
+ }
+ else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+ {
+ ok = true;
+ return;
+ }
+ }
+ break;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const CharType &type) override
+ {
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+ }
+
+ void visit (const ReferenceType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const PointerType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const ParamType &) override { ok = true; }
+
+ void visit (const DynamicObjectType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+ void visit (const ClosureType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ ok = true;
+ return;
+ }
+
+ BaseCmp::visit (type);
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const InferType *base;
+};
+
+class FnCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ FnCmp (const FnType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+ }
+
+ void visit (const FnType &type) override
+ {
+ if (base->num_params () != type.num_params ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto a = base->param_at (i).second;
+ auto b = type.param_at (i).second;
+
+ if (!a->can_eq (b, emit_error_flag))
+ {
+ emit_error_flag = false;
+ BaseCmp::visit (type);
+ return;
+ }
+ }
+
+ if (!base->get_return_type ()->can_eq (type.get_return_type (),
+ emit_error_flag))
+ {
+ emit_error_flag = false;
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const FnType *base;
+};
+
+class FnptrCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ FnptrCmp (const FnPtr *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+ void visit (const FnPtr &type) override
+ {
+ if (base->num_params () != type.num_params ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ auto this_ret_type = base->get_return_type ();
+ auto other_ret_type = type.get_return_type ();
+ if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto this_param = base->param_at (i);
+ auto other_param = type.param_at (i);
+ if (!this_param->can_eq (other_param, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+ }
+
+ ok = true;
+ }
+
+ void visit (const FnType &type) override
+ {
+ if (base->num_params () != type.num_params ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ auto this_ret_type = base->get_return_type ();
+ auto other_ret_type = type.get_return_type ();
+ if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto this_param = base->param_at (i);
+ auto other_param = type.param_at (i).second;
+ if (!this_param->can_eq (other_param, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const FnPtr *base;
+};
+
+class ClosureCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ClosureCmp (const ClosureType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ClosureType *base;
+};
+
+class ArrayCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ArrayCmp (const ArrayType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const ArrayType &type) override
+ {
+ // check base type
+ const BaseType *base_element = base->get_element_type ();
+ const BaseType *other_element = type.get_element_type ();
+ if (!base_element->can_eq (other_element, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ArrayType *base;
+};
+
+class SliceCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ SliceCmp (const SliceType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const SliceType &type) override
+ {
+ // check base type
+ const BaseType *base_element = base->get_element_type ();
+ const BaseType *other_element = type.get_element_type ();
+ if (!base_element->can_eq (other_element, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const SliceType *base;
+};
+
+class BoolCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ BoolCmp (const BoolType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const BoolType &type) override { ok = true; }
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const BoolType *base;
+};
+
+class IntCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ IntCmp (const IntType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+ }
+
+ void visit (const IntType &type) override
+ {
+ ok = type.get_int_kind () == base->get_int_kind ();
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const IntType *base;
+};
+
+class UintCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ UintCmp (const UintType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+ }
+
+ void visit (const UintType &type) override
+ {
+ ok = type.get_uint_kind () == base->get_uint_kind ();
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const UintType *base;
+};
+
+class FloatCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ FloatCmp (const FloatType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () != InferType::InferTypeKind::INTEGRAL;
+ }
+
+ void visit (const FloatType &type) override
+ {
+ ok = type.get_float_kind () == base->get_float_kind ();
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const FloatType *base;
+};
+
+class ADTCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ADTCmp (const ADTType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const ADTType &type) override
+ {
+ if (base->get_adt_kind () != type.get_adt_kind ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ if (base->number_of_variants () != type.number_of_variants ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < type.number_of_variants (); ++i)
+ {
+ TyTy::VariantDef *a = base->get_variants ().at (i);
+ TyTy::VariantDef *b = type.get_variants ().at (i);
+
+ if (a->num_fields () != b->num_fields ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t j = 0; j < a->num_fields (); j++)
+ {
+ TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+ TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+ TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+ TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+ if (!this_field_ty->can_eq (other_field_ty, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+ }
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ADTType *base;
+};
+
+class TupleCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ TupleCmp (const TupleType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const TupleType &type) override
+ {
+ if (base->num_fields () != type.num_fields ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_fields (); i++)
+ {
+ BaseType *bo = base->get_field (i);
+ BaseType *fo = type.get_field (i);
+
+ if (!bo->can_eq (fo, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const TupleType *base;
+};
+
+class USizeCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ USizeCmp (const USizeType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+ }
+
+ void visit (const USizeType &type) override { ok = true; }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const USizeType *base;
+};
+
+class ISizeCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ISizeCmp (const ISizeType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+ }
+
+ void visit (const ISizeType &type) override { ok = true; }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ISizeType *base;
+};
+
+class CharCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ CharCmp (const CharType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const InferType &type) override
+ {
+ ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+ }
+
+ void visit (const CharType &type) override { ok = true; }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const CharType *base;
+};
+
+class ReferenceCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ReferenceCmp (const ReferenceType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const ReferenceType &type) override
+ {
+ auto base_type = base->get_base ();
+ auto other_base_type = type.get_base ();
+
+ bool mutability_match = base->is_mutable () == type.is_mutable ();
+ if (!mutability_match)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ if (!base_type->can_eq (other_base_type, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ReferenceType *base;
+};
+
+class PointerCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ PointerCmp (const PointerType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const PointerType &type) override
+ {
+ auto base_type = base->get_base ();
+ auto other_base_type = type.get_base ();
+
+ // rust is permissive about mutablity here you can always go from mutable to
+ // immutable but not the otherway round
+ bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+ if (!mutability_ok)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ if (!base_type->can_eq (other_base_type, emit_error_flag))
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const PointerType *base;
+};
+
+class ParamCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ ParamCmp (const ParamType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ // param types are a placeholder we shouldn't have cases where we unify
+ // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+ //
+ // foo<i32>{ a: 123 }.
+ // Then this enforces the i32 type to be referenced on the
+ // field via an hirid.
+ //
+ // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+ // to handle the typing of the struct
+ bool can_eq (const BaseType *other) override
+ {
+ if (!base->can_resolve ())
+ return BaseCmp::can_eq (other);
+
+ auto lookup = base->resolve ();
+ return lookup->can_eq (other, emit_error_flag);
+ }
+
+ // imagine the case where we have:
+ // struct Foo<T>(T);
+ // Then we declare a generic impl block
+ // impl <X>Foo<X> { ... }
+ // both of these types are compatible so we mostly care about the number of
+ // generic arguments
+ void visit (const ParamType &) override { ok = true; }
+
+ void visit (const InferType &) override { ok = true; }
+
+ void visit (const FnType &) override { ok = true; }
+
+ void visit (const FnPtr &) override { ok = true; }
+
+ void visit (const ADTType &) override { ok = true; }
+
+ void visit (const ArrayType &) override { ok = true; }
+
+ void visit (const SliceType &) override { ok = true; }
+
+ void visit (const BoolType &) override { ok = true; }
+
+ void visit (const IntType &) override { ok = true; }
+
+ void visit (const UintType &) override { ok = true; }
+
+ void visit (const USizeType &) override { ok = true; }
+
+ void visit (const ISizeType &) override { ok = true; }
+
+ void visit (const FloatType &) override { ok = true; }
+
+ void visit (const CharType &) override { ok = true; }
+
+ void visit (const ReferenceType &) override { ok = true; }
+
+ void visit (const PointerType &) override { ok = true; }
+
+ void visit (const StrType &) override { ok = true; }
+
+ void visit (const NeverType &) override { ok = true; }
+
+ void visit (const DynamicObjectType &) override { ok = true; }
+
+ void visit (const PlaceholderType &type) override
+ {
+ ok = base->get_symbol ().compare (type.get_symbol ()) == 0;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const ParamType *base;
+};
+
+class StrCmp : public BaseCmp
+{
+ // FIXME we will need a enum for the StrType like ByteBuf etc..
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ StrCmp (const StrType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const StrType &type) override { ok = true; }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const StrType *base;
+};
+
+class NeverCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ NeverCmp (const NeverType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const NeverType &type) override { ok = true; }
+
+ void visit (const InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ ok = true;
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+ const NeverType *base;
+};
+
+class PlaceholderCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ PlaceholderCmp (const PlaceholderType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ bool can_eq (const BaseType *other) override
+ {
+ if (!base->can_resolve ())
+ return BaseCmp::can_eq (other);
+
+ BaseType *lookup = base->resolve ();
+ return lookup->can_eq (other, emit_error_flag);
+ }
+
+ void visit (const TupleType &) override { ok = true; }
+
+ void visit (const ADTType &) override { ok = true; }
+
+ void visit (const InferType &) override { ok = true; }
+
+ void visit (const FnType &) override { ok = true; }
+
+ void visit (const FnPtr &) override { ok = true; }
+
+ void visit (const ArrayType &) override { ok = true; }
+
+ void visit (const BoolType &) override { ok = true; }
+
+ void visit (const IntType &) override { ok = true; }
+
+ void visit (const UintType &) override { ok = true; }
+
+ void visit (const USizeType &) override { ok = true; }
+
+ void visit (const ISizeType &) override { ok = true; }
+
+ void visit (const FloatType &) override { ok = true; }
+
+ void visit (const ErrorType &) override { ok = true; }
+
+ void visit (const CharType &) override { ok = true; }
+
+ void visit (const ReferenceType &) override { ok = true; }
+
+ void visit (const ParamType &) override { ok = true; }
+
+ void visit (const StrType &) override { ok = true; }
+
+ void visit (const NeverType &) override { ok = true; }
+
+ void visit (const SliceType &) override { ok = true; }
+
+private:
+ const BaseType *get_base () const override { return base; }
+
+ const PlaceholderType *base;
+};
+
+class DynamicCmp : public BaseCmp
+{
+ using Rust::TyTy::BaseCmp::visit;
+
+public:
+ DynamicCmp (const DynamicObjectType *base, bool emit_errors)
+ : BaseCmp (base, emit_errors), base (base)
+ {}
+
+ void visit (const DynamicObjectType &type) override
+ {
+ if (base->num_specified_bounds () != type.num_specified_bounds ())
+ {
+ BaseCmp::visit (type);
+ return;
+ }
+
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ ok = base->bounds_compatible (type, ref_locus, false);
+ }
+
+private:
+ const BaseType *get_base () const override { return base; }
+
+ const DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_CMP_H
diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h
new file mode 100644
index 00000000000..77d912a5921
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-rules.h
@@ -0,0 +1,1584 @@
+// 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_RULES
+#define RUST_TYTY_RULES
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+/* Rules specify how to unify two Ty. For example, the result of unifying the
+ two tuples (u64, A) and (B, i64) would be (u64, i64).
+
+ Performing a unification requires a double dispatch. To illustrate, suppose
+ we want to unify `ty1` and `ty2`. Here's what it looks like:
+ 1. The caller calls `ty1.unify(ty2)`. This is the first dispatch.
+ 2. `ty1` creates a rule specific to its type(e.g. TupleRules).
+ 3. The rule calls `ty2.accept_vis(rule)`. This is the second dispatch.
+ 4. `ty2` calls `rule.visit(*this)`, which will method-overload to the
+ correct implementation at compile time.
+
+ The nice thing about Rules is that they seperate unification logic from the
+ representation of Ty. To support unifying a new Ty, implement its
+ `accept_vis` and `unify` method to pass the unification request to Rules.
+ Then, create a new `XXXRules` class and implement one `visit` method for
+ every Ty it can unify with. */
+class BaseRules : public TyVisitor
+{
+public:
+ virtual ~BaseRules () {}
+
+ /* Unify two ty. Returns a pointer to the newly-created unified ty, or nullptr
+ if the two types cannot be unified. The caller is responsible for releasing
+ the memory of the returned ty.
+
+ This method is meant to be used internally by Ty. If you're trying to unify
+ two ty, you can simply call `unify` on ty themselves. */
+ virtual BaseType *unify (BaseType *other)
+ {
+ if (other->get_kind () == TypeKind::PARAM)
+ {
+ ParamType *p = static_cast<ParamType *> (other);
+ other = p->resolve ();
+ }
+ else if (other->get_kind () == TypeKind::PLACEHOLDER)
+ {
+ PlaceholderType *p = static_cast<PlaceholderType *> (other);
+ if (p->can_resolve ())
+ {
+ other = p->resolve ();
+ return get_base ()->unify (other);
+ }
+ }
+ else if (other->get_kind () == TypeKind::PROJECTION)
+ {
+ ProjectionType *p = static_cast<ProjectionType *> (other);
+ other = p->get ();
+ return get_base ()->unify (other);
+ }
+
+ other->accept_vis (*this);
+ if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+ return resolved;
+
+ resolved->append_reference (get_base ()->get_ref ());
+ resolved->append_reference (other->get_ref ());
+ for (auto ref : get_base ()->get_combined_refs ())
+ resolved->append_reference (ref);
+ for (auto ref : other->get_combined_refs ())
+ resolved->append_reference (ref);
+
+ other->append_reference (resolved->get_ref ());
+ other->append_reference (get_base ()->get_ref ());
+ get_base ()->append_reference (resolved->get_ref ());
+ get_base ()->append_reference (other->get_ref ());
+
+ bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER;
+ bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER;
+ bool results_is_non_general_infer_var
+ = (result_is_infer_var
+ && (static_cast<InferType *> (resolved))->get_infer_kind ()
+ != TyTy::InferType::GENERAL);
+ if (result_resolved || results_is_non_general_infer_var)
+ {
+ for (auto &ref : resolved->get_combined_refs ())
+ {
+ TyTy::BaseType *ref_tyty = nullptr;
+ bool ok = context->lookup_type (ref, &ref_tyty);
+ if (!ok)
+ continue;
+
+ // if any of the types are inference variables lets fix them
+ if (ref_tyty->get_kind () == TyTy::TypeKind::INFER)
+ {
+ context->insert_type (
+ Analysis::NodeMapping (mappings->get_current_crate (),
+ UNKNOWN_NODEID, ref,
+ UNKNOWN_LOCAL_DEFID),
+ resolved->clone ());
+ }
+ }
+ }
+ return resolved;
+ }
+
+ virtual void visit (TupleType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ADTType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (InferType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (FnType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (FnPtr &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ArrayType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (SliceType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (BoolType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (IntType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (UintType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (USizeType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ISizeType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (FloatType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ErrorType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (CharType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ReferenceType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (PointerType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ParamType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (StrType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (NeverType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (PlaceholderType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ProjectionType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (DynamicObjectType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+ virtual void visit (ClosureType &type) override
+ {
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+ RichLocation r (ref_locus);
+ r.add_range (base_locus);
+ rust_error_at (r, "expected [%s] got [%s]",
+ get_base ()->as_string ().c_str (),
+ type.as_string ().c_str ());
+ }
+
+protected:
+ BaseRules (BaseType *base)
+ : mappings (Analysis::Mappings::get ()),
+ context (Resolver::TypeCheckContext::get ()),
+ resolved (new ErrorType (base->get_ref (), base->get_ref ()))
+ {}
+
+ Analysis::Mappings *mappings;
+ Resolver::TypeCheckContext *context;
+
+ /* Temporary storage for the result of a unification.
+ We could return the result directly instead of storing it in the rule
+ object, but that involves modifying the visitor pattern to accommodate
+ the return value, which is too complex. */
+ BaseType *resolved;
+
+private:
+ /* Returns a pointer to the ty that created this rule. */
+ virtual BaseType *get_base () = 0;
+};
+
+class InferRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ InferRules (InferType *base) : BaseRules (base), base (base) {}
+
+ void visit (BoolType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (IntType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (UintType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (USizeType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (ISizeType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind ()
+ == TyTy::InferType::InferTypeKind::INTEGRAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (FloatType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+ || (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (ArrayType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (SliceType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (ADTType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (TupleType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (InferType &type) override
+ {
+ switch (base->get_infer_kind ())
+ {
+ case InferType::InferTypeKind::GENERAL:
+ resolved = type.clone ();
+ return;
+
+ case InferType::InferTypeKind::INTEGRAL: {
+ if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+ {
+ resolved = type.clone ();
+ return;
+ }
+ else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+ {
+ resolved = base->clone ();
+ return;
+ }
+ }
+ break;
+
+ case InferType::InferTypeKind::FLOAT: {
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ resolved = type.clone ();
+ return;
+ }
+ else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+ {
+ resolved = base->clone ();
+ return;
+ }
+ }
+ break;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (CharType &type) override
+ {
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+ }
+
+ void visit (ReferenceType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (PointerType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (ParamType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (DynamicObjectType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+ void visit (ClosureType &type) override
+ {
+ bool is_valid
+ = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+ if (is_valid)
+ {
+ resolved = type.clone ();
+ return;
+ }
+
+ BaseRules::visit (type);
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ InferType *base;
+};
+
+class FnRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ FnRules (FnType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (FnType &type) override
+ {
+ if (base->num_params () != type.num_params ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto a = base->param_at (i).second;
+ auto b = type.param_at (i).second;
+
+ auto unified_param = a->unify (b);
+ if (unified_param == nullptr)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+ }
+
+ auto unified_return
+ = base->get_return_type ()->unify (type.get_return_type ());
+ if (unified_return == nullptr)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ FnType *base;
+};
+
+class FnptrRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ FnptrRules (FnPtr *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (FnPtr &type) override
+ {
+ auto this_ret_type = base->get_return_type ();
+ auto other_ret_type = type.get_return_type ();
+ auto unified_result = this_ret_type->unify (other_ret_type);
+ if (unified_result == nullptr
+ || unified_result->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ if (base->num_params () != type.num_params ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto this_param = base->param_at (i);
+ auto other_param = type.param_at (i);
+ auto unified_param = this_param->unify (other_param);
+ if (unified_param == nullptr
+ || unified_param->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (FnType &type) override
+ {
+ auto this_ret_type = base->get_return_type ();
+ auto other_ret_type = type.get_return_type ();
+ auto unified_result = this_ret_type->unify (other_ret_type);
+ if (unified_result == nullptr
+ || unified_result->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ if (base->num_params () != type.num_params ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < base->num_params (); i++)
+ {
+ auto this_param = base->param_at (i);
+ auto other_param = type.param_at (i).second;
+ auto unified_param = this_param->unify (other_param);
+ if (unified_param == nullptr
+ || unified_param->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ FnPtr *base;
+};
+
+class ClosureRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ClosureRules (ClosureType *base) : BaseRules (base), base (base) {}
+
+ // TODO
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ClosureType *base;
+};
+
+class ArrayRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ArrayRules (ArrayType *base) : BaseRules (base), base (base) {}
+
+ void visit (ArrayType &type) override
+ {
+ // check base type
+ auto base_resolved
+ = base->get_element_type ()->unify (type.get_element_type ());
+ if (base_resolved == nullptr)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved
+ = new ArrayType (type.get_ref (), type.get_ty_ref (),
+ type.get_ident ().locus, type.get_capacity_expr (),
+ TyVar (base_resolved->get_ref ()));
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ArrayType *base;
+};
+
+class SliceRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ SliceRules (SliceType *base) : BaseRules (base), base (base) {}
+
+ void visit (SliceType &type) override
+ {
+ // check base type
+ auto base_resolved
+ = base->get_element_type ()->unify (type.get_element_type ());
+ if (base_resolved == nullptr)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = new SliceType (type.get_ref (), type.get_ty_ref (),
+ type.get_ident ().locus,
+ TyVar (base_resolved->get_ref ()));
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ SliceType *base;
+};
+
+class BoolRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ BoolRules (BoolType *base) : BaseRules (base), base (base) {}
+
+ void visit (BoolType &type) override
+ {
+ resolved = new BoolType (type.get_ref (), type.get_ty_ref ());
+ }
+
+ void visit (InferType &type) override
+ {
+ switch (type.get_infer_kind ())
+ {
+ case InferType::InferTypeKind::GENERAL:
+ resolved = base->clone ();
+ break;
+
+ default:
+ BaseRules::visit (type);
+ break;
+ }
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ BoolType *base;
+};
+
+class IntRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ IntRules (IntType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ // cant assign a float inference variable
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (IntType &type) override
+ {
+ if (type.get_int_kind () != base->get_int_kind ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved
+ = new IntType (type.get_ref (), type.get_ty_ref (), type.get_int_kind ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ IntType *base;
+};
+
+class UintRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ UintRules (UintType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ // cant assign a float inference variable
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (UintType &type) override
+ {
+ if (type.get_uint_kind () != base->get_uint_kind ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = new UintType (type.get_ref (), type.get_ty_ref (),
+ type.get_uint_kind ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ UintType *base;
+};
+
+class FloatRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ FloatRules (FloatType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (FloatType &type) override
+ {
+ if (type.get_float_kind () != base->get_float_kind ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = new FloatType (type.get_ref (), type.get_ty_ref (),
+ type.get_float_kind ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ FloatType *base;
+};
+
+class ADTRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ADTRules (ADTType *base) : BaseRules (base), base (base) {}
+
+ void visit (ADTType &type) override
+ {
+ if (base->get_adt_kind () != type.get_adt_kind ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ if (base->number_of_variants () != type.number_of_variants ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ for (size_t i = 0; i < type.number_of_variants (); ++i)
+ {
+ TyTy::VariantDef *a = base->get_variants ().at (i);
+ TyTy::VariantDef *b = type.get_variants ().at (i);
+
+ if (a->num_fields () != b->num_fields ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ for (size_t j = 0; j < a->num_fields (); j++)
+ {
+ TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+ TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+ TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+ TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+ BaseType *unified_ty = this_field_ty->unify (other_field_ty);
+ if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+ return;
+ }
+ }
+
+ // generic args for the unit-struct case
+ if (type.is_unit () && base->is_unit ())
+ {
+ rust_assert (type.get_num_substitutions ()
+ == base->get_num_substitutions ());
+
+ for (size_t i = 0; i < type.get_num_substitutions (); i++)
+ {
+ auto &a = base->get_substs ().at (i);
+ auto &b = type.get_substs ().at (i);
+
+ auto pa = a.get_param_ty ();
+ auto pb = b.get_param_ty ();
+
+ auto res = pa->unify (pb);
+ if (res->get_kind () == TyTy::TypeKind::ERROR)
+ {
+ return;
+ }
+ }
+ }
+
+ resolved = type.clone ();
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ADTType *base;
+};
+
+class TupleRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ TupleRules (TupleType *base) : BaseRules (base), base (base) {}
+
+ void visit (TupleType &type) override
+ {
+ if (base->num_fields () != type.num_fields ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ std::vector<TyVar> fields;
+ for (size_t i = 0; i < base->num_fields (); i++)
+ {
+ BaseType *bo = base->get_field (i);
+ BaseType *fo = type.get_field (i);
+
+ BaseType *unified_ty = bo->unify (fo);
+ if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+ return;
+
+ fields.push_back (TyVar (unified_ty->get_ref ()));
+ }
+
+ resolved = new TyTy::TupleType (type.get_ref (), type.get_ty_ref (),
+ type.get_ident ().locus, fields);
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ TupleType *base;
+};
+
+class USizeRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ USizeRules (USizeType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ // cant assign a float inference variable
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (USizeType &type) override { resolved = type.clone (); }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ USizeType *base;
+};
+
+class ISizeRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ISizeRules (ISizeType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ // cant assign a float inference variable
+ if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (ISizeType &type) override { resolved = type.clone (); }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ISizeType *base;
+};
+
+class CharRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ CharRules (CharType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+ void visit (CharType &type) override { resolved = type.clone (); }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ CharType *base;
+};
+
+class ReferenceRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ReferenceRules (ReferenceType *base) : BaseRules (base), base (base) {}
+
+ void visit (ReferenceType &type) override
+ {
+ auto base_type = base->get_base ();
+ auto other_base_type = type.get_base ();
+
+ TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+ if (base_resolved == nullptr
+ || base_resolved->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ // rust is permissive about mutablity here you can always go from mutable to
+ // immutable but not the otherway round
+ bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+ if (!mutability_ok)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = new ReferenceType (base->get_ref (), base->get_ty_ref (),
+ TyVar (base_resolved->get_ref ()),
+ base->mutability ());
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ReferenceType *base;
+};
+
+class PointerRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ PointerRules (PointerType *base) : BaseRules (base), base (base) {}
+
+ void visit (PointerType &type) override
+ {
+ auto base_type = base->get_base ();
+ auto other_base_type = type.get_base ();
+
+ TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+ if (base_resolved == nullptr
+ || base_resolved->get_kind () == TypeKind::ERROR)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ // rust is permissive about mutablity here you can always go from mutable to
+ // immutable but not the otherway round
+ bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+ if (!mutability_ok)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = new PointerType (base->get_ref (), base->get_ty_ref (),
+ TyVar (base_resolved->get_ref ()),
+ base->mutability ());
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ resolved->set_ref (type.get_ref ());
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ PointerType *base;
+};
+
+class ParamRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ ParamRules (ParamType *base) : BaseRules (base), base (base) {}
+
+ // param types are a placeholder we shouldn't have cases where we unify
+ // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+ //
+ // foo<i32>{ a: 123 }.
+ // Then this enforces the i32 type to be referenced on the
+ // field via an hirid.
+ //
+ // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+ // to handle the typing of the struct
+ BaseType *unify (BaseType *other) override final
+ {
+ if (!base->can_resolve ())
+ return BaseRules::unify (other);
+
+ auto lookup = base->resolve ();
+ return lookup->unify (other);
+ }
+
+ void visit (ParamType &type) override
+ {
+ if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = type.clone ();
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ ParamType *base;
+};
+
+class StrRules : public BaseRules
+{
+ // FIXME we will need a enum for the StrType like ByteBuf etc..
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ StrRules (StrType *base) : BaseRules (base), base (base) {}
+
+ void visit (StrType &type) override { resolved = type.clone (); }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ StrType *base;
+};
+
+class NeverRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ NeverRules (NeverType *base) : BaseRules (base), base (base) {}
+
+ void visit (NeverType &type) override { resolved = type.clone (); }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ NeverType *base;
+};
+
+class PlaceholderRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ PlaceholderRules (PlaceholderType *base) : BaseRules (base), base (base) {}
+
+ BaseType *unify (BaseType *other) override final
+ {
+ if (!base->can_resolve ())
+ return BaseRules::unify (other);
+
+ BaseType *lookup = base->resolve ();
+ return lookup->unify (other);
+ }
+
+ void visit (PlaceholderType &type) override
+ {
+ if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = type.clone ();
+ }
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ PlaceholderType *base;
+};
+
+class DynamicRules : public BaseRules
+{
+ using Rust::TyTy::BaseRules::visit;
+
+public:
+ DynamicRules (DynamicObjectType *base) : BaseRules (base), base (base) {}
+
+ void visit (InferType &type) override
+ {
+ if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ }
+
+ void visit (DynamicObjectType &type) override
+ {
+ if (base->num_specified_bounds () != type.num_specified_bounds ())
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ Location ref_locus = mappings->lookup_location (type.get_ref ());
+ if (!base->bounds_compatible (type, ref_locus, true))
+ {
+ BaseRules::visit (type);
+ return;
+ }
+
+ resolved = base->clone ();
+ }
+
+private:
+ BaseType *get_base () override { return base; }
+
+ DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_RULES