Skip to content

Commit

Permalink
gccrs: Fix compilation of trait-items which map to impl items
Browse files Browse the repository at this point in the history
When we have paths such as Try::from_error the Try references the Trait
and then from_error references the from_error trait item. So this resolves
directly to a trait implementation which has the type:

  fn <Self> (v: placeholder) -> Self

Remember that a path such as: Try::from_error gets handled by doing a
clever substitution: <T? as Try>::from_error

The main piece here is that we resolve this function type and for
monomoprhization we know this is a trait call but we know that all trait's
have an implicit Self type param which in this case is Result<i32, i32>
so when it comes to knowing which impl block this is we got rid of the old
bad insert/lookup receiver hack and use the specified type to know which
impl block we are talking about to generate the function.

The hard part here is inside rust-compil-item.cc, where we have the
'concete' type which is the trait item fntype of:

  fn <Result<i32, i32>> (v : i32) -> Result<i32,i32>

This does not really match the signiture of the impl item for Result
which is:

  fn <T, E> (v: i32) -> Result<T, E>

So what we need to do is actually infer this by inputing inference
variables on the impl fntype then unify'ing the trait object to this to
compute the types of this to monomorphize this.

Fixes #3381

gcc/rust/ChangeLog:

	* backend/rust-compile-expr.cc (CompileExpr::visit): remove receiver interface
	* backend/rust-compile-item.cc (CompileItem::visit): monomorphize trait to impl item
	* backend/rust-compile-resolve-path.cc (HIRCompileBase::query_compile): use trait item Self
	* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit): remove receiver interface
	(TypeCheckExpr::resolve_fn_trait_call): likewise
	* typecheck/rust-hir-type-check-path.cc (TypeCheckExpr::visit): likewise
	(TypeCheckExpr::resolve_segments): likewise
	* typecheck/rust-hir-type-check-type.cc (TypeCheckType::visit): likewise
	* typecheck/rust-hir-type-check.h: likewise
	* typecheck/rust-typecheck-context.cc (TypeCheckContext::insert_receiver): remove
	(TypeCheckContext::lookup_receiver): remove

gcc/testsuite/ChangeLog:

	* rust/execute/torture/issue-3381.rs: New test.

Signed-off-by: Philip Herron <[email protected]>
  • Loading branch information
philberty committed Feb 4, 2025
1 parent 68e24e2 commit d3c9821
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 46 deletions.
8 changes: 4 additions & 4 deletions gcc/rust/backend/rust-compile-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1394,8 +1394,8 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup_fntype);

TyTy::BaseType *receiver = nullptr;
ok = ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
&receiver);
ok = ctx->get_tyctx ()->lookup_type (
expr.get_receiver ().get_mappings ().get_hirid (), &receiver);
rust_assert (ok);

bool is_dyn_dispatch
Expand Down Expand Up @@ -1532,8 +1532,8 @@ CompileExpr::resolve_operator_overload (

TyTy::BaseType *receiver = nullptr;
bool ok
= ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
&receiver);
= ctx->get_tyctx ()->lookup_type (lhs_expr.get_mappings ().get_hirid (),
&receiver);
rust_assert (ok);

bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
Expand Down
27 changes: 25 additions & 2 deletions gcc/rust/backend/rust-compile-item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "rust-compile-item.h"
#include "rust-compile-implitem.h"
#include "rust-compile-extern.h"
#include "rust-substitution-mapper.h"
#include "rust-type-util.h"
#include "rust-immutable-name-resolution-context.h"

namespace Rust {
Expand Down Expand Up @@ -165,12 +167,33 @@ CompileItem::visit (HIR::Function &function)
// is given
if (concrete == nullptr)
return;
else

rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *concrete_fnty = static_cast<TyTy::FnType *> (concrete);
bool is_trait_item_concrete
= ctx->get_mappings ()
.lookup_trait_item_defid (concrete_fnty->get_id ())
.has_value ();
if (!is_trait_item_concrete)
{
rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
fntype = static_cast<TyTy::FnType *> (concrete);
fntype->monomorphize ();
}
else
{
TyTy::BaseType *infer
= Resolver::SubstMapper::InferSubst (fntype, function.get_locus ());
TyTy::BaseType *resolved
= Resolver::unify_site (function.get_mappings ().get_hirid (),
TyTy::TyWithLocation (infer),
TyTy::TyWithLocation (concrete),
function.get_locus ());

rust_assert (resolved->is<TyTy::FnType> ());
fntype = resolved->as<TyTy::FnType> ();
}

fntype->monomorphize ();
}
else
{
Expand Down
16 changes: 10 additions & 6 deletions gcc/rust/backend/rust-compile-resolve-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,20 @@ HIRCompileBase::query_compile (HirId ref, TyTy::BaseType *lookup,
trait->get_mappings ().get_defid (), &trait_ref);
rust_assert (ok);

TyTy::BaseType *receiver = nullptr;
ok = ctx->get_tyctx ()->lookup_receiver (mappings.get_hirid (),
&receiver);
rust_assert (ok);
receiver = receiver->destructure ();

// the type resolver can only resolve type bounds to their trait
// item so its up to us to figure out if this path should resolve
// to an trait-impl-block-item or if it can be defaulted to the
// trait-impl-item's definition
//
// because we know this is resolved to a trait item we can actually
// just grab the Self type parameter here for the receiver to match
// the appropriate impl block

rust_assert (lookup->is<TyTy::FnType> ());
auto fn = lookup->as<TyTy::FnType> ();
rust_assert (fn->get_num_type_params () > 0);
auto &self = fn->get_substs ().at (0);
auto receiver = self.get_param_ty ();
auto candidates
= Resolver::PathProbeImplTrait::Probe (receiver, final_segment,
trait_ref);
Expand Down
6 changes: 0 additions & 6 deletions gcc/rust/typecheck/rust-hir-type-check-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1105,8 +1105,6 @@ TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
return;
}

context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);

rust_debug_loc (expr.get_locus (), "attempting to resolve method for %s",
receiver_tyty->debug_str ().c_str ());
auto candidates
Expand Down Expand Up @@ -1750,9 +1748,6 @@ TypeCheckExpr::resolve_operator_overload (
context->insert_autoderef_mappings (expr.get_lvalue_mappings ().get_hirid (),
std::move (candidate.adjustments));

// now its just like a method-call-expr
context->insert_receiver (expr.get_mappings ().get_hirid (), lhs);

PathProbeCandidate &resolved_candidate = candidate.candidate;
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
NodeId resolved_node_id
Expand Down Expand Up @@ -1997,7 +1992,6 @@ TypeCheckExpr::resolve_fn_trait_call (HIR::CallExpr &expr,
HirId autoderef_mappings_id = fnexpr.get_mappings ().get_hirid ();
context->insert_autoderef_mappings (autoderef_mappings_id,
std::move (candidate.adjustments));
context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);

PathProbeCandidate &resolved_candidate = candidate.candidate;
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
Expand Down
3 changes: 0 additions & 3 deletions gcc/rust/typecheck/rust-hir-type-check-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
root_resolved_node_id);
}
context->insert_receiver (expr.get_mappings ().get_hirid (), root);
return;
}

Expand Down Expand Up @@ -559,8 +558,6 @@ TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
}

rust_assert (resolved_node_id != UNKNOWN_NODEID);
context->insert_receiver (expr_mappings.get_hirid (), prev_segment);

if (flag_name_resolution_2_0)
{
auto &nr_ctx = const_cast<Resolver2_0::NameResolutionContext &> (
Expand Down
4 changes: 0 additions & 4 deletions gcc/rust/typecheck/rust-hir-type-check-type.cc
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,6 @@ TypeCheckType::visit (HIR::QualifiedPathInType &path)
resolver->insert_resolved_type (path.get_mappings ().get_nodeid (),
root_resolved_node_id);
}

context->insert_receiver (path.get_mappings ().get_hirid (), root);
return;
}

Expand Down Expand Up @@ -704,9 +702,7 @@ TypeCheckType::resolve_segments (
}
}

context->insert_receiver (expr_mappings.get_hirid (), prev_segment);
rust_assert (resolved_node_id != UNKNOWN_NODEID);

if (flag_name_resolution_2_0)
{
auto &nr_ctx = const_cast<Resolver2_0::NameResolutionContext &> (
Expand Down
4 changes: 0 additions & 4 deletions gcc/rust/typecheck/rust-hir-type-check.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,6 @@ class TypeCheckContext
void insert_trait_reference (DefId id, TraitReference &&ref);
bool lookup_trait_reference (DefId id, TraitReference **ref);

void insert_receiver (HirId id, TyTy::BaseType *t);
bool lookup_receiver (HirId id, TyTy::BaseType **ref);

void insert_associated_trait_impl (HirId id,
AssociatedImplTrait &&associated);
bool lookup_associated_trait_impl (HirId id,
Expand Down Expand Up @@ -282,7 +279,6 @@ class TypeCheckContext
std::vector<TyTy::BaseType *> loop_type_stack;
StackedContexts<TypeCheckBlockContextItem> block_stack;
std::map<DefId, TraitReference> trait_context;
std::map<HirId, TyTy::BaseType *> receiver_context;
std::map<HirId, AssociatedImplTrait> associated_impl_traits;

// trait-id -> list of < self-tyty:impl-id>
Expand Down
17 changes: 0 additions & 17 deletions gcc/rust/typecheck/rust-typecheck-context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,23 +254,6 @@ TypeCheckContext::lookup_trait_reference (DefId id, TraitReference **ref)
return true;
}

void
TypeCheckContext::insert_receiver (HirId id, TyTy::BaseType *t)
{
receiver_context[id] = t;
}

bool
TypeCheckContext::lookup_receiver (HirId id, TyTy::BaseType **ref)
{
auto it = receiver_context.find (id);
if (it == receiver_context.end ())
return false;

*ref = it->second;
return true;
}

void
TypeCheckContext::insert_associated_trait_impl (
HirId id, AssociatedImplTrait &&associated)
Expand Down
90 changes: 90 additions & 0 deletions gcc/testsuite/rust/execute/torture/issue-3381.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* { dg-output "Err: 15\r*\n" } */
#[lang = "sized"]
trait Sized {}

enum Result<T, E> {
#[lang = "Ok"]
Ok(T),
#[lang = "Err"]
Err(E),
}

#[lang = "try"]
pub trait Try {
type Ok;
type Error;

#[lang = "into_result"]
#[unstable(feature = "try_trait", issue = "42327")]
fn into_result(self) -> Result<Self::Ok, Self::Error>;

#[lang = "from_error"]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_error(v: Self::Ok) -> Self;

#[lang = "from_ok"]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_ok(v: Self::Error) -> Self;
}

impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;

fn into_result(self) -> Result<T, E> {
self
}

fn from_ok(v: T) -> Self {
Result::Ok(v)
}

fn from_error(v: E) -> Self {
Result::Err(v)
}
}

pub trait From<T>: Sized {
fn from(_: T) -> Self;
}

impl<T> From<T> for T {
fn from(t: T) -> Self {
t
}
}

fn print(s: &str, value: i32) {
extern "C" {
fn printf(s: *const i8, ...);
}

unsafe {
printf(s as *const str as *const i8, value);
}
}

fn baz() -> Result<i32, i32> {
Result::Err(15)
}

fn foo() -> Result<i32, i32> {
let b = match baz() {
Result::Ok(value) => value,
Result::Err(err) => {
return Try::from_error(From::from(err));
}
};

Result::Ok(15 + b)
}

fn main() -> i32 {
let a = foo();
match a {
Result::Ok(value) => print("Ok: %i\n", value),
Result::Err(err) => print("Err: %i\n", err),
};

0
}

0 comments on commit d3c9821

Please sign in to comment.