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 4 pull requests #94367

Closed
wants to merge 9 commits into from
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