Skip to content

Silence unnecessary "missing dyn" errors and tweak E0746 suggestions #122957

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

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
84a80f8
Rework `impl Trait` and `Box<dyn Trait>` return type suggestions in E…
estebank Mar 21, 2024
68c9974
Account for `type Alias = dyn Trait;` in unsized return suggestion
estebank Mar 21, 2024
deaff65
Centralize calculation of FullfillmentError span
estebank Mar 22, 2024
1ddaf9e
On implicit `Sized` bound on fn argument, point at type instead of pa…
estebank Mar 23, 2024
fdb5cc7
Tweak `Sized` obligation spans on fn argument `impl Trait`
estebank Mar 23, 2024
e36b49f
Silence "missing `dyn`" error if "`!Sized` type" error is already emi…
estebank Mar 23, 2024
20ed449
Remove unnecessary `info!`
estebank Mar 23, 2024
8ae7b86
On `fn` with no borrowed argument, explain that `&dyn Trait` could ha…
estebank Mar 23, 2024
4bbc212
fix clippy test
estebank Mar 23, 2024
3533606
On fn argument obligation, point at type instead of pattern
estebank Mar 30, 2024
f777d7a
Remove `FullfillmentError::span` method
estebank Mar 30, 2024
9253052
Remove unused argument
estebank Mar 30, 2024
7907667
Deduplicate `?Sized` binding errors to only their declaration, not use
estebank Apr 1, 2024
b037c8b
Add test for calling fn with unsized return type
estebank Apr 1, 2024
e0035fc
Account for `-> ?Sized` fn calls when deduplicating unsized local errors
estebank Apr 1, 2024
6a79c05
Drive-by formatting change: import types used from `hir` instead of `…
estebank Apr 1, 2024
19dca1a
Further dedup unsized locals errors involving method calls
estebank Apr 1, 2024
cd9d746
Fix rebase: `Node::Local` rename
estebank Apr 1, 2024
2002699
Do not error on `let ref x: str = *"";`
estebank Apr 2, 2024
24869fe
Account for tuples and structs in unsized locals errors for more accu…
estebank Apr 2, 2024
c8a20a7
Emit single explanatory note on unsized locals and fn params instead …
estebank Apr 2, 2024
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
8 changes: 8 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,14 @@ impl<'hir> QPath<'hir> {
QPath::LangItem(_, span) => span,
}
}

pub fn res(&self) -> Option<Res> {
match self {
QPath::LangItem(..) => None,
QPath::Resolved(_, path) => Some(path.res),
QPath::TypeRelative(_, path_segment) => Some(path_segment.res),
}
}
}

/// Hints at the original code for a let statement.
Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable);
if is_object_safe {
diag.multipart_suggestion_verbose(
"alternatively, you can return an owned trait object",
"alternatively, you can return a boxed trait object",
vec![
(ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
(ty.span.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
} else if is_downgradable {
// We'll emit the object safety error already, with a structured suggestion.
}
if is_downgradable
&& (matches!(self_ty.kind, hir::TyKind::TraitObject(_, _, TraitObjectSyntax::None))
|| !is_object_safe)
{
// We'll emit the object safety or `!Sized` error already, with a structured
// suggestion.
diag.downgrade_to_delayed_bug();
}
return true;
Expand Down Expand Up @@ -242,6 +247,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
diag.downgrade_to_delayed_bug();
}
} else {
if let hir::TyKind::TraitObject(.., TraitObjectSyntax::None) = self_ty.kind
&& is_downgradable
{
// We'll emit a `Sized` error already, with a structured suggestion.
diag.downgrade_to_delayed_bug();
}
let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
// There are more than one trait bound, we need surrounding parentheses.
vec![
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub(super) fn check_fn<'a, 'tcx>(
if !params_can_be_unsized {
fcx.require_type_is_sized(
param_ty,
param.pat.span,
param.ty_span,
// ty.span == binding_span iff this is a closure parameter with no type ascription,
// or if it's an implicit `self` parameter
traits::SizedArgumentType(
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.require_type_is_sized_deferred(
input,
span,
traits::SizedArgumentType(None),
traits::SizedArgumentType(args.get(i).map(|e| e.hir_id)),
);
}
}
Expand All @@ -600,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.require_type_is_sized_deferred(
output,
call.map_or(expr.span, |e| e.span),
traits::SizedCallReturnType,
traits::SizedCallReturnType(call.map(|e| e.hir_id)),
);
}

Expand Down
139 changes: 129 additions & 10 deletions compiler/rustc_hir_typeck/src/gather_locals.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::FnCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::PatKind;
Expand Down Expand Up @@ -61,11 +62,12 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
// *distinct* cases. so track when we are hitting a pattern *within* an fn
// parameter.
outermost_fn_param_pat: Option<(Span, hir::HirId)>,
pat_expr_map: FxHashMap<hir::HirId, Span>,
}

impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
Self { fcx, outermost_fn_param_pat: None }
Self { fcx, outermost_fn_param_pat: None, pat_expr_map: Default::default() }
}

fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
Expand All @@ -92,18 +94,57 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
/// again during type checking by querying [`FnCtxt::local_ty`] for the same hir_id.
fn declare(&mut self, decl: Declaration<'tcx>) {
let local_ty = match decl.ty {
Some(ref ty) => {
let o_ty = self.fcx.lower_ty(ty);
Some(ref hir_ty) => {
let o_ty = self.fcx.lower_ty(hir_ty);

let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw));
debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
debug!(?hir_ty.hir_id, ?o_ty, ?c_ty, "visit_local");
self.fcx
.typeck_results
.borrow_mut()
.user_provided_types_mut()
.insert(ty.hir_id, c_ty);
.insert(hir_ty.hir_id, c_ty);

Some(o_ty.normalized)
let ty = o_ty.normalized;
match decl.pat.kind {
// We explicitly allow `let ref x: str = *"";`
hir::PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes(_), _), ..) => {}
// We explicitly allow `let _: dyn Trait;` and allow the `visit_pat` check to
// handle `let (x, _): (sized, str) = *r;`. Otherwise with the later we'd
// complain incorrectly about the `str` that is otherwise unused.
_ if {
let mut is_wild = false;
decl.pat.walk(|pat| {
if let hir::PatKind::Wild = pat.kind {
is_wild = true;
false
} else {
true
}
});
is_wild
} => {}
_ => {
if self.outermost_fn_param_pat.is_some() {
if !self.fcx.tcx.features().unsized_fn_params {
self.fcx.require_type_is_sized(
ty,
hir_ty.span,
traits::SizedArgumentType(Some(decl.pat.hir_id)),
);
}
} else {
if !self.fcx.tcx.features().unsized_locals {
self.fcx.require_type_is_sized(
ty,
hir_ty.span,
traits::VariableType(decl.pat.hir_id),
);
}
}
}
}
Some(ty)
}
None => None,
};
Expand All @@ -117,18 +158,95 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
}
}

/// Builds a correspondence mapping between the bindings in a pattern and the expression that
/// originates the value. This is then used on unsized locals errors to point at the sub expression
/// That corresponds to the sub-pattern with the `?Sized` type, instead of the binding.
///
/// This is somewhat limited, as it only supports bindings, tuples and structs for now, falling back
/// on pointing at the binding otherwise.
struct JointVisitorExpr<'hir> {
pat: &'hir hir::Pat<'hir>,
expr: &'hir hir::Expr<'hir>,
map: FxHashMap<hir::HirId, Span>,
}

impl<'hir> JointVisitorExpr<'hir> {
fn walk(&mut self) {
match (self.pat.kind, self.expr.kind) {
(hir::PatKind::Tuple(pat_fields, pos), hir::ExprKind::Tup(expr_fields))
if pat_fields.len() == expr_fields.len() && pos.as_opt_usize() == None =>
{
for (pat, expr) in pat_fields.iter().zip(expr_fields.iter()) {
self.map.insert(pat.hir_id, expr.span);
let mut v = JointVisitorExpr { pat, expr, map: Default::default() };
v.walk();
self.map.extend(v.map);
}
}
(hir::PatKind::Binding(..), hir::ExprKind::MethodCall(path, ..)) => {
self.map.insert(self.pat.hir_id, path.ident.span);
}
(hir::PatKind::Binding(..), _) => {
self.map.insert(self.pat.hir_id, self.expr.span);
}
(
hir::PatKind::Struct(pat_path, pat_fields, _),
hir::ExprKind::Struct(call_path, expr_fields, _),
) if pat_path.res() == call_path.res() && pat_path.res().is_some() => {
for (pat_field, expr_field) in pat_fields.iter().zip(expr_fields.iter()) {
self.map.insert(pat_field.hir_id, expr_field.span);
let mut v = JointVisitorExpr {
pat: pat_field.pat,
expr: expr_field.expr,
map: Default::default(),
};
v.walk();
self.map.extend(v.map);
}
}
(
hir::PatKind::TupleStruct(pat_path, pat_fields, _),
hir::ExprKind::Call(
hir::Expr { kind: hir::ExprKind::Path(expr_path), .. },
expr_fields,
),
) if pat_path.res() == expr_path.res() && pat_path.res().is_some() => {
for (pat, expr) in pat_fields.iter().zip(expr_fields.iter()) {
self.map.insert(pat.hir_id, expr.span);
let mut v = JointVisitorExpr { pat: pat, expr: expr, map: Default::default() };
v.walk();
self.map.extend(v.map);
}
}
_ => {}
}
}
}

impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
// Add explicitly-declared locals.
fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) {
self.declare(local.into());
intravisit::walk_local(self, local)
if let Some(init) = local.init {
let mut v = JointVisitorExpr { pat: &local.pat, expr: &init, map: Default::default() };
v.walk();
self.pat_expr_map.extend(v.map);
}
intravisit::walk_local(self, local);
}

fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Let(let_expr) = expr.kind {
let mut v = JointVisitorExpr {
pat: &let_expr.pat,
expr: &let_expr.init,
map: Default::default(),
};
v.walk();
self.pat_expr_map.extend(v.map);
self.declare((let_expr, expr.hir_id).into());
}
intravisit::walk_expr(self, expr)
intravisit::walk_expr(self, expr);
}

fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
Expand All @@ -147,7 +265,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
if !self.fcx.tcx.features().unsized_fn_params {
self.fcx.require_type_is_sized(
var_ty,
p.span,
ty_span,
// ty_span == ident.span iff this is a closure parameter with no type
// ascription, or if it's an implicit `self` parameter
traits::SizedArgumentType(
Expand All @@ -163,7 +281,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
}
} else {
if !self.fcx.tcx.features().unsized_locals {
self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
let span = *self.pat_expr_map.get(&p.hir_id).unwrap_or(&p.span);
self.fcx.require_type_is_sized(var_ty, span, traits::VariableType(p.hir_id));
}
}

Expand Down
36 changes: 18 additions & 18 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use crate::ty::{GenericArgsRef, TyCtxt};

use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{HirId, MatchSource};
use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
Expand Down Expand Up @@ -262,10 +262,10 @@ pub enum ObligationCauseCode<'tcx> {
/// expression that caused the obligation, and the `usize`
/// indicates exactly which predicate it is in the list of
/// instantiated predicates.
ExprItemObligation(DefId, rustc_hir::HirId, usize),
ExprItemObligation(DefId, HirId, usize),

/// Combines `ExprItemObligation` and `BindingObligation`.
ExprBindingObligation(DefId, Span, rustc_hir::HirId, usize),
ExprBindingObligation(DefId, Span, HirId, usize),

/// A type like `&'a T` is WF only if `T: 'a`.
ReferenceOutlivesReferent(Ty<'tcx>),
Expand All @@ -287,13 +287,13 @@ pub enum ObligationCauseCode<'tcx> {
/// `S { ... }` must be `Sized`.
StructInitializerSized,
/// Type of each variable must be `Sized`.
VariableType(hir::HirId),
VariableType(HirId),
/// Argument type must be `Sized`.
SizedArgumentType(Option<hir::HirId>),
SizedArgumentType(Option<HirId>),
/// Return type must be `Sized`.
SizedReturnType,
/// Return type of a call expression must be `Sized`.
SizedCallReturnType,
SizedCallReturnType(Option<HirId>),
/// Yield type must be `Sized`.
SizedYieldType,
/// Inline asm operand type must be `Sized`.
Expand Down Expand Up @@ -335,9 +335,9 @@ pub enum ObligationCauseCode<'tcx> {

FunctionArgumentObligation {
/// The node of the relevant argument in the function call.
arg_hir_id: hir::HirId,
arg_hir_id: HirId,
/// The node of the function call.
call_hir_id: hir::HirId,
call_hir_id: HirId,
/// The obligation introduced by this argument.
parent_code: InternedObligationCauseCode<'tcx>,
},
Expand Down Expand Up @@ -402,18 +402,18 @@ pub enum ObligationCauseCode<'tcx> {
ReturnNoExpression,

/// `return` with an expression
ReturnValue(hir::HirId),
ReturnValue(HirId),

/// Opaque return type of this function
OpaqueReturnType(Option<(Ty<'tcx>, Span)>),

/// Block implicit return
BlockTailExpression(hir::HirId, hir::MatchSource),
BlockTailExpression(HirId, MatchSource),

/// #[feature(trivial_bounds)] is not enabled
TrivialBound,

AwaitableExpr(hir::HirId),
AwaitableExpr(HirId),

ForLoopIterator,

Expand All @@ -431,8 +431,8 @@ pub enum ObligationCauseCode<'tcx> {
MatchImpl(ObligationCause<'tcx>, DefId),

BinOp {
lhs_hir_id: hir::HirId,
rhs_hir_id: Option<hir::HirId>,
lhs_hir_id: HirId,
rhs_hir_id: Option<HirId>,
rhs_span: Option<Span>,
rhs_is_lit: bool,
output_ty: Option<Ty<'tcx>>,
Expand Down Expand Up @@ -562,14 +562,14 @@ pub enum StatementAsExpression {
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeVisitable, TypeFoldable)]
pub struct MatchExpressionArmCause<'tcx> {
pub arm_block_id: Option<hir::HirId>,
pub arm_block_id: Option<HirId>,
pub arm_ty: Ty<'tcx>,
pub arm_span: Span,
pub prior_arm_block_id: Option<hir::HirId>,
pub prior_arm_block_id: Option<HirId>,
pub prior_arm_ty: Ty<'tcx>,
pub prior_arm_span: Span,
pub scrut_span: Span,
pub source: hir::MatchSource,
pub source: MatchSource,
pub prior_non_diverging_arms: Vec<Span>,
// Is the expectation of this match expression an RPIT?
pub tail_defines_return_position_impl_trait: Option<LocalDefId>,
Expand All @@ -578,8 +578,8 @@ pub struct MatchExpressionArmCause<'tcx> {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
pub struct IfExpressionCause<'tcx> {
pub then_id: hir::HirId,
pub else_id: hir::HirId,
pub then_id: HirId,
pub else_id: HirId,
pub then_ty: Ty<'tcx>,
pub else_ty: Ty<'tcx>,
pub outer_span: Option<Span>,
Expand Down
Loading