Skip to content
Closed
12 changes: 6 additions & 6 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,17 +240,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
ty::Tuple(ref tys) => {
let mut has_emitted = false;
let spans = if let hir::ExprKind::Tup(comps) = &expr.kind {
let comps = if let hir::ExprKind::Tup(comps) = expr.kind {
debug_assert_eq!(comps.len(), tys.len());
comps.iter().map(|e| e.span).collect()
comps
} else {
vec![]
&[]
};
for (i, ty) in tys.iter().enumerate() {
let descr_post = &format!(" in tuple element {}", i);
let span = *spans.get(i).unwrap_or(&span);
if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural_len)
{
let e = comps.get(i).unwrap_or(expr);
let span = e.span;
if check_must_use_ty(cx, ty, e, span, descr_pre, descr_post, plural_len) {
has_emitted = true;
}
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ pub enum ObligationCauseCode<'tcx> {

/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
MatchImpl(ObligationCause<'tcx>, DefId),

BinOp {
rhs_span: Option<Span>,
is_lit: bool,
},
}

/// The 'location' at which we try to perform HIR-based wf checking.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
err.span_label(enclosing_scope_span, s.as_str());
}

self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ pub trait InferCtxtExt<'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
span: Span,
);

fn suggest_floating_point_literal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'_>,
trait_ref: &ty::PolyTraitRef<'tcx>,
);
}

fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
Expand Down Expand Up @@ -1910,8 +1917,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| ObligationCauseCode::AwaitableExpr(_)
| ObligationCauseCode::ForLoopIterator
| ObligationCauseCode::QuestionMark
| ObligationCauseCode::CheckAssociatedTypeBounds { .. }
| ObligationCauseCode::LetElse
| ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {}
| ObligationCauseCode::BinOp { .. } => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
Expand Down Expand Up @@ -2497,6 +2505,32 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
}
}

fn suggest_floating_point_literal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut DiagnosticBuilder<'_>,
trait_ref: &ty::PolyTraitRef<'tcx>,
) {
let rhs_span = match obligation.cause.code() {
ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span,
_ => return,
};
match (
trait_ref.skip_binder().self_ty().kind(),
trait_ref.skip_binder().substs.type_at(1).kind(),
) {
(ty::Float(_), ty::Infer(InferTy::IntVar(_))) => {
err.span_suggestion_verbose(
rhs_span.shrink_to_hi(),
"consider using a floating-point literal by writing it with `.0`",
String::from(".0"),
Applicability::MaybeIncorrect,
);
}
_ => {}
}
}
}

/// Collect all the returned expressions within the input expression.
Expand Down
29 changes: 25 additions & 4 deletions compiler/rustc_typeck/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::layout::MAX_SIMD_LANES;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, Ty, TyCtxt};
Expand Down Expand Up @@ -417,10 +417,31 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Spa
// have UB during initialization if they are uninhabited, but there also seems to be no good
// reason to allow any statics to be uninhabited.
let ty = tcx.type_of(def_id);
let Ok(layout) = tcx.layout_of(ParamEnv::reveal_all().and(ty)) else {
let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
Ok(l) => l,
// Foreign statics that overflow their allowed size should emit an error
Err(LayoutError::SizeOverflow(_))
if {
let node = tcx.hir().get_by_def_id(def_id);
matches!(
node,
hir::Node::ForeignItem(hir::ForeignItem {
kind: hir::ForeignItemKind::Static(..),
..
})
)
} =>
{
tcx.sess
.struct_span_err(span, "extern static is too large for the current architecture")
.emit();
return;
}
// Generic statics are rejected, but we still reach this case.
tcx.sess.delay_span_bug(span, "generic static must be rejected");
return;
Err(e) => {
tcx.sess.delay_span_bug(span, &e.to_string());
return;
}
};
if layout.abi.is_uninhabited() {
tcx.struct_span_lint_hir(
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}

pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>(
&self,
span: Span,
value: T,
opt_input_expr: Option<&hir::Expr<'_>>,
) -> InferOk<'tcx, T>
where
T: TypeFoldable<'tcx>,
{
self.inh.partially_normalize_associated_types_in(
ObligationCause::new(
span,
self.body_id,
traits::BinOp {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
},
),
self.param_env,
value,
)
}

pub fn require_type_meets(
&self,
ty: Ty<'tcx>,
Expand Down
128 changes: 119 additions & 9 deletions compiler/rustc_typeck/src/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,15 +333,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}

pub(super) fn obligation_for_op_method(
&self,
span: Span,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_type: Option<Ty<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
{
// Construct a trait-reference `self_ty : Trait<input_tys>`
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
match param.kind {
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
GenericParamDefKind::Type { .. } => {
if param.index == 0 {
return self_ty.into();
} else if let Some(input_type) = opt_input_type {
return input_type.into();
}
}
}
self.var_for_def(span, param)
});

let trait_ref = ty::TraitRef::new(trait_def_id, substs);

// Construct an obligation
let poly_trait_ref = ty::Binder::dummy(trait_ref);
(
traits::Obligation::new(
traits::ObligationCause::new(
span,
self.body_id,
traits::BinOp {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
},
),
self.param_env,
poly_trait_ref.without_const().to_predicate(self.tcx),
),
substs,
)
}

/// `lookup_method_in_trait` is used for overloaded operators.
/// It does a very narrow slice of what the normal probe/confirm path does.
/// In particular, it doesn't really do any probing: it simply constructs
/// an obligation for a particular trait with the given self type and checks
/// whether that trait is implemented.
//
// FIXME(#18741): it seems likely that we can consolidate some of this
// code with the other method-lookup code. In particular, the second half
// of this method is basically the same as confirmation.
#[instrument(level = "debug", skip(self, span, opt_input_types))]
pub(super) fn lookup_method_in_trait(
&self,
Expand All @@ -358,7 +400,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let (obligation, substs) =
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
self.construct_obligation_for_trait(
span,
m_name,
trait_def_id,
obligation,
substs,
None,
false,
)
}

pub(super) fn lookup_op_method_in_trait(
&self,
span: Span,
m_name: Ident,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_type: Option<Ty<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
let (obligation, substs) = self.obligation_for_op_method(
span,
trait_def_id,
self_ty,
opt_input_type,
opt_input_expr,
);
self.construct_obligation_for_trait(
span,
m_name,
trait_def_id,
obligation,
substs,
opt_input_expr,
true,
)
}

// FIXME(#18741): it seems likely that we can consolidate some of this
// code with the other method-lookup code. In particular, the second half
// of this method is basically the same as confirmation.
fn construct_obligation_for_trait(
&self,
span: Span,
m_name: Ident,
trait_def_id: DefId,
obligation: traits::PredicateObligation<'tcx>,
substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
is_op: bool,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!(?obligation);

// Now we want to know if this can be matched
Expand Down Expand Up @@ -395,8 +487,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
let fn_sig = fn_sig.subst(self.tcx, substs);

let InferOk { value, obligations: o } =
self.normalize_associated_types_in_as_infer_ok(span, fn_sig);
let InferOk { value, obligations: o } = if is_op {
self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
} else {
self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
};
let fn_sig = {
obligations.extend(o);
value
Expand All @@ -412,16 +507,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// any late-bound regions appearing in its bounds.
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);

let InferOk { value, obligations: o } =
self.normalize_associated_types_in_as_infer_ok(span, bounds);
let InferOk { value, obligations: o } = if is_op {
self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
} else {
self.normalize_associated_types_in_as_infer_ok(span, bounds)
};
let bounds = {
obligations.extend(o);
value
};

assert!(!bounds.has_escaping_bound_vars());

let cause = traits::ObligationCause::misc(span, self.body_id);
let cause = if is_op {
ObligationCause::new(
span,
self.body_id,
traits::BinOp {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
},
)
} else {
traits::ObligationCause::misc(span, self.body_id)
};
obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));

// Also add an obligation for the method type being well-formed.
Expand Down
Loading