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:
commit
002da76821
57 changed files with 749 additions and 321 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 } => {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -110,11 +110,43 @@ 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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_method_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -740,36 +740,20 @@ 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 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());
|
||||
|
@ -780,11 +764,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
|
||||
)
|
||||
});
|
||||
ty
|
||||
}),
|
||||
);
|
||||
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
|
||||
ty,
|
||||
bound_tys,
|
||||
self.mk_bound_variable_kinds(&vars),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands the given impl trait type, stopping if the type is recursive.
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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::"
|
||||
|
|
|
@ -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() {}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
10
tests/ui/asm/const-resolve-error.rs
Normal file
10
tests/ui/asm/const-resolve-error.rs
Normal 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() {}
|
14
tests/ui/asm/const-resolve-error.stderr
Normal file
14
tests/ui/asm/const-resolve-error.stderr
Normal 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`.
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
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() {}
|
||||
|
|
|
@ -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];
|
||||
| ^^^^
|
||||
|
|
12
tests/ui/never_type/unused_trait_in_never_pattern_body.rs
Normal file
12
tests/ui/never_type/unused_trait_in_never_pattern_body.rs
Normal 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() {}
|
|
@ -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`.
|
37
tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs
Normal file
37
tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs
Normal 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
|
||||
}
|
|
@ -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`.
|
59
tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
Normal file
59
tests/ui/repeat-expr/copy-check-deferred-before-fallback.rs
Normal 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`
|
||||
}
|
|
@ -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`.
|
25
tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
Normal file
25
tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs
Normal 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);
|
||||
}
|
6
tests/ui/repeat-expr/dont-require-copy-on-infer.rs
Normal file
6
tests/ui/repeat-expr/dont-require-copy-on-infer.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//@ check-pass
|
||||
#![feature(generic_arg_infer)]
|
||||
|
||||
fn main() {
|
||||
let a: [_; 1] = [String::new(); _];
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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`.
|
|
@ -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
|
||||
|
||||
|
|
11
tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs
Normal file
11
tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs
Normal 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]
|
||||
}
|
24
tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr
Normal file
24
tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr
Normal 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`.
|
Loading…
Add table
Add a link
Reference in a new issue