From e0c8780a5baa78cbb5e8759d5783000547c0db75 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 23 Mar 2022 20:57:48 -0700 Subject: [PATCH 1/9] Better suggestions for Fn trait selection errors --- .../src/infer/error_reporting/mod.rs | 6 +- compiler/rustc_middle/src/ty/closure.rs | 18 ++- .../src/traits/error_reporting/mod.rs | 126 +++++++++++++++++- .../issue-62529-3.stderr | 2 + src/test/ui/issues/issue-59494.stderr | 2 + src/test/ui/trait-bounds/mismatch-fn-trait.rs | 28 ++++ .../ui/trait-bounds/mismatch-fn-trait.stderr | 81 +++++++++++ .../unboxed-closures-fnmut-as-fn.stderr | 1 + 8 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/trait-bounds/mismatch-fn-trait.rs create mode 100644 src/test/ui/trait-bounds/mismatch-fn-trait.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index abc25d51776f3..fb6adccdf497b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1086,7 +1086,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Compares two given types, eliding parts that are the same between them and highlighting /// relevant differences, and return two representation of those types for highlighted printing. - fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) { + pub fn cmp( + &self, + t1: Ty<'tcx>, + t2: Ty<'tcx>, + ) -> (DiagnosticStyledString, DiagnosticStyledString) { debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind()); // helper functions diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 1446f7dac3638..3bddf7fb6ffc4 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -119,9 +119,21 @@ impl<'tcx> ClosureKind { /// See `Ty::to_opt_closure_kind` for more details. pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self { - ty::ClosureKind::Fn => tcx.types.i8, - ty::ClosureKind::FnMut => tcx.types.i16, - ty::ClosureKind::FnOnce => tcx.types.i32, + ClosureKind::Fn => tcx.types.i8, + ClosureKind::FnMut => tcx.types.i16, + ClosureKind::FnOnce => tcx.types.i32, + } + } + + pub fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if Some(def_id) == tcx.lang_items().fn_once_trait() { + Some(ClosureKind::FnOnce) + } else if Some(def_id) == tcx.lang_items().fn_mut_trait() { + Some(ClosureKind::FnMut) + } else if Some(def_id) == tcx.lang_items().fn_trait() { + Some(ClosureKind::Fn) + } else { + None } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 229e108d5d640..b727cd4a3cc8b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - EvaluationResult, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, - Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, - OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, - SelectionContext, SelectionError, TraitNotObjectSafe, + EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -21,6 +21,8 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; +use rustc_infer::infer::error_reporting::same_type_modulo_infer; +use rustc_infer::traits::TraitEngine; use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; @@ -103,6 +105,17 @@ pub trait InferCtxtExt<'tcx> { found_args: Vec, is_closure: bool, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + constness: ty::BoundConstness, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; } impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { @@ -563,7 +576,64 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } // Try to report a help message - if !trait_ref.has_infer_types_or_consts() + if is_fn_trait + && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( + obligation.param_env, + trait_ref.self_ty(), + trait_predicate.skip_binder().constness, + trait_predicate.skip_binder().polarity, + ) + { + // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following + // suggestion to add trait bounds for the type, since we only typically implement + // these traits once. + + // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying + // to implement. + let selected_kind = + ty::ClosureKind::from_def_id(self.tcx, trait_ref.def_id()) + .expect("expected to map DefId to ClosureKind"); + if !implemented_kind.extends(selected_kind) { + err.note( + &format!( + "`{}` implements `{}`, but it must implement `{}`, which is more general", + trait_ref.skip_binder().self_ty(), + implemented_kind, + selected_kind + ) + ); + } + + // Note any argument mismatches + let given_ty = params.skip_binder(); + let expected_ty = trait_ref.skip_binder().substs.type_at(1); + if let ty::Tuple(given) = given_ty.kind() + && let ty::Tuple(expected) = expected_ty.kind() + { + if expected.len() != given.len() { + // Note number of types that were expected and given + err.note( + &format!( + "expected a closure taking {} argument{}, but one taking {} argument{} was given", + given.len(), + if given.len() == 1 { "" } else { "s" }, + expected.len(), + if expected.len() == 1 { "" } else { "s" }, + ) + ); + } else if !same_type_modulo_infer(given_ty, expected_ty) { + // Print type mismatch + let (expected_args, given_args) = + self.cmp(given_ty, expected_ty); + err.note_expected_found( + &"a closure with arguments", + expected_args, + &"a closure with arguments", + given_args, + ); + } + } + } else if !trait_ref.has_infer_types_or_consts() && self.predicate_can_apply(obligation.param_env, trait_ref) { // If a where-clause may be useful, remind the @@ -1144,6 +1214,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err } + + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + constness: ty::BoundConstness, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the substitutions + // of the trait are. + let var = self.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }); + let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef::new(trait_def_id, substs), + constness, + polarity, + }) + .to_predicate(self.tcx), + ); + let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); + fulfill_cx.register_predicate_obligation(self, obligation); + if fulfill_cx.select_all_or_error(self).is_empty() { + return Ok(( + ty::ClosureKind::from_def_id(self.tcx, trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } } trait InferCtxtPrivExt<'hir, 'tcx> { diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-62529-3.stderr b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-62529-3.stderr index b110734642177..066bf431a83d7 100644 --- a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -4,6 +4,8 @@ error[E0277]: expected a `Fn<(<_ as ATC<'a>>::Type,)>` closure, found `F` LL | call(f, ()); | ^^^^ expected an `Fn<(<_ as ATC<'a>>::Type,)>` closure, found `F` | + = note: expected a closure with arguments `((),)` + found a closure with arguments `(<_ as ATC<'a>>::Type,)` note: required by a bound in `call` --> $DIR/issue-62529-3.rs:9:36 | diff --git a/src/test/ui/issues/issue-59494.stderr b/src/test/ui/issues/issue-59494.stderr index a9284535e4dc4..8b542bb69de2e 100644 --- a/src/test/ui/issues/issue-59494.stderr +++ b/src/test/ui/issues/issue-59494.stderr @@ -7,6 +7,8 @@ LL | let t8 = t8n(t7, t7p(f, g)); | required by a bound introduced by this call | = help: the trait `Fn<(_,)>` is not implemented for `impl Fn(((_, _), _))` + = note: expected a closure with arguments `(((_, _), _),)` + found a closure with arguments `(_,)` note: required by a bound in `t8n` --> $DIR/issue-59494.rs:5:45 | diff --git a/src/test/ui/trait-bounds/mismatch-fn-trait.rs b/src/test/ui/trait-bounds/mismatch-fn-trait.rs new file mode 100644 index 0000000000000..0ed64043a9a56 --- /dev/null +++ b/src/test/ui/trait-bounds/mismatch-fn-trait.rs @@ -0,0 +1,28 @@ +fn take(_f: impl FnMut(i32)) {} + +fn test1(f: impl FnMut(u32)) { + take(f) + //~^ ERROR [E0277] +} + +fn test2(f: impl FnMut(i32, i32)) { + take(f) + //~^ ERROR [E0277] +} + +fn test3(f: impl FnMut()) { + take(f) + //~^ ERROR [E0277] +} + +fn test4(f: impl FnOnce(i32)) { + take(f) + //~^ ERROR [E0277] +} + +fn test5(f: impl FnOnce(u32)) { + take(f) + //~^ ERROR [E0277] +} + +fn main() {} diff --git a/src/test/ui/trait-bounds/mismatch-fn-trait.stderr b/src/test/ui/trait-bounds/mismatch-fn-trait.stderr new file mode 100644 index 0000000000000..961e6d88fbef4 --- /dev/null +++ b/src/test/ui/trait-bounds/mismatch-fn-trait.stderr @@ -0,0 +1,81 @@ +error[E0277]: expected a `FnMut<(i32,)>` closure, found `impl FnMut(u32)` + --> $DIR/mismatch-fn-trait.rs:4:10 + | +LL | take(f) + | ---- ^ expected an `FnMut<(i32,)>` closure, found `impl FnMut(u32)` + | | + | required by a bound introduced by this call + | + = note: expected a closure with arguments `(u32,)` + found a closure with arguments `(i32,)` +note: required by a bound in `take` + --> $DIR/mismatch-fn-trait.rs:1:18 + | +LL | fn take(_f: impl FnMut(i32)) {} + | ^^^^^^^^^^ required by this bound in `take` + +error[E0277]: expected a `FnMut<(i32,)>` closure, found `impl FnMut(i32, i32)` + --> $DIR/mismatch-fn-trait.rs:9:10 + | +LL | take(f) + | ---- ^ expected an `FnMut<(i32,)>` closure, found `impl FnMut(i32, i32)` + | | + | required by a bound introduced by this call + | + = note: expected a closure taking 2 arguments, but one taking 1 argument was given +note: required by a bound in `take` + --> $DIR/mismatch-fn-trait.rs:1:18 + | +LL | fn take(_f: impl FnMut(i32)) {} + | ^^^^^^^^^^ required by this bound in `take` + +error[E0277]: expected a `FnMut<(i32,)>` closure, found `impl FnMut()` + --> $DIR/mismatch-fn-trait.rs:14:10 + | +LL | take(f) + | ---- ^ expected an `FnMut<(i32,)>` closure, found `impl FnMut()` + | | + | required by a bound introduced by this call + | + = note: expected a closure taking 0 arguments, but one taking 1 argument was given +note: required by a bound in `take` + --> $DIR/mismatch-fn-trait.rs:1:18 + | +LL | fn take(_f: impl FnMut(i32)) {} + | ^^^^^^^^^^ required by this bound in `take` + +error[E0277]: expected a `FnMut<(i32,)>` closure, found `impl FnOnce(i32)` + --> $DIR/mismatch-fn-trait.rs:19:10 + | +LL | take(f) + | ---- ^ expected an `FnMut<(i32,)>` closure, found `impl FnOnce(i32)` + | | + | required by a bound introduced by this call + | + = note: `impl FnOnce(i32)` implements `FnOnce`, but it must implement `FnMut`, which is more general +note: required by a bound in `take` + --> $DIR/mismatch-fn-trait.rs:1:18 + | +LL | fn take(_f: impl FnMut(i32)) {} + | ^^^^^^^^^^ required by this bound in `take` + +error[E0277]: expected a `FnMut<(i32,)>` closure, found `impl FnOnce(u32)` + --> $DIR/mismatch-fn-trait.rs:24:10 + | +LL | take(f) + | ---- ^ expected an `FnMut<(i32,)>` closure, found `impl FnOnce(u32)` + | | + | required by a bound introduced by this call + | + = note: `impl FnOnce(u32)` implements `FnOnce`, but it must implement `FnMut`, which is more general + = note: expected a closure with arguments `(u32,)` + found a closure with arguments `(i32,)` +note: required by a bound in `take` + --> $DIR/mismatch-fn-trait.rs:1:18 + | +LL | fn take(_f: impl FnMut(i32)) {} + | ^^^^^^^^^^ required by this bound in `take` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index f379d73eecff7..0ea1c1dcd5bde 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -7,6 +7,7 @@ LL | let x = call_it(&S, 22); | required by a bound introduced by this call | = help: the trait `Fn<(isize,)>` is not implemented for `S` + = note: `S` implements `FnMut`, but it must implement `Fn`, which is more general note: required by a bound in `call_it` --> $DIR/unboxed-closures-fnmut-as-fn.rs:22:14 | From 91ac9cf5952ef00218c215c564ec7f56c479019d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 24 Mar 2022 21:34:57 -0700 Subject: [PATCH 2/9] suggest wrapping single-expr blocks in square brackets --- compiler/rustc_parse/src/parser/expr.rs | 18 +-- compiler/rustc_typeck/src/check/demand.rs | 1 + .../rustc_typeck/src/check/fn_ctxt/checks.rs | 145 ++++++++++-------- .../src/check/fn_ctxt/suggestions.rs | 71 +++++++++ .../brackets-to-braces-single-element.rs | 10 ++ .../brackets-to-braces-single-element.stderr | 38 +++++ .../issue-87830-try-brackets-for-arrays.rs | 7 +- ...issue-87830-try-brackets-for-arrays.stderr | 23 ++- 8 files changed, 222 insertions(+), 91 deletions(-) create mode 100644 src/test/ui/did_you_mean/brackets-to-braces-single-element.rs create mode 100644 src/test/ui/did_you_mean/brackets-to-braces-single-element.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 148e0a24ec304..beffbe410ba16 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1919,17 +1919,13 @@ impl<'a> Parser<'a> { match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) { Ok(arr) => { let hi = snapshot.prev_token.span; - self.struct_span_err( - arr.span, - "this code is interpreted as a block expression, not an array", - ) - .multipart_suggestion( - "try using [] instead of {}", - vec![(lo, "[".to_owned()), (hi, "]".to_owned())], - Applicability::MaybeIncorrect, - ) - .note("to define an array, one would use square brackets instead of curly braces") - .emit(); + self.struct_span_err(arr.span, "this is a block expression, not an array") + .multipart_suggestion( + "to make an array, use square brackets instead of curly braces", + vec![(lo, "[".to_owned()), (hi, "]".to_owned())], + Applicability::MaybeIncorrect, + ) + .emit(); self.restore_snapshot(snapshot); Some(self.mk_expr_err(arr.span)) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 58e5c9315c30c..b9d7c1ea1c638 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -39,6 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_no_capture_closure(err, expected, expr_ty); self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_parentheses(err, expr); + self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); self.report_closure_inferred_return_type(err, expected); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index b7b45929e1463..29cefe63c0b94 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -774,57 +774,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let prev_diverges = self.diverges.get(); let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - let (ctxt, ()) = - self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for (pos, s) in blk.stmts.iter().enumerate() { - self.check_stmt(s, blk.stmts.len() - 1 == pos); - } + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for (pos, s) in blk.stmts.iter().enumerate() { + self.check_stmt(s, blk.stmts.len() - 1 == pos); + } - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = - self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - coerce.coerce(self, &cause, tail_expr, tail_expr_ty); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(blk.hir_id); + let coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + let span = self.get_expr_coercion_span(tail_expr); + let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); + let ty_for_diagnostic = coerce.merged_ty(); + // We use coerce_inner here because we want to augment the error + // suggesting to wrap the block in square brackets if it might've + // been mistaken array syntax + coerce.coerce_inner( + self, + &cause, + Some(tail_expr), + tail_expr_ty, + Some(&mut |diag: &mut Diagnostic| { + self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic); + }), + false, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.get().is_always() { + // #50009 -- Do not point at the entire fn block span, point at the return type + // span, as it is the cause of the requirement, and + // `consider_hint_about_removing_semicolon` will point at the last expression + // if it were a relevant part of the error. This improves usability in editors + // that highlight errors inline. + let mut sp = blk.span; + let mut fn_span = None; + if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + let ret_sp = decl.output.span(); + if let Some(block_sp) = self.parent_item_span(blk.hir_id) { + // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the + // output would otherwise be incorrect and even misleading. Make sure + // the span we're aiming at correspond to a `fn` body. + if block_sp == blk.span { + sp = ret_sp; + fn_span = Some(ident.span); } } - coerce.coerce_forced_unit( + } + coerce.coerce_forced_unit( self, &self.misc(sp), &mut |err| { @@ -837,21 +848,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Our block must be a `assign desugar local; assignment` if let Some(hir::Node::Block(hir::Block { stmts: - [hir::Stmt { - kind: - hir::StmtKind::Local(hir::Local { - source: hir::LocalSource::AssignDesugar(_), - .. - }), - .. - }, hir::Stmt { - kind: - hir::StmtKind::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - }), - .. - }], + [ + hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { + source: + hir::LocalSource::AssignDesugar(_), + .. + }), + .. + }, + hir::Stmt { + kind: + hir::StmtKind::Expr(hir::Expr { + kind: hir::ExprKind::Assign(..), + .. + }), + .. + }, + ], .. })) = self.tcx.hir().find(blk.hir_id) { @@ -871,9 +886,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, false, ); - } } - }); + } + }); if ctxt.may_break { // If we can break from the block, then the block's exit is always reachable diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 67d61668b6d85..1ff4008ccdff3 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -757,6 +757,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Given an expression type mismatch, peel any `&` expressions until we get to + /// a block expression, and then suggest replacing the braces with square braces + /// if it was possibly mistaken array syntax. + pub(crate) fn suggest_block_to_brackets_peeling_refs( + &self, + diag: &mut Diagnostic, + mut expr: &hir::Expr<'_>, + mut expr_ty: Ty<'tcx>, + mut expected_ty: Ty<'tcx>, + ) { + loop { + match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { + ( + hir::ExprKind::AddrOf(_, _, inner_expr), + ty::Ref(_, inner_expr_ty, _), + ty::Ref(_, inner_expected_ty, _), + ) => { + expr = *inner_expr; + expr_ty = *inner_expr_ty; + expected_ty = *inner_expected_ty; + } + (hir::ExprKind::Block(blk, _), _, _) => { + self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); + break; + } + _ => break, + } + } + } + + /// Suggest wrapping the block in square brackets instead of curly braces + /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. + pub(crate) fn suggest_block_to_brackets( + &self, + diag: &mut Diagnostic, + blk: &hir::Block<'_>, + blk_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) { + if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { + if self.can_coerce(blk_ty, *elem_ty) + && blk.stmts.is_empty() + && blk.rules == hir::BlockCheckMode::DefaultBlock + { + let source_map = self.tcx.sess.source_map(); + if let Ok(snippet) = source_map.span_to_snippet(blk.span) { + if snippet.starts_with('{') && snippet.ends_with('}') { + diag.multipart_suggestion_verbose( + "to create an array, use square brackets instead of curly braces", + vec![ + ( + blk.span + .shrink_to_lo() + .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), + "[".to_string(), + ), + ( + blk.span + .shrink_to_hi() + .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), + "]".to_string(), + ), + ], + Applicability::MachineApplicable, + ); + } + } + } + } + } + fn is_loop(&self, id: hir::HirId) -> bool { let node = self.tcx.hir().get(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) diff --git a/src/test/ui/did_you_mean/brackets-to-braces-single-element.rs b/src/test/ui/did_you_mean/brackets-to-braces-single-element.rs new file mode 100644 index 0000000000000..4d0109767fc0f --- /dev/null +++ b/src/test/ui/did_you_mean/brackets-to-braces-single-element.rs @@ -0,0 +1,10 @@ +const A: [&str; 1] = { "hello" }; +//~^ ERROR mismatched types + +const B: &[u32] = &{ 1 }; +//~^ ERROR mismatched types + +const C: &&[u32; 1] = &&{ 1 }; +//~^ ERROR mismatched types + +fn main() {} diff --git a/src/test/ui/did_you_mean/brackets-to-braces-single-element.stderr b/src/test/ui/did_you_mean/brackets-to-braces-single-element.stderr new file mode 100644 index 0000000000000..6ded03e45b55e --- /dev/null +++ b/src/test/ui/did_you_mean/brackets-to-braces-single-element.stderr @@ -0,0 +1,38 @@ +error[E0308]: mismatched types + --> $DIR/brackets-to-braces-single-element.rs:1:24 + | +LL | const A: [&str; 1] = { "hello" }; + | ^^^^^^^ expected array `[&'static str; 1]`, found `&str` + | +help: to create an array, use square brackets instead of curly braces + | +LL | const A: [&str; 1] = [ "hello" ]; + | ~ ~ + +error[E0308]: mismatched types + --> $DIR/brackets-to-braces-single-element.rs:4:19 + | +LL | const B: &[u32] = &{ 1 }; + | ^^^^^^ expected slice `[u32]`, found integer + | + = note: expected reference `&'static [u32]` + found reference `&{integer}` +help: to create an array, use square brackets instead of curly braces + | +LL | const B: &[u32] = &[ 1 ]; + | ~ ~ + +error[E0308]: mismatched types + --> $DIR/brackets-to-braces-single-element.rs:7:27 + | +LL | const C: &&[u32; 1] = &&{ 1 }; + | ^ expected array `[u32; 1]`, found integer + | +help: to create an array, use square brackets instead of curly braces + | +LL | const C: &&[u32; 1] = &&[ 1 ]; + | ~ ~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs index 53dad85900a65..070ffaa1eff00 100644 --- a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs +++ b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs @@ -1,15 +1,16 @@ fn main() {} -const FOO: [u8; 3] = { //~ ERROR this code is interpreted as a block expression +const FOO: [u8; 3] = { + //~^ ERROR this is a block expression, not an array 1, 2, 3 }; const BAR: [&str; 3] = {"one", "two", "three"}; -//~^ ERROR this code is interpreted as a block expression +//~^ ERROR this is a block expression, not an array fn foo() { {1, 2, 3}; - //~^ ERROR this code is interpreted as a block expression + //~^ ERROR this is a block expression, not an array } fn bar() { diff --git a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr index 9ab491f5c238f..d5ad1a72b8250 100644 --- a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr +++ b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr @@ -1,46 +1,45 @@ -error: this code is interpreted as a block expression, not an array +error: this is a block expression, not an array --> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22 | LL | const FOO: [u8; 3] = { | ______________________^ +LL | | LL | | 1, 2, 3 LL | | }; | |_^ | - = note: to define an array, one would use square brackets instead of curly braces -help: try using [] instead of {} +help: to make an array, use square brackets instead of curly braces | LL ~ const FOO: [u8; 3] = [ +LL | LL | 1, 2, 3 LL ~ ]; | -error: this code is interpreted as a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:7:24 +error: this is a block expression, not an array + --> $DIR/issue-87830-try-brackets-for-arrays.rs:8:24 | LL | const BAR: [&str; 3] = {"one", "two", "three"}; | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: to define an array, one would use square brackets instead of curly braces -help: try using [] instead of {} +help: to make an array, use square brackets instead of curly braces | LL | const BAR: [&str; 3] = ["one", "two", "three"]; | ~ ~ -error: this code is interpreted as a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:11:5 +error: this is a block expression, not an array + --> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5 | LL | {1, 2, 3}; | ^^^^^^^^^ | - = note: to define an array, one would use square brackets instead of curly braces -help: try using [] instead of {} +help: to make an array, use square brackets instead of curly braces | LL | [1, 2, 3]; | ~ ~ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` - --> $DIR/issue-87830-try-brackets-for-arrays.rs:16:6 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6 | LL | 1, 2, 3 | ^ expected one of `.`, `;`, `?`, `}`, or an operator From 4fc23a14b8fda56236e483ee69226b1bdf54f1e3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 26 Mar 2022 15:29:59 -0500 Subject: [PATCH 3/9] Make `impl Debug for rustdoc::clean::Item` easier to read Before: ``` Item { name: Some("Send"), attrs: Attributes { doc_strings: [DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:15:1: 15:60 (#0), parent_module: None, doc: " Types that can be transferred across thread boundaries.", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:16:1: 16:4 (#0), parent_module: None, doc: "", kind: SugaredDoc, indent: 0 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:17:1: 17:78 (#0), parent_module: None, doc: " This trait is automatically implemented when the compiler determines it's", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:18:1: 18:17 (#0), parent_module: None, doc: " appropriate.", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:19:1: 19:4 (#0), parent_module: None, doc: "", kind: SugaredDoc, indent: 0 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:20:1: 20:70 (#0), parent_module: None, doc: " An example of a non-`Send` type is the reference-counting pointer", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:21:1: 21:85 (#0), parent_module: None, doc: " [`rc::Rc`][`Rc`]. If two threads attempt to clone [`Rc`]s that point to the same", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:22:1: 22:81 (#0), parent_module: None, doc: " reference-counted value, they might try to update the reference count at the", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:23:1: 23:83 (#0), parent_module: None, doc: " same time, which is [undefined behavior][ub] because [`Rc`] doesn't use atomic", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:24:1: 24:84 (#0), parent_module: None, doc: " operations. Its cousin [`sync::Arc`][arc] does use atomic operations (incurring", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:25:1: 25:39 (#0), parent_module: None, doc: " some overhead) and thus is `Send`.", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:26:1: 26:4 (#0), parent_module: None, doc: "", kind: SugaredDoc, indent: 0 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:27:1: 27:74 (#0), parent_module: None, doc: " See [the Nomicon](../../nomicon/send-and-sync.html) for more details.", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:28:1: 28:4 (#0), parent_module: None, doc: "", kind: SugaredDoc, indent: 0 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:29:1: 29:40 (#0), parent_module: None, doc: " [`Rc`]: ../../std/rc/struct.Rc.html", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:30:1: 30:42 (#0), parent_module: None, doc: " [arc]: ../../std/sync/struct.Arc.html", kind: SugaredDoc, indent: 1 }, DocFragment { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:31:1: 31:61 (#0), parent_module: None, doc: " [ub]: ../../reference/behavior-considered-undefined.html", kind: SugaredDoc, indent: 1 }], other_attrs: [Attribute { kind: Normal(AttrItem { path: Path { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:3: 32:9 (#0), segments: [PathSegment { ident: stable#0, id: NodeId(33577), args: None }], tokens: None }, args: Delimited(DelimSpan { open: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:9: 32:10 (#0), close: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:44: 32:45 (#0) }, Parenthesis, TokenStream([(Token(Token { kind: Ident("feature", false), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:10: 32:17 (#0) }), Alone), (Token(Token { kind: Eq, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:18: 32:19 (#0) }), Alone), (Token(Token { kind: Literal(Lit { kind: Str, symbol: "rust1", suffix: None }), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:20: 32:27 (#0) }), Joint), (Token(Token { kind: Comma, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:27: 32:28 (#0) }), Alone), (Token(Token { kind: Ident("since", false), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:29: 32:34 (#0) }), Alone), (Token(Token { kind: Eq, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:35: 32:36 (#0) }), Alone), (Token(Token { kind: Literal(Lit { kind: Str, symbol: "1.0.0", suffix: None }), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:37: 32:44 (#0) }), Alone)])), tokens: None }, None), id: AttrId(48), style: Outer, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:32:1: 32:46 (#0) }, Attribute { kind: Normal(AttrItem { path: Path { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:33:23: 33:44 (#0), segments: [PathSegment { ident: rustc_diagnostic_item#0, id: NodeId(33578), args: None }], tokens: None }, args: Eq(/rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:33:45: 33:46 (#0), Token { kind: Literal(Lit { kind: Str, symbol: "Send", suffix: None }), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:33:47: 33:53 (#0) }), tokens: None }, None), id: AttrId(49), style: Outer, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:33:23: 33:53 (#0) }, Attribute { kind: Normal(AttrItem { path: Path { span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:34:3: 34:25 (#0), segments: [PathSegment { ident: rustc_on_unimplemented#0, id: NodeId(33580), args: None }], tokens: None }, args: Delimited(DelimSpan { open: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:34:25: 34:26 (#0), close: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:37:1: 37:2 (#0) }, Parenthesis, TokenStream([(Token(Token { kind: Ident("message", false), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:35:5: 35:12 (#0) }), Alone), (Token(Token { kind: Eq, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:35:13: 35:14 (#0) }), Alone), (Token(Token { kind: Literal(Lit { kind: Str, symbol: "`{Self}` cannot be sent between threads safely", suffix: None }), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:35:15: 35:63 (#0) }), Joint), (Token(Token { kind: Comma, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:35:63: 35:64 (#0) }), Alone), (Token(Token { kind: Ident("label", false), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:36:5: 36:10 (#0) }), Alone), (Token(Token { kind: Eq, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:36:11: 36:12 (#0) }), Alone), (Token(Token { kind: Literal(Lit { kind: Str, symbol: "`{Self}` cannot be sent between threads safely", suffix: None }), span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:36:13: 36:61 (#0) }), Alone)])), tokens: None }, None), id: AttrId(50), style: Outer, span: /rustc/37b55c8a0cafdb60b9168da34f904acc70157df8/library/core/src/marker.rs:34:1: 37:3 (#0) }] }, visibility: Public, kind: TraitItem(Trait { unsafety: Unsafe, items: [], generics: Generics { params: [], where_predicates: [] }, bounds: [], is_auto: true }), def_id: DefId(DefId(2:3027 ~ core[7f4a]::marker::Send)), cfg: None } ``` After: ``` Item { name: Some("Send"), visibility: Public, def_id: DefId(DefId(2:3027 ~ core[7f4a]::marker::Send)), kind: Trait, docs: "Types that can be transferred across thread boundaries.\n\nThis trait is automatically implemented when the compiler determines it's\nappropriate.\n\nAn example of a non-`Send` type is the reference-counting pointer\n[`rc::Rc`][`Rc`]. If two threads attempt to clone [`Rc`]s that point to the same\nreference-counted value, they might try to update the reference count at the\nsame time, which is [undefined behavior][ub] because [`Rc`] doesn't use atomic\noperations. Its cousin [`sync::Arc`][arc] does use atomic operations (incurring\nsome overhead) and thus is `Send`.\n\nSee [the Nomicon](../../nomicon/send-and-sync.html) for more details.\n\n[`Rc`]: ../../std/rc/struct.Rc.html\n[arc]: ../../std/sync/struct.Arc.html\n[ub]: ../../reference/behavior-considered-undefined.html" } ``` --- src/librustdoc/clean/types.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 47289eb8978b9..5b10101f37c4f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::default::Default; +use std::fmt; use std::hash::Hash; use std::lazy::SyncOnceCell as OnceCell; use std::path::PathBuf; @@ -351,7 +352,7 @@ crate enum ExternalLocation { /// Anything with a source location and set of attributes and, optionally, a /// name. That is, anything that can be documented. This doesn't correspond /// directly to the AST's concept of an item; it's a strict superset. -#[derive(Clone, Debug)] +#[derive(Clone)] crate struct Item { /// The name of this item. /// Optional because not every item has a name, e.g. impls. @@ -366,6 +367,27 @@ crate struct Item { crate cfg: Option>, } +/// NOTE: this does NOT unconditionally print every item, to avoid thousands of lines of logs. +/// If you want to see the debug output for attributes and the `kind` as well, use `{:#?}` instead of `{:?}`. +impl fmt::Debug for Item { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let alternate = f.alternate(); + // hand-picked fields that don't bloat the logs too much + let mut fmt = f.debug_struct("Item"); + fmt.field("name", &self.name) + .field("visibility", &self.visibility) + .field("def_id", &self.def_id); + // allow printing the full item if someone really wants to + if alternate { + fmt.field("attrs", &self.attrs).field("kind", &self.kind).field("cfg", &self.cfg); + } else { + fmt.field("kind", &self.type_()); + fmt.field("docs", &self.doc_value()); + } + fmt.finish() + } +} + // `Item` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Item, 56); From a421cbbead5a9817fdca94a62a119e32684ae068 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Mar 2022 19:34:16 -0400 Subject: [PATCH 4/9] interpret: make isize::MAX the limit for dynamic value sizes --- .../rustc_const_eval/src/interpret/eval_context.rs | 13 +++++++------ compiler/rustc_const_eval/src/interpret/traits.rs | 5 +++-- compiler/rustc_middle/src/mir/interpret/pointer.rs | 5 +++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index abd7094440e57..a81f8b16230a4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -23,8 +23,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou use super::{ AllocCheck, AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, - MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer, Provenance, - Scalar, ScalarMaybeUninit, StackPopJump, + MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer, + PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, StackPopJump, }; use crate::transform::validate::equal_up_to_regions; @@ -678,7 +678,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = size.align_to(align); // Check if this brought us over the size limit. - if size.bytes() >= self.tcx.data_layout.obj_size_bound() { + if size > self.max_size_of_val() { throw_ub!(InvalidMeta("total size is bigger than largest supported object")); } Ok(Some((size, align))) @@ -694,9 +694,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let elem = layout.field(self, 0); // Make sure the slice is not too big. - let size = elem.size.checked_mul(len, self).ok_or_else(|| { - err_ub!(InvalidMeta("slice is bigger than largest supported object")) - })?; + let size = elem.size * len; + if size > self.max_size_of_val() { + throw_ub!(InvalidMeta("slice is bigger than largest supported object")); + } Ok(Some((size, elem.align.abi))) } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 131674decc961..fc60a40e2ada6 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -110,16 +110,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap())? .check_init()?; let size = size.to_machine_usize(self)?; + let size = Size::from_bytes(size); let align = vtable .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap())? .check_init()?; let align = align.to_machine_usize(self)?; let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; - if size >= self.tcx.data_layout.obj_size_bound() { + if size > self.max_size_of_val() { throw_ub!(InvalidVtableSize); } - Ok((Size::from_bytes(size), align)) + Ok((size, align)) } pub fn read_new_vtable_after_trait_upcasting_from_vtable( diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 7d101046a96b4..813c0912f5396 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -18,6 +18,11 @@ pub trait PointerArithmetic: HasDataLayout { self.data_layout().pointer_size } + #[inline(always)] + fn max_size_of_val(&self) -> Size { + Size::from_bytes(self.machine_isize_max()) + } + #[inline] fn machine_usize_max(&self) -> u64 { self.pointer_size().unsigned_int_max().try_into().unwrap() From 53c540a66600a08375f2162f7e20c527deabe7a6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Mar 2022 19:49:52 -0400 Subject: [PATCH 5/9] audit check_mul uses in interpret --- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5eff7d693c5a6..c80d7d7178742 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -531,7 +531,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, Pointer>> { // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); - // The computed offset, in bytes, cannot overflow an isize. + // The computed offset, in bytes, must not overflow an isize. + // `checked_mul` enforces a too small bound, but no actual allocation can be big enough for + // the difference to be noticeable. let offset_bytes = offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; // The offset being in bounds cannot rely on "wrapping around" the address space. @@ -563,6 +565,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let count = self.read_scalar(&count)?.to_machine_usize(self)?; let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; let (size, align) = (layout.size, layout.align.abi); + // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), + // but no actual allocation can be big enough for the difference to be noticeable. let size = size.checked_mul(count, self).ok_or_else(|| { err_ub_format!( "overflow computing total size of `{}`", @@ -588,6 +592,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let byte = self.read_scalar(&byte)?.to_u8()?; let count = self.read_scalar(&count)?.to_machine_usize(self)?; + // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), + // but no actual allocation can be big enough for the difference to be noticeable. let len = layout .size .checked_mul(count, self) From a417911c16d1465f10ddda6cceb34f35edad05eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Mar 2022 20:02:11 -0400 Subject: [PATCH 6/9] catch overflow in slice size computation --- .../src/interpret/eval_context.rs | 3 +- .../const-eval/ub-wide-ptr.32bit.stderr | 53 +++++++++++-------- .../const-eval/ub-wide-ptr.64bit.stderr | 53 +++++++++++-------- src/test/ui/consts/const-eval/ub-wide-ptr.rs | 3 ++ 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a81f8b16230a4..1b8186b5aadba 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -694,7 +694,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let elem = layout.field(self, 0); // Make sure the slice is not too big. - let size = elem.size * len; + let size = elem.size.bytes().saturating_mul(len); // we rely on `max_size_of_val` being smaller than `u64::MAX`. + let size = Size::from_bytes(size); if size > self.max_size_of_val() { throw_ub!(InvalidMeta("slice is bigger than largest supported object")); } diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr index f8c9dca566b93..46901bd3b9b34 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr @@ -104,6 +104,17 @@ LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:71:1 | +LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ff ff ff 7f │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:74:1 + | LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | @@ -113,7 +124,7 @@ LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:74:1 + --> $DIR/ub-wide-ptr.rs:77:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) @@ -124,7 +135,7 @@ LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999us } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:77:1 + --> $DIR/ub-wide-ptr.rs:80:1 | LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -135,7 +146,7 @@ LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3) } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:81:1 + --> $DIR/ub-wide-ptr.rs:84:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .[0]: encountered 0x03, but expected a boolean @@ -146,29 +157,29 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:87:1 + --> $DIR/ub-wide-ptr.rs:90:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..0: encountered 0x03, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾─allocN─╼ │ ╾──╼ + ╾allocN─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:90:1 + --> $DIR/ub-wide-ptr.rs:93:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..1[0]: encountered 0x03, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾─allocN─╼ │ ╾──╼ + ╾allocN─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:97:1 + --> $DIR/ub-wide-ptr.rs:100:1 | LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { LL | | @@ -183,7 +194,7 @@ LL | | }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:105:1 + --> $DIR/ub-wide-ptr.rs:108:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered too small vtable @@ -194,7 +205,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:108:1 + --> $DIR/ub-wide-ptr.rs:111:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered too small vtable @@ -205,7 +216,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:111:1 + --> $DIR/ub-wide-ptr.rs:114:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered dangling vtable pointer in wide pointer @@ -216,7 +227,7 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:113:1 + --> $DIR/ub-wide-ptr.rs:116:1 | LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer @@ -227,7 +238,7 @@ LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92 } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:115:1 + --> $DIR/ub-wide-ptr.rs:118:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -238,7 +249,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92 } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:117:1 + --> $DIR/ub-wide-ptr.rs:120:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -249,7 +260,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:119:1 + --> $DIR/ub-wide-ptr.rs:122:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -260,7 +271,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:123:1 + --> $DIR/ub-wide-ptr.rs:126:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..: encountered 0x03, but expected a boolean @@ -271,7 +282,7 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:127:1 + --> $DIR/ub-wide-ptr.rs:130:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer @@ -282,7 +293,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:129:1 + --> $DIR/ub-wide-ptr.rs:132:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable @@ -293,17 +304,17 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm } error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:135:5 + --> $DIR/ub-wide-ptr.rs:138:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is not a valid pointer error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:139:5 + --> $DIR/ub-wide-ptr.rs:142:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 12 bytes starting at offset N is out-of-bounds -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr index ded007ce28139..b76f88928678a 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr @@ -104,6 +104,17 @@ LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:71:1 | +LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ff ff ff ff ff ff ff 7f │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:74:1 + | LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer | @@ -113,7 +124,7 @@ LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:74:1 + --> $DIR/ub-wide-ptr.rs:77:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) @@ -124,7 +135,7 @@ LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999us } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:77:1 + --> $DIR/ub-wide-ptr.rs:80:1 | LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -135,7 +146,7 @@ LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3) } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:81:1 + --> $DIR/ub-wide-ptr.rs:84:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .[0]: encountered 0x03, but expected a boolean @@ -146,29 +157,29 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:87:1 + --> $DIR/ub-wide-ptr.rs:90:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..0: encountered 0x03, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────allocN───────╼ │ ╾──────╼ + ╾──────allocN───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:90:1 + --> $DIR/ub-wide-ptr.rs:93:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..1[0]: encountered 0x03, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────allocN───────╼ │ ╾──────╼ + ╾──────allocN───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:97:1 + --> $DIR/ub-wide-ptr.rs:100:1 | LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { LL | | @@ -183,7 +194,7 @@ LL | | }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:105:1 + --> $DIR/ub-wide-ptr.rs:108:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered too small vtable @@ -194,7 +205,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:108:1 + --> $DIR/ub-wide-ptr.rs:111:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered too small vtable @@ -205,7 +216,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:111:1 + --> $DIR/ub-wide-ptr.rs:114:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered dangling vtable pointer in wide pointer @@ -216,7 +227,7 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:113:1 + --> $DIR/ub-wide-ptr.rs:116:1 | LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer @@ -227,7 +238,7 @@ LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92 } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:115:1 + --> $DIR/ub-wide-ptr.rs:118:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -238,7 +249,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92 } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:117:1 + --> $DIR/ub-wide-ptr.rs:120:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -249,7 +260,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:119:1 + --> $DIR/ub-wide-ptr.rs:122:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -260,7 +271,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:123:1 + --> $DIR/ub-wide-ptr.rs:126:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at ..: encountered 0x03, but expected a boolean @@ -271,7 +282,7 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:127:1 + --> $DIR/ub-wide-ptr.rs:130:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer @@ -282,7 +293,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:129:1 + --> $DIR/ub-wide-ptr.rs:132:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable @@ -293,17 +304,17 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm } error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:135:5 + --> $DIR/ub-wide-ptr.rs:138:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is not a valid pointer error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:139:5 + --> $DIR/ub-wide-ptr.rs:142:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 24 bytes starting at offset N is out-of-bounds -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs index 0fb9f7960ce1a..ea48a095df95c 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -67,6 +67,9 @@ const SLICE_LENGTH_UNINIT: &[u8] = unsafe { // bad slice: length too big const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; //~^ ERROR it is undefined behavior to use this value +// bad slice: length computation overflows +const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; +//~^ ERROR it is undefined behavior to use this value // bad slice: length not an int const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; //~^ ERROR it is undefined behavior to use this value From cb0d15b0904aacc8f6106a68e17d234d466a027e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Mar 2022 22:34:37 -0400 Subject: [PATCH 7/9] test const size_of_val_raw on big value --- src/test/ui/consts/const-size_of_val-align_of_val.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/ui/consts/const-size_of_val-align_of_val.rs b/src/test/ui/consts/const-size_of_val-align_of_val.rs index 5c0d7d94d64f1..5a78313c48350 100644 --- a/src/test/ui/consts/const-size_of_val-align_of_val.rs +++ b/src/test/ui/consts/const-size_of_val-align_of_val.rs @@ -2,8 +2,9 @@ #![feature(const_size_of_val, const_align_of_val)] #![feature(const_size_of_val_raw, const_align_of_val_raw, layout_for_ptr)] +#![feature(const_slice_from_raw_parts)] -use std::mem; +use std::{mem, ptr}; struct Foo(u32); @@ -34,6 +35,8 @@ const ALIGN_OF_UGH: usize = mem::align_of_val(&UGH); const SIZE_OF_SLICE: usize = mem::size_of_val("foobar".as_bytes()); const SIZE_OF_DANGLING: usize = unsafe { mem::size_of_val_raw(0x100 as *const i32) }; +const SIZE_OF_BIG: usize = + unsafe { mem::size_of_val_raw(ptr::slice_from_raw_parts(0 as *const u8, isize::MAX as usize)) }; const ALIGN_OF_DANGLING: usize = unsafe { mem::align_of_val_raw(0x100 as *const i16) }; fn main() { @@ -46,6 +49,7 @@ fn main() { assert_eq!(ALIGN_OF_UGH, mem::align_of::()); assert_eq!(SIZE_OF_DANGLING, mem::size_of::()); + assert_eq!(SIZE_OF_BIG, isize::MAX as usize); assert_eq!(ALIGN_OF_DANGLING, mem::align_of::()); assert_eq!(SIZE_OF_SLICE, "foobar".len()); From 2983698b203d32a5c59234c022e8ff8dc532b515 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 31 Mar 2022 08:56:41 -0700 Subject: [PATCH 8/9] rustdoc: do not show primitives and keywords as private --- src/librustdoc/clean/types.rs | 19 +++++++++++-------- src/test/rustdoc/keyword.rs | 1 + src/test/rustdoc/primitive.rs | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 src/test/rustdoc/primitive.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 3e60ed2f7c4e5..81c05509c4e80 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -471,14 +471,17 @@ impl Item { ) -> Item { trace!("name={:?}, def_id={:?}", name, def_id); - Item { - def_id: def_id.into(), - kind: box kind, - name, - attrs, - visibility: cx.tcx.visibility(def_id).clean(cx), - cfg, - } + // Primitives and Keywords are written in the source code as private modules. + // The modules need to be private so that nobody actually uses them, but the + // keywords and primitives that they are documenting are public. + let visibility = if matches!(&kind, ItemKind::KeywordItem(..) | ItemKind::PrimitiveItem(..)) + { + Visibility::Public + } else { + cx.tcx.visibility(def_id).clean(cx) + }; + + Item { def_id: def_id.into(), kind: box kind, name, attrs, visibility, cfg } } /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined diff --git a/src/test/rustdoc/keyword.rs b/src/test/rustdoc/keyword.rs index 29ceda4f7c1d3..1cebe4c679770 100644 --- a/src/test/rustdoc/keyword.rs +++ b/src/test/rustdoc/keyword.rs @@ -12,6 +12,7 @@ // @has foo/index.html '//a/@href' '../foo/index.html' // @!has foo/foo/index.html // @!has-dir foo/foo +// @!has foo/index.html '//span' '🔒' #[doc(keyword = "match")] /// this is a test! mod foo{} diff --git a/src/test/rustdoc/primitive.rs b/src/test/rustdoc/primitive.rs new file mode 100644 index 0000000000000..605ca4d170b39 --- /dev/null +++ b/src/test/rustdoc/primitive.rs @@ -0,0 +1,21 @@ +#![crate_name = "foo"] + +#![feature(rustdoc_internals)] + +// @has foo/index.html '//h2[@id="primitives"]' 'Primitive Types' +// @has foo/index.html '//a[@href="primitive.i32.html"]' 'i32' +// @has foo/index.html '//div[@class="sidebar-elems"]//li/a' 'Primitive Types' +// @has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#primitives' +// @has foo/primitive.i32.html '//a[@class="primitive"]' 'i32' +// @has foo/primitive.i32.html '//span[@class="in-band"]' 'Primitive Type i32' +// @has foo/primitive.i32.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' +// @has foo/index.html '//a/@href' '../foo/index.html' +// @!has foo/index.html '//span' '🔒' +#[doc(primitive = "i32")] +/// this is a test! +mod i32{} + +// @has foo/primitive.bool.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello' +#[doc(primitive = "bool")] +/// hello +mod bool {} From 79220247cd28bf84110976bf07e99f7b4aa9ec2b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 17 Mar 2022 11:05:32 +0100 Subject: [PATCH 9/9] Categorize and sort unstable features in std. --- library/std/src/lib.rs | 137 ++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 63 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 133ced5f26cfb..819ec10a4b4b6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -218,111 +218,122 @@ feature(slice_index_methods, coerce_unsized, sgx_platform) )] #![deny(rustc::existing_doc_keyword)] -// std is implemented with unstable features, many of which are internal -// compiler details that will never be stable -// NB: the following list is sorted to minimize merge conflicts. +// +// Language features: #![feature(alloc_error_handler)] -#![feature(alloc_layout_extra)] -#![feature(allocator_api)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] -#![feature(array_error_internals)] -#![feature(assert_matches)] #![feature(associated_type_bounds)] -#![feature(async_iterator)] -#![feature(atomic_mut_ptr)] -#![feature(bench_black_box)] #![feature(box_syntax)] #![feature(c_unwind)] -#![feature(c_variadic)] -#![feature(cfg_accessible)] -#![feature(cfg_eval)] #![feature(cfg_target_thread_local)] -#![feature(char_error_internals)] -#![feature(char_internals)] -#![feature(concat_bytes)] #![feature(concat_idents)] #![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))] #![cfg_attr(bootstrap, feature(const_fn_trait_bound))] -#![feature(const_format_args)] -#![feature(const_io_structs)] -#![feature(const_ip)] -#![feature(const_ipv4)] -#![feature(const_ipv6)] #![feature(const_mut_refs)] -#![feature(const_option)] -#![feature(const_socketaddr)] #![feature(const_trait_impl)] -#![feature(c_size_t)] -#![feature(core_ffi_c)] -#![feature(core_intrinsics)] -#![feature(core_panic)] -#![feature(custom_test_frameworks)] #![feature(decl_macro)] +#![cfg_attr(not(bootstrap), feature(deprecated_suggestion))] #![feature(doc_cfg)] #![feature(doc_cfg_hide)] -#![feature(rustdoc_internals)] -#![cfg_attr(not(bootstrap), feature(deprecated_suggestion))] #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] -#![feature(duration_checked_float)] -#![feature(duration_constants)] -#![feature(edition_panic)] -#![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] -#![feature(extend_one)] -#![feature(float_minimum_maximum)] -#![feature(format_args_nl)] -#![feature(strict_provenance)] -#![feature(get_mut_unchecked)] -#![feature(hashmap_internals)] -#![feature(int_error_internals)] #![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(linkage)] -#![feature(log_syntax)] -#![feature(map_try_insert)] -#![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] -#![feature(mixed_integer_ops)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(new_uninit)] #![feature(nll)] +#![feature(platform_intrinsics)] +#![feature(prelude_import)] +#![feature(rustc_attrs)] +#![feature(rustdoc_internals)] +#![feature(staged_api)] +#![feature(thread_local)] +#![feature(try_blocks)] +// +// Library features (core): +#![feature(array_error_internals)] +#![feature(atomic_mut_ptr)] +#![feature(char_error_internals)] +#![feature(char_internals)] +#![feature(core_intrinsics)] +#![feature(duration_checked_float)] +#![feature(duration_constants)] +#![feature(exact_size_is_empty)] +#![feature(extend_one)] +#![feature(float_minimum_maximum)] +#![feature(hashmap_internals)] +#![feature(int_error_internals)] +#![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_write_slice)] +#![feature(mixed_integer_ops)] #![feature(nonnull_slice_from_raw_parts)] -#![feature(once_cell)] +#![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(panic_can_unwind)] -#![feature(panic_unwind)] -#![feature(platform_intrinsics)] #![feature(portable_simd)] -#![feature(prelude_import)] #![feature(ptr_as_uninit)] #![feature(raw_os_nonzero)] -#![feature(rustc_attrs)] -#![feature(saturating_int_impl)] #![feature(slice_internals)] #![feature(slice_ptr_get)] -#![feature(staged_api)] #![feature(std_internals)] -#![feature(stdsimd)] #![feature(str_internals)] -#![feature(test)] -#![feature(thread_local)] -#![feature(thread_local_internals)] -#![feature(toowned_clone_into)] +#![feature(strict_provenance)] #![feature(total_cmp)] -#![feature(trace_macros)] -#![feature(try_blocks)] +// +// Library features (alloc): +#![feature(alloc_layout_extra)] +#![feature(allocator_api)] +#![feature(get_mut_unchecked)] +#![feature(map_try_insert)] +#![feature(new_uninit)] +#![feature(toowned_clone_into)] #![feature(try_reserve_kind)] #![feature(vec_into_raw_parts)] -// NB: the above list is sorted to minimize merge conflicts. +// +// Library features (unwind): +#![feature(panic_unwind)] +// +// Only for re-exporting: +#![feature(assert_matches)] +#![feature(async_iterator)] +#![feature(c_size_t)] +#![feature(c_variadic)] +#![feature(cfg_accessible)] +#![feature(cfg_eval)] +#![feature(concat_bytes)] +#![feature(const_format_args)] +#![feature(core_ffi_c)] +#![feature(core_panic)] +#![feature(custom_test_frameworks)] +#![feature(edition_panic)] +#![feature(format_args_nl)] +#![feature(log_syntax)] +#![feature(once_cell)] +#![feature(saturating_int_impl)] +#![feature(stdsimd)] +#![feature(test)] +#![feature(trace_macros)] +// +// Only used in tests/benchmarks: +#![feature(bench_black_box)] +// +// Only for const-ness: +#![feature(const_io_structs)] +#![feature(const_ip)] +#![feature(const_ipv4)] +#![feature(const_ipv6)] +#![feature(const_option)] +#![feature(const_socketaddr)] +#![feature(thread_local_internals)] +// #![default_lib_allocator] // Explicitly import the prelude. The compiler uses this same unstable attribute