Auto merge of #137838 - matthiaskrgr:rollup-5brlcyr, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #137045 (Defer repeat expr `Copy` checks to end of type checking)
 - #137171 (Suggest swapping equality on E0277)
 - #137686 (Handle asm const similar to inline const)
 - #137689 (Use `Binder<Vec<Ty>>` instead of `Vec<Binder<Ty>>` in both solvers for sized/auto traits/etc.)
 - #137718 (Use original command for showing sccache stats)
 - #137730 (checked_ilog tests: deal with a bit of float imprecision)
 - #137735 (Update E0133 docs for 2024 edition)
 - #137742 (unconditionally lower match arm even if it's unneeded for never pattern in match)
 - #137771 (Tweak incorrect ABI suggestion and make suggestion verbose)

Failed merges:

 - #137723 (Make `rust.description` more general-purpose and pass `CFG_VER_DESCRIPTION`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-03-01 05:03:30 +00:00
commit 002da76821
57 changed files with 749 additions and 321 deletions

View file

@ -88,7 +88,7 @@ ast_lowering_invalid_abi_clobber_abi =
invalid ABI for `clobber_abi`
.note = the following ABIs are supported on this target: {$supported_abis}
ast_lowering_invalid_abi_suggestion = did you mean
ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
ast_lowering_invalid_asm_template_modifier_const =
asm template modifiers are not allowed for `const` arguments

View file

@ -195,7 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
anon_const: self.lower_anon_const_to_anon_const(anon_const),
anon_const: self.lower_const_block(anon_const),
},
InlineAsmOperand::Sym { sym } => {
let static_def_id = self

View file

@ -46,8 +46,9 @@ pub(crate) struct TupleStructWithDefault {
#[derive(Subdiagnostic)]
#[suggestion(
ast_lowering_invalid_abi_suggestion,
code = "{suggestion}",
applicability = "maybe-incorrect"
code = "\"{suggestion}\"",
applicability = "maybe-incorrect",
style = "verbose"
)]
pub(crate) struct InvalidAbiSuggestion {
#[primary_span]

View file

@ -671,10 +671,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
let span = self.lower_span(arm.span);
self.lower_attrs(hir_id, &arm.attrs, arm.span);
let is_never_pattern = pat.is_never_pattern();
let body = if let Some(body) = &arm.body
// We need to lower the body even if it's unneeded for never pattern in match,
// ensure that we can get HirId for DefId if need (issue #137708).
let body = arm.body.as_ref().map(|x| self.lower_expr(x));
let body = if let Some(body) = body
&& !is_never_pattern
{
self.lower_expr(body)
body
} else {
// Either `body.is_none()` or `is_never_pattern` here.
if !is_never_pattern {

View file

@ -1510,7 +1510,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: abi.span,
suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
span: abi.span,
suggestion: format!("\"{suggested_name}\""),
suggestion: suggested_name.to_string(),
}),
command: "rustc --print=calling-conventions".to_string(),
});

View file

@ -45,6 +45,7 @@ unsafe fn g() {
```
Linting against this is controlled via the `unsafe_op_in_unsafe_fn` lint, which
is `allow` by default but will be upgraded to `warn` in a future edition.
is `warn` by default in the 2024 edition and `allow` by default in earlier
editions.
[unsafe-section]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html

View file

@ -3512,7 +3512,7 @@ pub enum InlineAsmOperand<'hir> {
out_expr: Option<&'hir Expr<'hir>>,
},
Const {
anon_const: &'hir AnonConst,
anon_const: ConstBlock,
},
SymFn {
expr: &'hir Expr<'hir>,

View file

@ -1447,7 +1447,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>(
visit_opt!(visitor, visit_expr, out_expr);
}
InlineAsmOperand::Const { anon_const, .. } => {
try_visit!(visitor.visit_anon_const(anon_const));
try_visit!(visitor.visit_inline_const(anon_const));
}
InlineAsmOperand::SymFn { expr, .. } => {
try_visit!(visitor.visit_expr(expr));

View file

@ -1,5 +1,3 @@
use std::assert_matches::debug_assert_matches;
use rustc_abi::FieldIdx;
use rustc_ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxIndexSet;
@ -21,6 +19,7 @@ pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
typing_env: ty::TypingEnv<'tcx>,
target_features: &'tcx FxIndexSet<Symbol>,
expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
}
enum NonAsmTypeReason<'tcx> {
@ -35,13 +34,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
typing_env: ty::TypingEnv<'tcx>,
get_operand_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a,
) -> Self {
InlineAsmCtxt {
tcx,
typing_env,
target_features: tcx.asm_target_features(def_id),
expr_ty: Box::new(get_operand_ty),
expr_ty: Box::new(expr_ty),
node_ty: Box::new(node_ty),
}
}
@ -49,6 +50,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
(self.expr_ty)(expr)
}
fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> {
(self.node_ty)(hir_id)
}
// FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
// Type still may have region variables, but `Sized` does not depend
@ -487,12 +492,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
);
}
}
// Typeck has checked that Const operands are integers.
hir::InlineAsmOperand::Const { anon_const } => {
debug_assert_matches!(
self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(),
ty::Error(_) | ty::Int(_) | ty::Uint(_)
);
let ty = self.node_ty(anon_const.hir_id);
match ty.kind() {
ty::Error(_) => {}
_ if ty.is_integral() => {}
_ => {
self.tcx
.dcx()
.struct_span_err(op_sp, "invalid type for `const` operand")
.with_span_label(
self.tcx.def_span(anon_const.def_id),
format!("is {} `{}`", ty.kind().article(), ty),
)
.with_help("`const` operands must be of an integer type")
.emit();
}
}
}
// Typeck has checked that SymFn refers to a function.
hir::InlineAsmOperand::SymFn { expr } => {

View file

@ -186,17 +186,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
{
Some(parent_did)
}
// Exclude `GlobalAsm` here which cannot have generics.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } => {
anon_const.hir_id == hir_id
}
_ => false,
}) =>
{
Some(parent_did)
}
Node::TyPat(_) => Some(parent_did),
_ => None,
}

View file

@ -8,7 +8,7 @@ use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span};
@ -35,13 +35,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
let parent_node_id = tcx.parent_hir_id(hir_id);
let parent_node = tcx.hir_node(parent_node_id);
let find_const = |&(op, op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == hir_id => {
Some((anon_const, op_sp))
}
_ => None,
};
match parent_node {
// Anon consts "inside" the type system.
Node::ConstArg(&ConstArg {
@ -50,31 +43,6 @@ fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx
..
}) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
// Anon consts outside the type system.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
| Node::Item(&Item { kind: ItemKind::GlobalAsm { asm, .. }, .. })
if let Some((anon_const, op_sp)) = asm.operands.iter().find_map(find_const) =>
{
let ty = tcx.typeck(def_id).node_type(hir_id);
match ty.kind() {
ty::Error(_) => ty,
ty::Int(_) | ty::Uint(_) => ty,
_ => {
let guar = tcx
.dcx()
.struct_span_err(op_sp, "invalid type for `const` operand")
.with_span_label(
tcx.def_span(anon_const.def_id),
format!("is {} `{}`", ty.kind().article(), ty),
)
.with_help("`const` operands must be of an integer type")
.emit();
Ty::new_error(tcx, guar)
}
}
}
Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
}

View file

@ -1413,7 +1413,8 @@ impl<'a> State<'a> {
hir::InlineAsmOperand::Const { ref anon_const } => {
s.word("const");
s.space();
s.print_anon_const(anon_const);
// Not using `print_inline_const` to avoid additional `const { ... }`
s.ann.nested(s, Nested::Body(anon_const.body))
}
hir::InlineAsmOperand::SymFn { ref expr } => {
s.word("sym_fn");

View file

@ -1853,12 +1853,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ty::new_error(tcx, guar);
}
// We defer checking whether the element type is `Copy` as it is possible to have
// an inference variable as a repeat count and it seems unlikely that `Copy` would
// have inference side effects required for type checking to succeed.
if tcx.features().generic_arg_infer() {
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
// If the length is 0, we don't create any elements, so we don't copy any.
// If the length is 1, we don't copy that one element, we move it. Only check
// for `Copy` if the length is larger, or unevaluated.
// FIXME(min_const_generic_exprs): We could perhaps defer this check so that
// we don't require `<?0t as Tr>::CONST` doesn't unnecessarily require `Copy`.
if count.try_to_target_usize(tcx).is_none_or(|x| x > 1) {
} else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
}
@ -1868,7 +1871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
fn enforce_repeat_element_needs_copy_bound(
pub(super) fn enforce_repeat_element_needs_copy_bound(
&self,
element: &hir::Expr<'_>,
element_ty: Ty<'tcx>,
@ -3771,13 +3774,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_asm_operand(out_expr, false);
}
}
hir::InlineAsmOperand::Const { ref anon_const } => {
self.check_expr_const_block(anon_const, Expectation::NoExpectation);
}
hir::InlineAsmOperand::SymFn { expr } => {
self.check_expr(expr);
}
// `AnonConst`s have their own body and is type-checked separately.
// As they don't flow into the type system we don't need them to
// be well-formed.
hir::InlineAsmOperand::Const { .. } => {}
hir::InlineAsmOperand::SymStatic { .. } => {}
hir::InlineAsmOperand::Label { block } => {
let previous_diverges = self.diverges.get();

View file

@ -85,25 +85,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
}
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
/// Resolves type and const variables in `t` if possible. Unlike the infcx
/// version (resolve_vars_if_possible), this version will
/// also select obligations if it seems useful, in an effort
/// to get more type information.
// FIXME(-Znext-solver): A lot of the calls to this method should
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
#[instrument(skip(self), level = "debug", ret)]
pub(crate) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
pub(crate) fn resolve_vars_with_obligations<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
mut t: T,
) -> T {
// No Infer()? Nothing needs doing.
if !ty.has_non_region_infer() {
if !t.has_non_region_infer() {
debug!("no inference var, nothing needs doing");
return ty;
return t;
}
// If `ty` is a type variable, see whether we already know what it is.
ty = self.resolve_vars_if_possible(ty);
if !ty.has_non_region_infer() {
debug!(?ty);
return ty;
// If `t` is a type variable, see whether we already know what it is.
t = self.resolve_vars_if_possible(t);
if !t.has_non_region_infer() {
debug!(?t);
return t;
}
// If not, try resolving pending obligations as much as
@ -111,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible(|_| {});
self.resolve_vars_if_possible(ty)
self.resolve_vars_if_possible(t)
}
pub(crate) fn record_deferred_call_resolution(
@ -1454,7 +1457,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sp: Span,
ct: ty::Const<'tcx>,
) -> ty::Const<'tcx> {
// FIXME(min_const_generic_exprs): We could process obligations here if `ct` is a var.
let ct = self.resolve_vars_with_obligations(ct);
if self.next_trait_solver()
&& let ty::ConstKind::Unevaluated(..) = ct.kind()
@ -1510,6 +1513,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
pub(crate) fn structurally_resolve_const(
&self,
sp: Span,
ct: ty::Const<'tcx>,
) -> ty::Const<'tcx> {
let ct = self.try_structurally_resolve_const(sp, ct);
if !ct.is_ct_infer() {
ct
} else {
let e = self.tainted_by_errors().unwrap_or_else(|| {
self.err_ctxt()
.emit_inference_failure_err(
self.body_id,
sp,
ct.into(),
TypeAnnotationNeeded::E0282,
true,
)
.emit()
});
// FIXME: Infer `?ct = {const error}`?
ty::Const::new_error(self.tcx, e)
}
}
pub(crate) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
&self,
id: HirId,

View file

@ -110,8 +110,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.erase_regions(ty)
}
};
InlineAsmCtxt::new(self.tcx, enclosing_id, self.typing_env(self.param_env), expr_ty)
.check_asm(asm);
let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
InlineAsmCtxt::new(
self.tcx,
enclosing_id,
self.typing_env(self.param_env),
expr_ty,
node_ty,
)
.check_asm(asm);
}
}
pub(in super::super) fn check_repeat_exprs(&self) {
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
// We want to emit an error if the const is not structurally resolveable as otherwise
// we can find up conservatively proving `Copy` which may infer the repeat expr count
// to something that never required `Copy` in the first place.
let count =
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
if count.references_error() {
continue;
}
// If the length is 0, we don't create any elements, so we don't copy any.
// If the length is 1, we don't copy that one element, we move it. Only check
// for `Copy` if the length is larger.
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
}
}
}

View file

@ -199,6 +199,15 @@ fn typeck_with_inspect<'tcx>(
fcx.write_ty(id, expected_type);
};
// Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision
// as neither option is strictly more permissive than the other. However, we opt to check repeat exprs
// first as errors from not having inferred array lengths yet seem less confusing than errors from inference
// fallback arbitrarily inferring something incompatible with `Copy` inference side effects.
//
// This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using
// marker traits in the future.
fcx.check_repeat_exprs();
fcx.type_inference_fallback();
// Even though coercion casts provide type hints, we check casts after fallback for

View file

@ -62,6 +62,9 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
pub(super) deferred_repeat_expr_checks:
RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
/// Whenever we introduce an adjustment from `!` into a type variable,
/// we record that type variable here. This is later used to inform
/// fallback. See the `fallback` module for details.
@ -96,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
deferred_transmute_checks: RefCell::new(Vec::new()),
deferred_asm_checks: RefCell::new(Vec::new()),
deferred_coroutine_interiors: RefCell::new(Vec::new()),
deferred_repeat_expr_checks: RefCell::new(Vec::new()),
diverging_type_vars: RefCell::new(Default::default()),
infer_var_info: RefCell::new(Default::default()),
}

View file

@ -248,13 +248,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
}
fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) {
self.visit_node_id(span, anon_const.hir_id);
let body = self.tcx().hir_body(anon_const.body);
self.visit_body(body);
}
}
///////////////////////////////////////////////////////////////////////////
@ -284,9 +277,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
self.visit_field_id(e.hir_id);
}
hir::ExprKind::ConstBlock(ref anon_const) => {
self.visit_const_block(e.span, anon_const);
}
_ => {}
}
@ -297,6 +287,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
self.fix_index_builtin_expr(e);
}
fn visit_inline_const(&mut self, anon_const: &hir::ConstBlock) {
let span = self.tcx().def_span(anon_const.def_id);
self.visit_node_id(span, anon_const.hir_id);
let body = self.tcx().hir_body(anon_const.body);
self.visit_body(body);
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
match &p.kind {
hir::GenericParamKind::Lifetime { .. } => {
@ -340,9 +338,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) {
self.visit_node_id(expr.span, expr.hir_id);
if let hir::PatExprKind::ConstBlock(c) = &expr.kind {
self.visit_const_block(expr.span, c);
}
intravisit::walk_pat_expr(self, expr);
}

View file

@ -2,6 +2,7 @@
//! the end of the file for details.
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use tracing::{debug, instrument};
@ -26,8 +27,9 @@ impl<'tcx> InferCtxt<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
if let Some(inner) = binder.clone().no_bound_vars() {
return inner;
// Inlined `no_bound_vars`.
if !binder.as_ref().skip_binder().has_escaping_bound_vars() {
return binder.skip_binder();
}
let next_universe = self.create_next_universe();

View file

@ -324,11 +324,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.features()
}
fn bound_coroutine_hidden_types(
fn coroutine_hidden_types(
self,
def_id: DefId,
) -> impl IntoIterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
self.bound_coroutine_hidden_types(def_id)
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
self.coroutine_hidden_types(def_id)
}
fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {

View file

@ -740,51 +740,37 @@ impl<'tcx> TyCtxt<'tcx> {
}
/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state.
// FIXME(compiler-errors): We should remove this when the old solver goes away;
// and all other usages of this function should go through `bound_coroutine_hidden_types`
// instead.
/// trait bounds on a coroutine's internal state. This properly replaces
/// `ReErased` with new existential bound lifetimes.
pub fn coroutine_hidden_types(
self,
def_id: DefId,
) -> impl Iterator<Item = ty::EarlyBinder<'tcx, Ty<'tcx>>> {
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
let coroutine_layout = self.mir_coroutine_witnesses(def_id);
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| ty::EarlyBinder::bind(decl.ty))
}
/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state. This properly replaces
/// `ReErased` with new existential bound lifetimes.
pub fn bound_coroutine_hidden_types(
self,
def_id: DefId,
) -> impl Iterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
let coroutine_layout = self.mir_coroutine_witnesses(def_id);
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(move |decl| {
let mut vars = vec![];
let ty = fold_regions(self, decl.ty, |re, debruijn| {
assert_eq!(re, self.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
ty,
self.mk_bound_variable_kinds(&vars),
))
})
let mut vars = vec![];
let bound_tys = self.mk_type_list_from_iter(
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| {
let ty = fold_regions(self, decl.ty, |re, debruijn| {
assert_eq!(re, self.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty
}),
);
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
bound_tys,
self.mk_bound_variable_kinds(&vars),
))
}
/// Expands the given impl trait type, stopping if the type is recursive.

View file

@ -730,12 +730,20 @@ impl<'tcx> ThirBuildCx<'tcx> {
}
}
hir::InlineAsmOperand::Const { ref anon_const } => {
let value =
mir::Const::from_unevaluated(tcx, anon_const.def_id.to_def_id())
.instantiate_identity();
let span = tcx.def_span(anon_const.def_id);
let ty = self.typeck_results.node_type(anon_const.hir_id);
let did = anon_const.def_id.to_def_id();
let typeck_root_def_id = tcx.typeck_root_def_id(did);
let parent_args = tcx.erase_regions(GenericArgs::identity_for_item(
tcx,
typeck_root_def_id,
));
let args =
InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty })
.args;
InlineAsmOperand::Const { value, span }
let uneval = mir::UnevaluatedConst::new(did, args);
let value = mir::Const::Unevaluated(uneval, ty);
InlineAsmOperand::Const { value, span: tcx.def_span(did) }
}
hir::InlineAsmOperand::SymFn { expr } => {
InlineAsmOperand::SymFn { value: self.mirror_expr(expr) }

View file

@ -18,7 +18,7 @@ use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
@ -33,10 +33,10 @@ where
| ty::FnPtr(..)
| ty::Error(_)
| ty::Never
| ty::Char => Ok(vec![]),
| ty::Char => Ok(ty::Binder::dummy(vec![])),
// Treat `str` like it's defined as `struct str([u8]);`
ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(cx, Ty::new_u8(cx)))]),
ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),
ty::Dynamic(..)
| ty::Param(..)
@ -49,53 +49,49 @@ where
}
ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
Ok(vec![ty::Binder::dummy(element_ty)])
Ok(ty::Binder::dummy(vec![element_ty]))
}
ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => {
Ok(vec![ty::Binder::dummy(element_ty)])
Ok(ty::Binder::dummy(vec![element_ty]))
}
ty::Tuple(tys) => {
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
Ok(tys.iter().map(ty::Binder::dummy).collect())
Ok(ty::Binder::dummy(tys.to_vec()))
}
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),
ty::CoroutineClosure(_, args) => {
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
}
ty::Coroutine(_, args) => {
let coroutine_args = args.as_coroutine();
Ok(vec![
ty::Binder::dummy(coroutine_args.tupled_upvars_ty()),
ty::Binder::dummy(coroutine_args.witness()),
])
Ok(ty::Binder::dummy(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()]))
}
ty::CoroutineWitness(def_id, args) => Ok(ecx
.cx()
.bound_coroutine_hidden_types(def_id)
.into_iter()
.map(|bty| bty.instantiate(cx, args))
.collect()),
.coroutine_hidden_types(def_id)
.instantiate(cx, args)
.map_bound(|tys| tys.to_vec())),
ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
// For `PhantomData<T>`, we pass `T`.
ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
ty::Adt(def, args) if def.is_phantom_data() => Ok(ty::Binder::dummy(vec![args.type_at(0)])),
ty::Adt(def, args) => {
Ok(def.all_field_tys(cx).iter_instantiated(cx, args).map(ty::Binder::dummy).collect())
Ok(ty::Binder::dummy(def.all_field_tys(cx).iter_instantiated(cx, args).collect()))
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
Ok(vec![ty::Binder::dummy(cx.type_of(def_id).instantiate(cx, args))])
Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args)]))
}
}
}
@ -104,7 +100,7 @@ where
pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
@ -130,7 +126,7 @@ where
| ty::CoroutineClosure(..)
| ty::Never
| ty::Dynamic(_, _, ty::DynStar)
| ty::Error(_) => Ok(vec![]),
| ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
ty::Str
| ty::Slice(_)
@ -145,11 +141,11 @@ where
panic!("unexpected type `{ty:?}`")
}
ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
// impl Sized for ()
// impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |ty| vec![ty::Binder::dummy(ty)])),
ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))),
// impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
// `sized_constraint(Adt)` is the deepest struct trail that can be determined
@ -162,9 +158,9 @@ where
// if the ADT is sized for all possible args.
ty::Adt(def, args) => {
if let Some(sized_crit) = def.sized_constraint(ecx.cx()) {
Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.cx(), args))])
Ok(ty::Binder::dummy(vec![sized_crit.instantiate(ecx.cx(), args)]))
} else {
Ok(vec![])
Ok(ty::Binder::dummy(vec![]))
}
}
}
@ -174,14 +170,14 @@ where
pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
match ty.kind() {
// impl Copy/Clone for FnDef, FnPtr
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(vec![]),
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
// Implementations are provided in core
ty::Uint(_)
@ -197,7 +193,7 @@ where
// Cannot implement in core, as we can't be generic over patterns yet,
// so we'd have to list all patterns and type combinations.
ty::Pat(ty, ..) => Ok(vec![ty::Binder::dummy(ty)]),
ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])),
ty::Dynamic(..)
| ty::Str
@ -215,14 +211,14 @@ where
}
// impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),
// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),
// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
ty::CoroutineClosure(_, args) => {
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
}
// only when `coroutine_clone` is enabled and the coroutine is movable
@ -232,10 +228,7 @@ where
Movability::Movable => {
if ecx.cx().features().coroutine_clone() {
let coroutine = args.as_coroutine();
Ok(vec![
ty::Binder::dummy(coroutine.tupled_upvars_ty()),
ty::Binder::dummy(coroutine.witness()),
])
Ok(ty::Binder::dummy(vec![coroutine.tupled_upvars_ty(), coroutine.witness()]))
} else {
Err(NoSolution)
}
@ -247,10 +240,9 @@ where
// impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
ty::CoroutineWitness(def_id, args) => Ok(ecx
.cx()
.bound_coroutine_hidden_types(def_id)
.into_iter()
.map(|bty| bty.instantiate(ecx.cx(), args))
.collect()),
.coroutine_hidden_types(def_id)
.instantiate(ecx.cx(), args)
.map_bound(|tys| tys.to_vec())),
}
}

View file

@ -967,7 +967,7 @@ where
/// `enter_forall`, but takes `&mut self` and passes it back through the
/// callback since it can't be aliased during the call.
pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
pub(super) fn enter_forall<T: TypeFoldable<I>, U>(
&mut self,
value: ty::Binder<I, T>,
f: impl FnOnce(&mut Self, T) -> U,

View file

@ -1239,17 +1239,15 @@ where
constituent_tys: impl Fn(
&EvalCtxt<'_, D>,
I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>,
) -> Result<Candidate<I>, NoSolution> {
self.probe_trait_candidate(source).enter(|ecx| {
let goals = constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
.map(|ty| {
ecx.enter_forall(ty, |ecx, ty| {
goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty))
})
})
.collect::<Vec<_>>();
let goals =
ecx.enter_forall(constituent_tys(ecx, goal.predicate.self_ty())?, |ecx, tys| {
tys.into_iter()
.map(|ty| goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty)))
.collect::<Vec<_>>()
});
ecx.add_goals(GoalSource::ImplWhereBound, goals);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})

View file

@ -459,4 +459,43 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
visit::walk_attribute(self, attr);
self.in_attr = orig_in_attr;
}
fn visit_inline_asm(&mut self, asm: &'a InlineAsm) {
let InlineAsm {
asm_macro: _,
template: _,
template_strs: _,
operands,
clobber_abis: _,
options: _,
line_spans: _,
} = asm;
for (op, _span) in operands {
match op {
InlineAsmOperand::In { expr, reg: _ }
| InlineAsmOperand::Out { expr: Some(expr), reg: _, late: _ }
| InlineAsmOperand::InOut { expr, reg: _, late: _ } => {
self.visit_expr(expr);
}
InlineAsmOperand::Out { expr: None, reg: _, late: _ } => {}
InlineAsmOperand::SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
self.visit_expr(in_expr);
if let Some(expr) = out_expr {
self.visit_expr(expr);
}
}
InlineAsmOperand::Const { anon_const } => {
let def = self.create_def(
anon_const.id,
kw::Empty,
DefKind::InlineConst,
anon_const.value.span,
);
self.with_parent(def, |this| visit::walk_anon_const(this, anon_const));
}
InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
InlineAsmOperand::Label { block } => self.visit_block(block),
}
}
}
}

View file

@ -2419,6 +2419,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
&mut vec![],
&mut Default::default(),
);
self.suggest_swapping_lhs_and_rhs(
err,
obligation.predicate,
obligation.param_env,
obligation.cause.code(),
);
self.suggest_unsized_bound_if_applicable(err, obligation);
if let Some(span) = err.span.primary_span()
&& let Some(mut diag) =

View file

@ -4914,6 +4914,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
true
}
pub(crate) fn suggest_swapping_lhs_and_rhs<T>(
&self,
err: &mut Diag<'_>,
predicate: T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
) where
T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
{
let tcx = self.tcx;
let predicate = predicate.upcast(tcx);
match *cause_code {
ObligationCauseCode::BinOp {
lhs_hir_id,
rhs_hir_id: Some(rhs_hir_id),
rhs_span: Some(rhs_span),
..
} if let Some(typeck_results) = &self.typeck_results
&& let hir::Node::Expr(lhs) = tcx.hir_node(lhs_hir_id)
&& let hir::Node::Expr(rhs) = tcx.hir_node(rhs_hir_id)
&& let Some(lhs_ty) = typeck_results.expr_ty_opt(lhs)
&& let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) =>
{
if let Some(pred) = predicate.as_trait_clause()
&& tcx.is_lang_item(pred.def_id(), LangItem::PartialEq)
&& self
.infcx
.type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env)
.must_apply_modulo_regions()
{
let lhs_span = tcx.hir().span(lhs_hir_id);
let sm = tcx.sess.source_map();
if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span)
&& let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span)
{
err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
err.multipart_suggestion(
"consider swapping the equality",
vec![(lhs_span, rhs_snippet), (rhs_span, lhs_snippet)],
Applicability::MaybeIncorrect,
);
}
}
}
_ => {}
}
}
}
/// Add a hint to add a missing borrow or remove an unnecessary one.

View file

@ -9,7 +9,7 @@ use std::ops::ControlFlow;
use std::{cmp, iter};
use hir::def::DefKind;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{Diag, EmissionGuarantee};
use rustc_hir as hir;
@ -25,7 +25,6 @@ use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds};
pub use rustc_middle::traits::select::*;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
use rustc_middle::ty::{
self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
@ -2205,8 +2204,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
ty::CoroutineWitness(def_id, args) => {
let hidden_types = bind_coroutine_hidden_types_above(
self.infcx,
let hidden_types = rebind_coroutine_witness_types(
self.infcx.tcx,
def_id,
args,
obligation.predicate.bound_vars(),
@ -2354,7 +2353,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
ty::CoroutineWitness(def_id, args) => {
bind_coroutine_hidden_types_above(self.infcx, def_id, args, t.bound_vars())
rebind_coroutine_witness_types(self.infcx.tcx, def_id, args, t.bound_vars())
}
// For `PhantomData<T>`, we pass `T`.
@ -2849,6 +2848,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
}
fn rebind_coroutine_witness_types<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
args: ty::GenericArgsRef<'tcx>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
let bound_coroutine_types = tcx.coroutine_hidden_types(def_id).skip_binder();
let shifted_coroutine_types =
tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder());
ty::Binder::bind_with_vars(
ty::EarlyBinder::bind(shifted_coroutine_types.to_vec()).instantiate(tcx, args),
tcx.mk_bound_variable_kinds_from_iter(
bound_vars.iter().chain(bound_coroutine_types.bound_vars()),
),
)
}
impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> {
TraitObligationStackList::with(self)
@ -3157,56 +3173,3 @@ pub(crate) enum ProjectionMatchesProjection {
Ambiguous,
No,
}
/// Replace all regions inside the coroutine interior with late bound regions.
/// Note that each region slot in the types gets a new fresh late bound region, which means that
/// none of the regions inside relate to any other, even if typeck had previously found constraints
/// that would cause them to be related.
#[instrument(level = "trace", skip(infcx), ret)]
fn bind_coroutine_hidden_types_above<'tcx>(
infcx: &InferCtxt<'tcx>,
def_id: DefId,
args: ty::GenericArgsRef<'tcx>,
bound_vars: &ty::List<ty::BoundVariableKind>,
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
let tcx = infcx.tcx;
let mut seen_tys = FxHashSet::default();
let considering_regions = infcx.considering_regions;
let num_bound_variables = bound_vars.len() as u32;
let mut counter = num_bound_variables;
let hidden_types: Vec<_> = tcx
.coroutine_hidden_types(def_id)
// Deduplicate tys to avoid repeated work.
.filter(|bty| seen_tys.insert(*bty))
.map(|mut bty| {
// Only remap erased regions if we use them.
if considering_regions {
bty = bty.map_bound(|ty| {
fold_regions(tcx, ty, |r, current_depth| match r.kind() {
ty::ReErased => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(counter),
kind: ty::BoundRegionKind::Anon,
};
counter += 1;
ty::Region::new_bound(tcx, current_depth, br)
}
r => bug!("unexpected region: {r:?}"),
})
})
}
bty.instantiate(tcx, args)
})
.collect();
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
bound_vars.iter().chain(
(num_bound_variables..counter)
.map(|_| ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)),
),
);
ty::Binder::bind_with_vars(hidden_types, bound_vars)
}

View file

@ -151,7 +151,7 @@ pub trait InferCtxtLike: Sized {
value: ty::Binder<Self::Interner, T>,
) -> T;
fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>(
fn enter_forall<T: TypeFoldable<Self::Interner>, U>(
&self,
value: ty::Binder<Self::Interner, T>,
f: impl FnOnce(T) -> U,

View file

@ -189,10 +189,10 @@ pub trait Interner:
type Features: Features<Self>;
fn features(self) -> Self::Features;
fn bound_coroutine_hidden_types(
fn coroutine_hidden_types(
self,
def_id: Self::DefId,
) -> impl IntoIterator<Item = ty::EarlyBinder<Self, ty::Binder<Self, Self::Ty>>>;
) -> ty::EarlyBinder<Self, ty::Binder<Self, Self::Tys>>;
fn fn_sig(
self,

View file

@ -1,5 +1,13 @@
//! Tests for the `Integer::{ilog,log2,log10}` methods.
/// Rounds the argument down to the next integer, except that we account for potential imprecision
/// in the input, so if `f` is very close to an integer, it will round to that.
fn round_down_imprecise(f: f32) -> u32 {
// Rounds up for values less than 16*EPSILON below an integer,
// and rounds down for everything else.
(f + 16.0 * f32::EPSILON) as u32
}
#[test]
fn checked_ilog() {
assert_eq!(999u32.checked_ilog(10), Some(2));
@ -25,11 +33,19 @@ fn checked_ilog() {
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=i16::MAX {
assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}");
assert_eq!(
i.checked_ilog(13),
Some(round_down_imprecise((i as f32).log(13.0))),
"checking {i}"
);
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=u16::MAX {
assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}");
assert_eq!(
i.checked_ilog(13),
Some(round_down_imprecise((i as f32).log(13.0))),
"checking {i}"
);
}
}
@ -46,25 +62,34 @@ fn checked_ilog2() {
assert_eq!(0i8.checked_ilog2(), None);
assert_eq!(0i16.checked_ilog2(), None);
assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32));
assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32));
assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32));
assert_eq!(8192u16.checked_ilog2(), Some(round_down_imprecise((8192f32).log2())));
assert_eq!(32768u16.checked_ilog2(), Some(round_down_imprecise((32768f32).log2())));
assert_eq!(8192i16.checked_ilog2(), Some(round_down_imprecise((8192f32).log2())));
for i in 1..=u8::MAX {
assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
assert_eq!(
i.checked_ilog2(),
Some(round_down_imprecise((i as f32).log2())),
"checking {i}"
);
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=u16::MAX {
// Guard against Android's imprecise f32::ilog2 implementation.
if i != 8192 && i != 32768 {
assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
}
assert_eq!(
i.checked_ilog2(),
Some(round_down_imprecise((i as f32).log2())),
"checking {i}"
);
}
for i in i8::MIN..=0 {
assert_eq!(i.checked_ilog2(), None, "checking {i}");
}
for i in 1..=i8::MAX {
assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
assert_eq!(
i.checked_ilog2(),
Some(round_down_imprecise((i as f32).log2())),
"checking {i}"
);
}
#[cfg(not(miri))] // Miri is too slow
for i in i16::MIN..=0 {
@ -72,10 +97,11 @@ fn checked_ilog2() {
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=i16::MAX {
// Guard against Android's imprecise f32::ilog2 implementation.
if i != 8192 {
assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}");
}
assert_eq!(
i.checked_ilog2(),
Some(round_down_imprecise((i as f32).log2())),
"checking {i}"
);
}
}
@ -92,15 +118,27 @@ fn checked_ilog10() {
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=i16::MAX {
assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
assert_eq!(
i.checked_ilog10(),
Some(round_down_imprecise((i as f32).log10())),
"checking {i}"
);
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=u16::MAX {
assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
assert_eq!(
i.checked_ilog10(),
Some(round_down_imprecise((i as f32).log10())),
"checking {i}"
);
}
#[cfg(not(miri))] // Miri is too slow
for i in 1..=100_000u32 {
assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}");
assert_eq!(
i.checked_ilog10(),
Some(round_down_imprecise((i as f32).log10())),
"checking {i}"
);
}
}

View file

@ -279,5 +279,5 @@ if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
fi
echo "::group::sccache stats"
sccache --show-adv-stats || true
sccache --show-stats || true
echo "::endgroup::"

View file

@ -1,13 +0,0 @@
//@ known-bug: #117877
//@ edition:2021
//@ needs-rustc-debug-assertions
//@ only-x86_64
#![feature(asm_const)]
use std::arch::asm;
async unsafe fn foo<'a>() {
asm!("/* {0} */", const N);
}
fn main() {}

View file

@ -2,23 +2,26 @@ error[E0703]: invalid ABI: found `riscv-interrupt`
--> $DIR/riscv-discoverability-guidance.rs:15:8
|
LL | extern "riscv-interrupt" fn isr() {}
| ^^^^^^^^^^^^^^^^^
| |
| invalid ABI
| help: did you mean: `"riscv-interrupt-m"`
| ^^^^^^^^^^^^^^^^^ invalid ABI
|
= note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
help: there's a similarly named valid ABI `riscv-interrupt-m`
|
LL | extern "riscv-interrupt-m" fn isr() {}
| ++
error[E0703]: invalid ABI: found `riscv-interrupt-u`
--> $DIR/riscv-discoverability-guidance.rs:20:8
|
LL | extern "riscv-interrupt-u" fn isr_U() {}
| ^^^^^^^^^^^^^^^^^^^
| |
| invalid ABI
| help: did you mean: `"riscv-interrupt-m"`
| ^^^^^^^^^^^^^^^^^^^ invalid ABI
|
= note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
help: there's a similarly named valid ABI `riscv-interrupt-m`
|
LL - extern "riscv-interrupt-u" fn isr_U() {}
LL + extern "riscv-interrupt-m" fn isr_U() {}
|
error: aborting due to 2 previous errors

View file

@ -2,23 +2,26 @@ error[E0703]: invalid ABI: found `riscv-interrupt`
--> $DIR/riscv-discoverability-guidance.rs:15:8
|
LL | extern "riscv-interrupt" fn isr() {}
| ^^^^^^^^^^^^^^^^^
| |
| invalid ABI
| help: did you mean: `"riscv-interrupt-m"`
| ^^^^^^^^^^^^^^^^^ invalid ABI
|
= note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
help: there's a similarly named valid ABI `riscv-interrupt-m`
|
LL | extern "riscv-interrupt-m" fn isr() {}
| ++
error[E0703]: invalid ABI: found `riscv-interrupt-u`
--> $DIR/riscv-discoverability-guidance.rs:20:8
|
LL | extern "riscv-interrupt-u" fn isr_U() {}
| ^^^^^^^^^^^^^^^^^^^
| |
| invalid ABI
| help: did you mean: `"riscv-interrupt-m"`
| ^^^^^^^^^^^^^^^^^^^ invalid ABI
|
= note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
help: there's a similarly named valid ABI `riscv-interrupt-m`
|
LL - extern "riscv-interrupt-u" fn isr_U() {}
LL + extern "riscv-interrupt-m" fn isr_U() {}
|
error: aborting due to 2 previous errors

View file

@ -0,0 +1,10 @@
//@ edition:2021
//@ needs-asm-support
use std::arch::asm;
async unsafe fn foo<'a>() {
asm!("/* {0} */", const N); //~ ERROR E0425
}
fn main() {}

View file

@ -0,0 +1,14 @@
error[E0425]: cannot find value `N` in this scope
--> $DIR/const-resolve-error.rs:7:29
|
LL | asm!("/* {0} */", const N);
| ^ not found in this scope
|
help: you might be missing a const parameter
|
LL | async unsafe fn foo<'a, const N: /* Type */>() {
| +++++++++++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`.

View file

@ -5,6 +5,6 @@ use std::arch::global_asm;
fn main() {}
global_asm!("/* {} */", const 1 << 500); //~ ERROR evaluation of constant value failed [E0080]
global_asm!("/* {} */", const 1 << 500); //~ ERROR E0080
global_asm!("/* {} */", const 1 / 0); //~ ERROR evaluation of constant value failed [E0080]
global_asm!("/* {} */", const 1 / 0); //~ ERROR E0080

View file

@ -1,10 +1,10 @@
error[E0080]: evaluation of constant value failed
error[E0080]: evaluation of `{global_asm#0}::{constant#0}` failed
--> $DIR/fail-const-eval-issue-121099.rs:8:31
|
LL | global_asm!("/* {} */", const 1 << 500);
| ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow
error[E0080]: evaluation of constant value failed
error[E0080]: evaluation of `{global_asm#1}::{constant#0}` failed
--> $DIR/fail-const-eval-issue-121099.rs:10:31
|
LL | global_asm!("/* {} */", const 1 / 0);

View file

@ -80,7 +80,7 @@ error: invalid type for `const` operand
LL | asm!("{}", const &0);
| ^^^^^^--
| |
| is a `&i32`
| is a `&{integer}`
|
= help: `const` operands must be of an integer type

View file

@ -1,11 +1,16 @@
static _MAYBE_STRINGS: [Option<String>; 5] = [None; 5];
//~^ ERROR the trait bound `String: Copy` is not satisfied
fn main() {
// should hint to create an inline `const` block
// or to create a new `const` item
// should hint to create an inline `const` block
// or to create a new `const` item
fn foo() {
let _strings: [String; 5] = [String::new(); 5];
//~^ ERROR the trait bound `String: Copy` is not satisfied
}
fn bar() {
let _maybe_strings: [Option<String>; 5] = [None; 5];
//~^ ERROR the trait bound `String: Copy` is not satisfied
}
fn main() {}

View file

@ -22,7 +22,7 @@ LL | let _strings: [String; 5] = [String::new(); 5];
= note: the `Copy` trait is required because this value will be copied for each element of the array
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/const-fn-in-vec.rs:9:48
--> $DIR/const-fn-in-vec.rs:12:48
|
LL | let _maybe_strings: [Option<String>; 5] = [None; 5];
| ^^^^

View file

@ -0,0 +1,12 @@
fn a() {
match 0 {
! => || { //~ ERROR `!` patterns are experimental
//~^ ERROR a never pattern is always unreachable
//~^^ ERROR mismatched types
use std::ops::Add;
0.add(1)
},
}
}
fn main() {}

View file

@ -0,0 +1,36 @@
error[E0658]: `!` patterns are experimental
--> $DIR/unused_trait_in_never_pattern_body.rs:3:9
|
LL | ! => || {
| ^
|
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: a never pattern is always unreachable
--> $DIR/unused_trait_in_never_pattern_body.rs:3:14
|
LL | ! => || {
| ______________^
LL | |
LL | |
LL | | use std::ops::Add;
LL | | 0.add(1)
LL | | },
| | ^
| | |
| |_________this will never be executed
| help: remove this expression
error: mismatched types
--> $DIR/unused_trait_in_never_pattern_body.rs:3:9
|
LL | ! => || {
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `i32`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,37 @@
#![feature(generic_arg_infer)]
// Test that would start passing if we defer repeat expr copy checks to end of
// typechecking and they're checked after integer fallback occurs. We accomplish
// this by contriving a situation where integer fallback allows progress to be
// made on a trait goal that infers the length of a repeat expr.
use std::marker::PhantomData;
struct NotCopy;
trait Trait<const N: usize> {}
impl Trait<2> for u32 {}
impl Trait<1> for i32 {}
fn make_goal<T: Trait<N>, const N: usize>(_: &T, _: [NotCopy; N]) {}
fn main() {
let a = 1;
let b = [NotCopy; _];
//~^ ERROR: type annotations needed
// a is of type `?y`
// b is of type `[NotCopy; ?x]`
// there is a goal ?y: Trait<?x>` with two candidates:
// - `i32: Trait<1>`, ?y=i32 ?x=1 which doesnt require `NotCopy: Copy`
// - `u32: Trait<2>` ?y=u32 ?x=2 which requires `NotCopy: Copy`
make_goal(&a, b);
// final repeat expr checks:
//
// `NotCopy; ?x`
// - succeeds if fallback happens before repeat exprs as `i32: Trait<?x>` infers `?x=1`
// - fails if repeat expr checks happen first as `?x` is unconstrained so cannot be
// structurally resolved
}

View file

@ -0,0 +1,14 @@
error[E0282]: type annotations needed for `[NotCopy; _]`
--> $DIR/copy-check-deferred-after-fallback.rs:21:9
|
LL | let b = [NotCopy; _];
| ^ ------- type must be known at this point
|
help: consider giving `b` an explicit type, where the value of const parameter `N` is specified
|
LL | let b: [_; N] = [NotCopy; _];
| ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -0,0 +1,59 @@
//@ check-pass
#![feature(generic_arg_infer)]
// Test that if we defer repeat expr copy checks to end of typechecking they're
// checked before integer fallback occurs. We accomplish this by contriving a
// situation where we have a goal that can be proven either via another repeat expr
// check or by integer fallback. In the integer fallback case an array length would
// be inferred to `2` requiring `NotCopy: Copy`, and in the repeat expr case it would
// be inferred to `1`.
use std::marker::PhantomData;
struct NotCopy;
struct Foo<T>(PhantomData<T>);
impl Clone for Foo<u32> {
fn clone(&self) -> Self {
Foo(PhantomData)
}
}
impl Copy for Foo<u32> {}
fn tie<T>(_: &T, _: [Foo<T>; 2]) {}
trait Trait<const N: usize> {}
impl Trait<2> for i32 {}
impl Trait<1> for u32 {}
fn make_goal<T: Trait<N>, const N: usize>(_: &T, _: [NotCopy; N]) {}
fn main() {
let a = 1;
let b: [Foo<_>; 2] = [Foo(PhantomData); _];
tie(&a, b);
let c = [NotCopy; _];
// a is of type `?y`
// b is of type `[Foo<?y>; 2]`
// c is of type `[NotCopy; ?x]`
// there is a goal ?y: Trait<?x>` with two candidates:
// - `i32: Trait<2>`, ?y=i32 ?x=2 which requires `NotCopy: Copy` when expr checks happen
// - `u32: Trait<1>` ?y=u32 ?x=1 which doesnt require `NotCopy: Copy`
make_goal(&a, c);
// final repeat expr checks:
//
// `Foo<?y>; 2`
// - Foo<?y>: Copy
// - requires ?y=u32
//
// `NotCopy; ?x`
// - fails if fallback happens before repeat exprs as `i32: Trait<?x>` infers `?x=2`
// - succeeds if repeat expr checks happen first as `?y=u32` means `u32: Trait<?x>`
// infers `?x=1`
}

View file

@ -0,0 +1,17 @@
error[E0282]: type annotations needed for `[Foo<_>; 2]`
--> $DIR/copy-inference-side-effects-are-lazy.rs:22:9
|
LL | let x = [Foo(PhantomData); 2];
| ^
LL |
LL | _ = extract(x).max(2);
| ---------- type must be known at this point
|
help: consider giving `x` an explicit type, where the type for type parameter `T` is specified
|
LL | let x: [Foo<T>; 2] = [Foo(PhantomData); 2];
| +++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -0,0 +1,25 @@
//@revisions: current gai
//@[current] check-pass
#![cfg_attr(gai, feature(generic_arg_infer))]
use std::marker::PhantomData;
struct Foo<T>(PhantomData<T>);
impl Clone for Foo<u8> {
fn clone(&self) -> Self {
Foo(PhantomData)
}
}
impl Copy for Foo<u8> {}
fn extract<T, const N: usize>(_: [Foo<T>; N]) -> T {
loop {}
}
fn main() {
let x = [Foo(PhantomData); 2];
//[gai]~^ ERROR: type annotations needed
_ = extract(x).max(2);
}

View file

@ -0,0 +1,6 @@
//@ check-pass
#![feature(generic_arg_infer)]
fn main() {
let a: [_; 1] = [String::new(); _];
}

View file

@ -0,0 +1,20 @@
#![feature(generic_arg_infer)]
struct Foo<const N: usize>;
impl Clone for Foo<1> {
fn clone(&self) -> Self {
Foo
}
}
impl Copy for Foo<1> {}
fn unify<const N: usize>(_: &[Foo<N>; N]) {
loop {}
}
fn main() {
let x = &[Foo::<_>; _];
//~^ ERROR: type annotations needed for `&[Foo<_>; _]`
_ = unify(x);
}

View file

@ -0,0 +1,14 @@
error[E0282]: type annotations needed for `&[Foo<_>; _]`
--> $DIR/no-conservative-copy-impl-requirement.rs:17:9
|
LL | let x = &[Foo::<_>; _];
| ^ -------- type must be known at this point
|
help: consider giving `x` an explicit type, where the value of const parameter `N` is specified
|
LL | let x: &[Foo<N>; N] = &[Foo::<_>; _];
| ++++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -2,12 +2,14 @@ error[E0703]: invalid ABI: found `cdedl`
--> $DIR/abi-typo.rs:2:8
|
LL | extern "cdedl" fn cdedl() {}
| ^^^^^^^
| |
| invalid ABI
| help: did you mean: `"cdecl"`
| ^^^^^^^ invalid ABI
|
= note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
help: there's a similarly named valid ABI `cdecl`
|
LL - extern "cdedl" fn cdedl() {}
LL + extern "cdecl" fn cdedl() {}
|
error: aborting due to 1 previous error

View file

@ -0,0 +1,11 @@
struct T(String);
impl PartialEq<String> for T {
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}
fn main() {
String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); //~ can't compare `String` with `T` [E0277]
}

View file

@ -0,0 +1,24 @@
error[E0277]: can't compare `String` with `T`
--> $DIR/partialeq_suggest_swap_on_e0277.rs:10:36
|
LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry"));
| ^^ no implementation for `String == T`
|
= help: the trait `PartialEq<T>` is not implemented for `String`
= help: the following other types implement trait `PartialEq<Rhs>`:
`String` implements `PartialEq<&str>`
`String` implements `PartialEq<ByteStr>`
`String` implements `PartialEq<ByteString>`
`String` implements `PartialEq<Cow<'_, str>>`
`String` implements `PartialEq<str>`
`String` implements `PartialEq`
= note: `T` implements `PartialEq<String>`
help: consider swapping the equality
|
LL - String::from("Girls Band Cry") == T(String::from("Girls Band Cry"));
LL + T(String::from("Girls Band Cry")) == String::from("Girls Band Cry");
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.