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

Rollup of 6 pull requests #127784

Closed
wants to merge 17 commits into from
Closed
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
77 changes: 62 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,74 @@ 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> {
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
// instead of fully qualified call for the self type coercion.
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,
None,
);
let segment = self.arena.alloc(segment);

let method_call_id = self.next_id();
if let Some(traits) = self.resolver.trait_map.remove(&delegation.id) {
self.trait_map.insert(method_call_id.local_id, traits.into_boxed_slice());
}

self.arena.alloc(hir::Expr {
hir_id: method_call_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),
Expand Down
65 changes: 63 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#![allow(rustc::untranslatable_diagnostic)]

use either::Either;
use hir::ClosureKind;
use hir::{ClosureKind, Path};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
Expand All @@ -16,6 +16,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, LangItem};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::VarDebugInfoContents;
use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
Expand Down Expand Up @@ -546,7 +547,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
self.suggest_cloning(err, ty, expr, None, Some(move_spans));
}
}
if let Some(pat) = finder.pat {

self.suggest_ref_for_dbg_args(expr, place, move_span, err);

// it's useless to suggest inserting `ref` when the span don't comes from local code
if let Some(pat) = finder.pat
&& !move_span.is_dummy()
&& !self.infcx.tcx.sess.source_map().is_imported(move_span)
{
*in_pattern = true;
let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
if let Some(pat) = finder.parent_pat {
Expand All @@ -561,6 +569,59 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
}
}

// for dbg!(x) which may take ownership, suggest dbg!(&x) instead
// but here we actually do not check whether the macro name is `dbg!`
// so that we may extend the scope a bit larger to cover more cases
fn suggest_ref_for_dbg_args(
&self,
body: &hir::Expr<'_>,
place: &Place<'tcx>,
move_span: Span,
err: &mut Diag<'infcx>,
) {
let var_info = self.body.var_debug_info.iter().find(|info| match info.value {
VarDebugInfoContents::Place(ref p) => p == place,
_ => false,
});
let arg_name = if let Some(var_info) = var_info {
var_info.name
} else {
return;
};
struct MatchArgFinder {
expr_span: Span,
match_arg_span: Option<Span>,
arg_name: Symbol,
}
impl Visitor<'_> for MatchArgFinder {
fn visit_expr(&mut self, e: &hir::Expr<'_>) {
// dbg! is expanded into a match pattern, we need to find the right argument span
if let hir::ExprKind::Match(expr, ..) = &e.kind
&& let hir::ExprKind::Path(hir::QPath::Resolved(
_,
path @ Path { segments: [seg], .. },
)) = &expr.kind
&& seg.ident.name == self.arg_name
&& self.expr_span.source_callsite().contains(expr.span)
{
self.match_arg_span = Some(path.span);
}
hir::intravisit::walk_expr(self, e);
}
}

let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name };
finder.visit_expr(body);
if let Some(macro_arg_span) = finder.match_arg_span {
err.span_suggestion_verbose(
macro_arg_span.shrink_to_lo(),
"consider borrowing instead of transferring ownership",
"&",
Applicability::MachineApplicable,
);
}
}

fn report_use_of_uninitialized(
&self,
mpi: MovePathIndex,
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_const_eval/src/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,13 +995,25 @@ where
}

/// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
/// Immutable strings are deduplicated and stored in global memory.
pub fn allocate_str(
&mut self,
str: &str,
kind: MemoryKind<M::MemoryKind>,
mutbl: Mutability,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?;
let tcx = self.tcx.tcx;

// Use cache for immutable strings.
let ptr = if mutbl.is_not() {
// Use dedup'd allocation function.
let id = tcx.allocate_bytes_dedup(str.as_bytes());

// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))?
} else {
self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?
};
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
let layout = self.layout_of(self.tcx.types.str_).unwrap();
Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout))
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(true),
callee_ty.peel_refs(),
callee_expr.unwrap().hir_id,
None,
TraitsInScope,
|mut ctxt| ctxt.probe_for_similar_candidate(),
)
Expand Down Expand Up @@ -948,6 +949,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&mut err,
);

self.suggest_deref_unwrap_or(
&mut err,
error_span,
callee_ty,
call_ident,
expected_ty,
provided_ty,
provided_args[*provided_idx],
is_method,
);

// Call out where the function is defined
self.label_fn_like(
&mut err,
Expand Down
69 changes: 69 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
true
}

// Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
#[instrument(level = "trace", skip(self, err, provided_expr))]
pub(crate) fn suggest_deref_unwrap_or(
&self,
err: &mut Diag<'_>,
error_span: Span,
callee_ty: Option<Ty<'tcx>>,
call_ident: Option<Ident>,
expected_ty: Ty<'tcx>,
provided_ty: Ty<'tcx>,
provided_expr: &Expr<'tcx>,
is_method: bool,
) {
if !is_method {
return;
}
let Some(callee_ty) = callee_ty else {
return;
};
let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
return;
};
let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
"Option"
} else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
"Result"
} else {
return;
};

let Some(call_ident) = call_ident else {
return;
};
if call_ident.name != sym::unwrap_or {
return;
}

let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
return;
};

// NOTE: Can we reuse `suggest_deref_or_ref`?

// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
&& let ty::Infer(_) = elem_ty.kind()
&& size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
{
let slice = Ty::new_slice(self.tcx, *elem_ty);
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
} else {
provided_ty
};

if !self.can_coerce(expected_ty, dummy_ty) {
return;
}
let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
err.multipart_suggestion_verbose(
msg,
vec![
(call_ident.span, "map_or".to_owned()),
(provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
],
Applicability::MachineApplicable,
);
}

/// Suggest wrapping the block in square brackets instead of curly braces
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
pub(crate) fn suggest_block_to_brackets(
Expand Down Expand Up @@ -1610,6 +1678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(true),
self_ty,
expr.hir_id,
None,
ProbeScope::TraitsInScope,
)
{
Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(true),
self_ty,
call_expr_id,
None,
ProbeScope::TraitsInScope,
) {
Ok(pick) => {
Expand Down Expand Up @@ -182,8 +183,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 pick = self.lookup_probe(
segment.ident,
self_ty,
call_expr,
segment.res.opt_def_id(),
ProbeScope::TraitsInScope,
)?;

self.lint_edition_dependent_dot_call(
self_ty, segment, span, call_expr, self_expr, &pick, args,
Expand All @@ -208,6 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
segment.ident,
trait_type,
call_expr,
None,
ProbeScope::TraitsInScope,
) {
Ok(ref new_pick) if pick.differs_from(new_pick) => {
Expand Down Expand Up @@ -276,6 +283,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
method_name: Ident,
self_ty: Ty<'tcx>,
call_expr: &hir::Expr<'_>,
expected_def_id: Option<DefId>,
scope: ProbeScope,
) -> probe::PickResult<'tcx> {
let pick = self.probe_for_name(
Expand All @@ -285,6 +293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(false),
self_ty,
call_expr.hir_id,
expected_def_id,
scope,
)?;
pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id);
Expand All @@ -306,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(true),
self_ty,
call_expr.hir_id,
None,
scope,
)?;
Ok(pick)
Expand Down Expand Up @@ -529,6 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
IsSuggestion(false),
self_ty,
expr_id,
None,
ProbeScope::TraitsInScope,
);
let pick = match (pick, struct_variant) {
Expand Down
Loading
Loading