Rollup merge of #126699 - Bryanskiy:delegation-coercion, r=compiler-errors
Delegation: support coercion for target expression (solves https://github.com/rust-lang/rust/issues/118212#issuecomment-2160723092) The implementation consist of 2 parts. Firstly, method call is generated instead of fully qualified call in AST->HIR lowering if there were no generic arguments or `Qpath` were provided. These restrictions are imposed due to the loss of information after desugaring. For example in ```rust trait Trait { fn foo(&self) {} } reuse <u8 as Trait>::foo; ``` We would like to generate such a code: ```rust fn foo<u8: Trait>(x: &u8) { x.foo(x) } ``` however, the signature is inherited during HIR analysis where `u8` was discarded. Then, we probe the single pre-resolved method. P.S In the future, we would like to avoid restrictions on the callee path by `Self` autoref/autoderef in fully qualified calls, but at the moment it didn't work out. r? `@petrochenkov`
This commit is contained in:
commit
36ea06827b
@ -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};
|
||||
@ -259,8 +259,7 @@ fn lower_delegation_body(
|
||||
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)
|
||||
@ -273,26 +272,81 @@ fn lower_delegation_body(
|
||||
})
|
||||
}
|
||||
|
||||
// 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> {
|
||||
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)))
|
||||
.unwrap_or_default()
|
||||
&& delegation.qself.is_none()
|
||||
&& !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(
|
||||
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),
|
||||
|
@ -182,8 +182,13 @@ pub fn lookup_method(
|
||||
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,
|
||||
|
@ -20,6 +20,7 @@
|
||||
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};
|
||||
@ -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,
|
||||
|
||||
@ -480,12 +484,35 @@ pub(crate) fn probe_op<OP, R>(
|
||||
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)
|
||||
})
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
|
|
||||
@ -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
|
||||
|
|
||||
@ -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`.
|
||||
|
@ -24,8 +24,8 @@ pub fn zero_args() -> i32 { 15 }
|
||||
|
||||
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) }
|
||||
}
|
||||
|
@ -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 }
|
||||
@ -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`
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
||||
@ -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.
|
||||
|
25
tests/ui/delegation/method-call-choice.rs
Normal file
25
tests/ui/delegation/method-call-choice.rs
Normal file
@ -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
tests/ui/delegation/method-call-choice.stderr
Normal file
21
tests/ui/delegation/method-call-choice.stderr
Normal file
@ -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`.
|
34
tests/ui/delegation/method-call-priority.rs
Normal file
34
tests/ui/delegation/method-call-priority.rs
Normal file
@ -0,0 +1,34 @@
|
||||
//@ run-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait Trait1 {
|
||||
fn foo(&self) -> i32 { 1 }
|
||||
}
|
||||
|
||||
trait Trait2 {
|
||||
fn foo(&self) -> i32 { 2 }
|
||||
}
|
||||
|
||||
struct F;
|
||||
impl Trait1 for F {}
|
||||
impl Trait2 for F {}
|
||||
|
||||
impl F {
|
||||
fn foo(&self) -> i32 { 3 }
|
||||
}
|
||||
|
||||
struct S(F);
|
||||
|
||||
impl Trait1 for S {
|
||||
// Make sure that the generated `self.0.foo()` does not turn into the inherent method `F::foo`
|
||||
// that has a higher priority than methods from traits.
|
||||
reuse Trait1::foo { self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(F);
|
||||
assert_eq!(s.foo(), 1);
|
||||
}
|
26
tests/ui/delegation/self-coercion.rs
Normal file
26
tests/ui/delegation/self-coercion.rs
Normal file
@ -0,0 +1,26 @@
|
||||
//@ run-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait : Sized {
|
||||
fn by_value(self) -> i32 { 1 }
|
||||
fn by_mut_ref(&mut self) -> i32 { 2 }
|
||||
fn by_ref(&self) -> i32 { 3 }
|
||||
}
|
||||
|
||||
struct F;
|
||||
impl Trait for F {}
|
||||
|
||||
struct S(F);
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut s = S(F);
|
||||
assert_eq!(s.by_ref(), 3);
|
||||
assert_eq!(s.by_mut_ref(), 2);
|
||||
assert_eq!(s.by_value(), 1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user