Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delegation: support coercion for target expression #126699

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 69 additions & 15 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

use crate::{ImplTraitPosition, ResolverAstLoweringExt};

use super::{ImplTraitContext, LoweringContext, ParamMode};
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};

use ast::visit::Visitor;
use hir::def::{DefKind, PartialRes, Res};
Expand Down Expand Up @@ -259,8 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self_param_id: pat_node_id,
};
self_resolver.visit_block(block);
let block = this.lower_block(block, false);
this.mk_expr(hir::ExprKind::Block(block, None), block.span)
this.lower_target_expr(&block)
} else {
let pat_hir_id = this.lower_node_id(pat_node_id);
this.generate_arg(pat_hir_id, span)
Expand All @@ -273,26 +272,81 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}

// Generates fully qualified call for the resulting body.
// FIXME(fn_delegation): Alternatives for target expression lowering:
// https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> {
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
if block.stmts.len() == 1
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
{
return self.lower_expr_mut(expr);
}

let block = self.lower_block(block, false);
self.mk_expr(hir::ExprKind::Block(block, None), block.span)
}

// Generates expression for the resulting body. If possible, `MethodCall` is used
// to allow autoref/autoderef for target expression. For example in:
//
// trait Trait : Sized {
// fn by_value(self) -> i32 { 1 }
// fn by_mut_ref(&mut self) -> i32 { 2 }
// fn by_ref(&self) -> i32 { 3 }
// }
//
// struct NewType(SomeType);
// impl Trait for NewType {
// reuse Trait::* { self.0 }
// }
//
// `self.0` will automatically coerce.
fn finalize_body_lowering(
&mut self,
delegation: &Delegation,
args: Vec<hir::Expr<'hir>>,
span: Span,
) -> hir::Expr<'hir> {
let path = self.lower_qpath(
delegation.id,
&delegation.qself,
&delegation.path,
ParamMode::Optional,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

let args = self.arena.alloc_from_iter(args);
let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span));

let has_generic_args =
delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());

let call = if self
.get_resolution_id(delegation.id, span)
.and_then(|def_id| Ok(self.has_self(def_id, span)))
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
.unwrap_or_default()
&& delegation.qself.is_none()
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
&& !has_generic_args
{
let ast_segment = delegation.path.segments.last().unwrap();
let segment = self.lower_path_segment(
delegation.path.span,
ast_segment,
ParamMode::Optional,
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
let segment = self.arena.alloc(segment);

self.arena.alloc(hir::Expr {
hir_id: self.next_id(),
kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span),
span,
})
} else {
let path = self.lower_qpath(
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
delegation.id,
&delegation.qself,
&delegation.path,
ParamMode::Optional,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span))
};
let block = self.arena.alloc(hir::Block {
stmts: &[],
expr: Some(call),
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
let pick =
self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
let scope = if let Some(only_method) = segment.res.opt_def_id() {
ProbeScope::Single(only_method)
} else {
ProbeScope::TraitsInScope
};

let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?;

self.lint_edition_dependent_dot_call(
self_ty, segment, span, call_expr, self_expr, &pick, args,
Expand Down
33 changes: 30 additions & 3 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::AssocItem;
use rustc_middle::ty::AssocItemContainer;
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::Upcast;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
Expand Down Expand Up @@ -216,6 +217,9 @@ pub enum Mode {

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum ProbeScope {
// Single candidate coming from pre-resolved delegation method.
Single(DefId),

// Assemble candidates coming only from traits in scope.
TraitsInScope,

Expand Down Expand Up @@ -480,12 +484,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
is_suggestion,
);

probe_cx.assemble_inherent_candidates();
match scope {
ProbeScope::TraitsInScope => {
probe_cx.assemble_extension_candidates_for_traits_in_scope()
probe_cx.assemble_inherent_candidates();
probe_cx.assemble_extension_candidates_for_traits_in_scope();
}
ProbeScope::AllTraits => {
probe_cx.assemble_inherent_candidates();
probe_cx.assemble_extension_candidates_for_all_traits();
}
ProbeScope::Single(def_id) => {
let item = self.tcx.associated_item(def_id);
// FIXME(fn_delegation): Delegation to inherent methods is not yet supported.
assert_eq!(item.container, AssocItemContainer::TraitContainer);

let trait_def_id = self.tcx.parent(def_id);
let trait_span = self.tcx.def_span(trait_def_id);

let trait_args = self.fresh_args_for_item(trait_span, trait_def_id);
let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args);

probe_cx.push_candidate(
Candidate {
item,
kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)),
import_ids: smallvec![],
},
false,
);
}
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
};
op(probe_cx)
})
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/delegation/bad-resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ impl Trait for S {

reuse foo { &self.0 }
//~^ ERROR cannot find function `foo` in this scope
reuse Trait::foo2 { self.0 }
//~^ ERROR cannot find function `foo2` in trait `Trait`
//~| ERROR method `foo2` is not a member of trait `Trait`
}

mod prefix {}
Expand Down
24 changes: 21 additions & 3 deletions tests/ui/delegation/bad-resolve.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ LL | reuse <F as Trait>::baz;
| | help: there is an associated function with a similar name: `bar`
| not a member of trait `Trait`

error[E0407]: method `foo2` is not a member of trait `Trait`
--> $DIR/bad-resolve.rs:37:5
|
LL | reuse Trait::foo2 { self.0 }
| ^^^^^^^^^^^^^----^^^^^^^^^^^
| | |
| | help: there is an associated function with a similar name: `foo`
| not a member of trait `Trait`

error[E0423]: expected function, found associated constant `Trait::C`
--> $DIR/bad-resolve.rs:24:11
|
Expand Down Expand Up @@ -54,6 +63,15 @@ error[E0425]: cannot find function `foo` in this scope
LL | reuse foo { &self.0 }
| ^^^ not found in this scope

error[E0425]: cannot find function `foo2` in trait `Trait`
--> $DIR/bad-resolve.rs:37:18
|
LL | fn foo(&self, x: i32) -> i32 { x }
| ---------------------------- similarly named associated function `foo` defined here
...
LL | reuse Trait::foo2 { self.0 }
| ^^^^ help: an associated function with a similar name exists: `foo`

error[E0046]: not all trait items implemented, missing: `Type`
--> $DIR/bad-resolve.rs:22:1
|
Expand All @@ -64,18 +82,18 @@ LL | impl Trait for S {
| ^^^^^^^^^^^^^^^^ missing `Type` in implementation

error[E0433]: failed to resolve: use of undeclared crate or module `unresolved_prefix`
--> $DIR/bad-resolve.rs:40:7
--> $DIR/bad-resolve.rs:43:7
|
LL | reuse unresolved_prefix::{a, b, c};
| ^^^^^^^^^^^^^^^^^ use of undeclared crate or module `unresolved_prefix`

error[E0433]: failed to resolve: `crate` in paths can only be used in start position
--> $DIR/bad-resolve.rs:41:29
--> $DIR/bad-resolve.rs:44:29
|
LL | reuse prefix::{self, super, crate};
| ^^^^^ `crate` in paths can only be used in start position

error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

Some errors have detailed explanations: E0046, E0324, E0407, E0423, E0425, E0433, E0575, E0576.
For more information about an error, try `rustc --explain E0046`.
4 changes: 2 additions & 2 deletions tests/ui/delegation/explicit-paths-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ reuse to_reuse::zero_args { self }

struct S(F);
impl Trait for S {
reuse Trait::bar { &self.0 }
reuse Trait::description { &self.0 }
reuse Trait::bar { self.0 }
reuse Trait::description { self.0 }
reuse <F as Trait>::static_method;
reuse <F as Trait>::static_method2 { S::static_method(self) }
}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/delegation/explicit-paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod inherent_impl_assoc_fn_to_other {
use crate::*;

impl S {
reuse Trait::foo1 { &self.0 }
reuse Trait::foo1 { self.0 }
reuse <S as Trait>::foo2;
reuse to_reuse::foo3;
reuse F::foo4 { &self.0 }
Expand All @@ -46,7 +46,7 @@ mod trait_impl_assoc_fn_to_other {
use crate::*;

impl Trait for S {
reuse Trait::foo1 { &self.0 }
reuse Trait::foo1 { self.0 }
reuse <F as Trait>::foo2;
reuse to_reuse::foo3;
//~^ ERROR method `foo3` is not a member of trait `Trait`
Expand Down
9 changes: 8 additions & 1 deletion tests/ui/delegation/explicit-paths.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,17 @@ error[E0308]: mismatched types
LL | trait Trait2 : Trait {
| -------------------- found this type parameter
LL | reuse <F as Trait>::foo1 { self }
| ^^^^ expected `&F`, found `&Self`
| ---- ^^^^ expected `&F`, found `&Self`
| |
| arguments to this function are incorrect
|
= note: expected reference `&F`
found reference `&Self`
note: method defined here
--> $DIR/explicit-paths.rs:5:8
|
LL | fn foo1(&self, x: i32) -> i32 { x }
| ^^^^ -----

error[E0277]: the trait bound `S2: Trait` is not satisfied
--> $DIR/explicit-paths.rs:78:16
Expand Down
25 changes: 16 additions & 9 deletions tests/ui/delegation/ice-issue-122550.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@ error[E0308]: mismatched types
LL | fn description(&self) -> &str {}
| ^^ expected `&str`, found `()`

error[E0308]: mismatched types
--> $DIR/ice-issue-122550.rs:13:39
|
LL | reuse <S as Trait>::description { &self.0 }
| ^^^^^^^ expected `&S`, found `&F`
|
= note: expected reference `&S`
found reference `&F`

error[E0277]: the trait bound `S: Trait` is not satisfied
--> $DIR/ice-issue-122550.rs:13:12
|
Expand All @@ -25,6 +16,22 @@ help: this trait has no implementations, consider adding one
LL | trait Trait {
| ^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/ice-issue-122550.rs:13:39
|
LL | reuse <S as Trait>::description { &self.0 }
| ----------- ^^^^^^^ expected `&S`, found `&F`
| |
| arguments to this function are incorrect
|
= note: expected reference `&S`
found reference `&F`
note: method defined here
--> $DIR/ice-issue-122550.rs:5:8
|
LL | fn description(&self) -> &str {}
| ^^^^^^^^^^^ -----

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0308.
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/delegation/method-call-choice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![feature(fn_delegation)]
#![allow(incomplete_features)]

trait Trait {
fn foo(&self) {}
}

struct F;
impl Trait for F {}
struct S(F);

pub mod to_reuse {
use crate::F;

pub fn foo(_: &F) {}
}

impl Trait for S {
// Make sure that the method call is not generated if the path resolution
// does not have a `self` parameter.
reuse to_reuse::foo { self.0 }
//~^ ERROR mismatched types
}

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/delegation/method-call-choice.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0308]: mismatched types
--> $DIR/method-call-choice.rs:21:27
|
LL | reuse to_reuse::foo { self.0 }
| --- ^^^^^^ expected `&F`, found `F`
| |
| arguments to this function are incorrect
|
note: function defined here
--> $DIR/method-call-choice.rs:15:12
|
LL | pub fn foo(_: &F) {}
| ^^^ -----
help: consider borrowing here
|
LL | reuse to_reuse::foo { &self.0 }
| +

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
Loading
Loading