From 631d767fee70a7514f212b7de72a77dc32587c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 3 Jun 2022 00:00:00 +0000 Subject: [PATCH 1/5] Remove `AlwaysLiveLocals` wrapper struct It is just a wrapper around a `BitSet` and doesn't have any functionality of its own. --- .../src/interpret/eval_context.rs | 4 +-- .../src/transform/validate.rs | 4 +-- .../src/impls/storage_liveness.rs | 5 ++- compiler/rustc_mir_dataflow/src/storage.rs | 34 +++++-------------- compiler/rustc_mir_transform/src/generator.rs | 10 +++--- 5 files changed, 19 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 0c954ac6e5f6e..4c23f84bd0075 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{ self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, }; -use rustc_mir_dataflow::storage::AlwaysLiveLocals; +use rustc_mir_dataflow::storage::always_live_locals; use rustc_query_system::ich::StableHashingContext; use rustc_session::Limit; use rustc_span::{Pos, Span}; @@ -715,7 +715,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Now mark those locals as dead that we do not want to initialize // Mark locals that use `Storage*` annotations as dead on function entry. - let always_live = AlwaysLiveLocals::new(self.body()); + let always_live = always_live_locals(self.body()); for local in locals.indices() { if !always_live.contains(local) { locals[local].value = LocalValue::Dead; diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 665b07c9f89ce..3f54d8642970c 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_mir_dataflow::impls::MaybeStorageLive; -use rustc_mir_dataflow::storage::AlwaysLiveLocals; +use rustc_mir_dataflow::storage::always_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_target::abi::{Size, VariantIdx}; @@ -48,7 +48,7 @@ impl<'tcx> MirPass<'tcx> for Validator { let param_env = tcx.param_env(def_id); let mir_phase = self.mir_phase; - let always_live_locals = AlwaysLiveLocals::new(body); + let always_live_locals = always_live_locals(body); let storage_liveness = MaybeStorageLive::new(always_live_locals) .into_engine(tcx, body) .iterate_to_fixpoint() diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 356a6b7765e16..33d2941814729 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,6 +1,5 @@ pub use super::*; -use crate::storage::AlwaysLiveLocals; use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor}; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -8,11 +7,11 @@ use std::cell::RefCell; #[derive(Clone)] pub struct MaybeStorageLive { - always_live_locals: AlwaysLiveLocals, + always_live_locals: BitSet, } impl MaybeStorageLive { - pub fn new(always_live_locals: AlwaysLiveLocals) -> Self { + pub fn new(always_live_locals: BitSet) -> Self { MaybeStorageLive { always_live_locals } } } diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs index 218d4557215fb..4a354c4c65b08 100644 --- a/compiler/rustc_mir_dataflow/src/storage.rs +++ b/compiler/rustc_mir_dataflow/src/storage.rs @@ -7,35 +7,17 @@ use rustc_middle::mir::{self, Local}; // // FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it // as a field in the `LocalDecl` for each `Local`. -#[derive(Debug, Clone)] -pub struct AlwaysLiveLocals(BitSet); +pub fn always_live_locals(body: &mir::Body<'_>) -> BitSet { + let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); -impl AlwaysLiveLocals { - pub fn new(body: &mir::Body<'_>) -> Self { - let mut always_live_locals = AlwaysLiveLocals(BitSet::new_filled(body.local_decls.len())); - - for block in body.basic_blocks() { - for statement in &block.statements { - use mir::StatementKind::{StorageDead, StorageLive}; - if let StorageLive(l) | StorageDead(l) = statement.kind { - always_live_locals.0.remove(l); - } + for block in body.basic_blocks() { + for statement in &block.statements { + use mir::StatementKind::{StorageDead, StorageLive}; + if let StorageLive(l) | StorageDead(l) = statement.kind { + always_live_locals.remove(l); } } - - always_live_locals } - pub fn into_inner(self) -> BitSet { - self.0 - } -} - -impl std::ops::Deref for AlwaysLiveLocals { - type Target = BitSet; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } + always_live_locals } diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 9eb77f6021373..89895fddd0cfa 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -228,7 +228,7 @@ struct TransformVisitor<'tcx> { suspension_points: Vec>, // The set of locals that have no `StorageLive`/`StorageDead` annotations. - always_live_locals: storage::AlwaysLiveLocals, + always_live_locals: BitSet, // The original RETURN_PLACE local new_ret_local: Local, @@ -450,7 +450,7 @@ struct LivenessInfo { fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - always_live_locals: &storage::AlwaysLiveLocals, + always_live_locals: &BitSet, movable: bool, ) -> LivenessInfo { let body_ref: &Body<'_> = &body; @@ -615,7 +615,7 @@ impl ops::Deref for GeneratorSavedLocals { fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &GeneratorSavedLocals, - always_live_locals: storage::AlwaysLiveLocals, + always_live_locals: BitSet, requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -625,7 +625,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( // Locals that are always live or ones that need to be stored across // suspension points are not eligible for overlap. - let mut ineligible_locals = always_live_locals.into_inner(); + let mut ineligible_locals = always_live_locals; ineligible_locals.intersect(&**saved_locals); // Compute the storage conflicts for all eligible locals. @@ -1300,7 +1300,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }, ); - let always_live_locals = storage::AlwaysLiveLocals::new(&body); + let always_live_locals = storage::always_live_locals(&body); let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); From 39de03d8446f839791ae223446d54b67564a3684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 5 Jun 2022 00:00:00 +0000 Subject: [PATCH 2/5] Change `Direction::{is_forward,is_backward}` functions into constants Make it explicit that the analysis direction is constant. This also makes the value immediately available for optimizations. Previously those functions were neither inline nor generic and so their definition was unavailable when using data flow framework from other crates. --- .../rustc_mir_dataflow/src/framework/cursor.rs | 8 ++++---- .../rustc_mir_dataflow/src/framework/direction.rs | 14 ++++---------- .../rustc_mir_dataflow/src/framework/engine.rs | 4 ++-- .../rustc_mir_dataflow/src/framework/graphviz.rs | 8 ++++---- compiler/rustc_mir_dataflow/src/framework/tests.rs | 4 ++-- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 2de5a9d6991fe..f3b5544aa8b9d 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -109,7 +109,7 @@ where /// For backward analyses, this is the state that will be propagated to its /// predecessors (ignoring edge-specific effects). pub fn seek_to_block_start(&mut self, block: BasicBlock) { - if A::Direction::is_forward() { + if A::Direction::IS_FORWARD { self.seek_to_block_entry(block) } else { self.seek_after(Location { block, statement_index: 0 }, Effect::Primary) @@ -123,7 +123,7 @@ where /// For forward analyses, this is the state that will be propagated to its /// successors (ignoring edge-specific effects). pub fn seek_to_block_end(&mut self, block: BasicBlock) { - if A::Direction::is_backward() { + if A::Direction::IS_BACKWARD { self.seek_to_block_entry(block) } else { self.seek_after(self.body.terminator_loc(block), Effect::Primary) @@ -157,7 +157,7 @@ where self.seek_to_block_entry(target.block); } else if let Some(curr_effect) = self.pos.curr_effect_index { let mut ord = curr_effect.statement_index.cmp(&target.statement_index); - if A::Direction::is_backward() { + if A::Direction::IS_BACKWARD { ord = ord.reverse() } @@ -173,7 +173,7 @@ where debug_assert_eq!(target.block, self.pos.block); let block_data = &self.body[target.block]; - let next_effect = if A::Direction::is_forward() { + let next_effect = if A::Direction::IS_FORWARD { #[rustfmt::skip] self.pos.curr_effect_index.map_or_else( || Effect::Before.at_index(0), diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 3a492b45849c9..05a4d7bbf3e6f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -9,11 +9,9 @@ use super::{ }; pub trait Direction { - fn is_forward() -> bool; + const IS_FORWARD: bool; - fn is_backward() -> bool { - !Self::is_forward() - } + const IS_BACKWARD: bool = !Self::IS_FORWARD; /// Applies all effects between the given `EffectIndex`s. /// @@ -68,9 +66,7 @@ pub trait Direction { pub struct Backward; impl Direction for Backward { - fn is_forward() -> bool { - false - } + const IS_FORWARD: bool = false; fn apply_effects_in_block<'tcx, A>( analysis: &A, @@ -338,9 +334,7 @@ where pub struct Forward; impl Direction for Forward { - fn is_forward() -> bool { - true - } + const IS_FORWARD: bool = true; fn apply_effects_in_block<'tcx, A>( analysis: &A, diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 50efb4c1dc42a..20e14a77c1e57 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -147,7 +147,7 @@ where let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks()); analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); - if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value { + if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != bottom_value { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } @@ -200,7 +200,7 @@ where let mut dirty_queue: WorkQueue = WorkQueue::with_none(body.basic_blocks().len()); - if A::Direction::is_forward() { + if A::Direction::IS_FORWARD { for (bb, _) in traversal::reverse_postorder(body) { dirty_queue.insert(bb); } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 3834c232ab6ba..59a2053ec7000 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -216,7 +216,7 @@ where // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. self.results.seek_to_block_end(block); - if self.results.get() != &block_start_state || A::Direction::is_backward() { + if self.results.get() != &block_start_state || A::Direction::IS_BACKWARD { let after_terminator_name = match terminator.kind { mir::TerminatorKind::Call { target: Some(_), .. } => "(on unwind)", _ => "(on end)", @@ -390,7 +390,7 @@ where let mut afters = diffs.after.into_iter(); let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| { - if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() } + if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() } }; for (i, statement) in body[block].statements.iter().enumerate() { @@ -527,7 +527,7 @@ where _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, ) { - if A::Direction::is_forward() { + if A::Direction::IS_FORWARD { self.prev_state.clone_from(state); } } @@ -538,7 +538,7 @@ where _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, ) { - if A::Direction::is_backward() { + if A::Direction::IS_BACKWARD { self.prev_state.clone_from(state); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 74c3b44f4250e..d9461fd3abd81 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -140,7 +140,7 @@ impl MockAnalysis<'_, D> { SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), }; - let mut pos = if D::is_forward() { + let mut pos = if D::IS_FORWARD { Effect::Before.at_index(0) } else { Effect::Before.at_index(self.body[block].statements.len()) @@ -153,7 +153,7 @@ impl MockAnalysis<'_, D> { return ret; } - if D::is_forward() { + if D::IS_FORWARD { pos = pos.next_in_forward_order(); } else { pos = pos.next_in_backward_order(); From 786c8b4419fbc8865e12343a6e7848b153d14e54 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 7 Jun 2022 22:54:15 +0900 Subject: [PATCH 3/5] Add regresion test for #95307 --- src/test/ui/async-await/issues/issue-95307.rs | 13 ++++++++ .../ui/async-await/issues/issue-95307.stderr | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/ui/async-await/issues/issue-95307.rs create mode 100644 src/test/ui/async-await/issues/issue-95307.stderr diff --git a/src/test/ui/async-await/issues/issue-95307.rs b/src/test/ui/async-await/issues/issue-95307.rs new file mode 100644 index 0000000000000..f7e48070ccde6 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-95307.rs @@ -0,0 +1,13 @@ +// edition:2018 + +// Regression test for #95307. +// The ICE occurred on all the editions, specifying edition:2018 to reduce diagnostics. + +pub trait C { + async fn new() -> [u8; _]; + //~^ ERROR: functions in traits cannot be declared `async` + //~| ERROR: using `_` for array lengths is unstable + //~| ERROR: in expressions, `_` can only be used on the left-hand side of an assignment +} + +fn main() {} diff --git a/src/test/ui/async-await/issues/issue-95307.stderr b/src/test/ui/async-await/issues/issue-95307.stderr new file mode 100644 index 0000000000000..60fca71eb4b62 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-95307.stderr @@ -0,0 +1,30 @@ +error[E0706]: functions in traits cannot be declared `async` + --> $DIR/issue-95307.rs:7:5 + | +LL | async fn new() -> [u8; _]; + | -----^^^^^^^^^^^^^^^^^^^^^ + | | + | `async` because of this + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error[E0658]: using `_` for array lengths is unstable + --> $DIR/issue-95307.rs:7:28 + | +LL | async fn new() -> [u8; _]; + | ^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/issue-95307.rs:7:28 + | +LL | async fn new() -> [u8; _]; + | ^ `_` not allowed here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0658, E0706. +For more information about an error, try `rustc --explain E0658`. From 1c26dd0db4bb8b0613c19c14f7fcbb0d4fa22726 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Tue, 7 Jun 2022 14:28:20 -0400 Subject: [PATCH 4/5] RustWrapper: adapt to APInt API changes in LLVM 15 In https://reviews.llvm.org/D125556 upstream changed sext() and zext() to allow some no-op cases, which previously required use of the *OrSelf() methods, which I assume is what was going on here. The *OrSelf() methods got removed in https://reviews.llvm.org/D125559 after two weeks of deprecation because they came with some bonus (probably-undesired) behavior. Since the behavior of sext() and zext() changed slightly, I kept the old *OrSelf() calls in LLVM 14 and earlier, and only use the new version in LLVM 15. r? @nikic --- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6d79e662a4279..f90bb7f236868 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1542,11 +1542,19 @@ extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *hig auto C = unwrap(CV); if (C->getBitWidth() > 128) { return false; } APInt AP; +#if LLVM_VERSION_GE(15, 0) + if (sext) { + AP = C->getValue().sext(128); + } else { + AP = C->getValue().zext(128); + } +#else if (sext) { AP = C->getValue().sextOrSelf(128); } else { AP = C->getValue().zextOrSelf(128); } +#endif *low = AP.getLoBits(64).getZExtValue(); *high = AP.getHiBits(64).getZExtValue(); return true; From 8542dd02a8c00bba435efe0bed20bc9bb22e7ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 7 Jun 2022 13:39:21 -0700 Subject: [PATCH 5/5] Use more targeted suggestion when confusing i8 with std::i8 --- compiler/rustc_typeck/src/astconv/mod.rs | 19 +++++++-------- .../rustc_typeck/src/check/method/suggest.rs | 24 ++++++++----------- .../suggest-std-when-using-type.stderr | 4 ++-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index bcff2ae512909..b1e180c701f50 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -1575,18 +1575,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { name: Symbol, ) -> ErrorGuaranteed { let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); - if let (true, Ok(snippet)) = ( - self.tcx() - .resolutions(()) - .confused_type_with_std_module - .keys() - .any(|full_span| full_span.contains(span)), - self.tcx().sess.source_map().span_to_snippet(span), - ) { + if self + .tcx() + .resolutions(()) + .confused_type_with_std_module + .keys() + .any(|full_span| full_span.contains(span)) + { err.span_suggestion( - span, + span.shrink_to_lo(), "you are looking for the module in `std`, not the primitive type", - format!("std::{}", snippet), + "std::".to_string(), Applicability::MachineApplicable, ); } else { diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 0e198907c8d50..4071c389266d3 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -327,26 +327,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { - err.span_suggestion( - *span, - "you are looking for the module in `std`, \ - not the primitive type", - format!("std::{}", snippet), - Applicability::MachineApplicable, - ); - } + err.span_suggestion( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::".to_string(), + Applicability::MachineApplicable, + ); } if let ty::RawPtr(_) = &actual.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ - type behind the pointer: https://doc.rust-lang.org/std/\ - primitive.pointer.html#method.as_ref", + type behind the pointer: https://doc.rust-lang.org/std/\ + primitive.pointer.html#method.as_ref", ); err.note( - "using `<*const T>::as_ref()` on a pointer \ - which is unaligned or points to invalid \ - or uninitialized memory is undefined behavior", + "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ + to invalid or uninitialized memory is undefined behavior", ); } diff --git a/src/test/ui/suggestions/suggest-std-when-using-type.stderr b/src/test/ui/suggestions/suggest-std-when-using-type.stderr index 4255281d9a7ac..2840fa121a75f 100644 --- a/src/test/ui/suggestions/suggest-std-when-using-type.stderr +++ b/src/test/ui/suggestions/suggest-std-when-using-type.stderr @@ -7,7 +7,7 @@ LL | let pi = f32::consts::PI; help: you are looking for the module in `std`, not the primitive type | LL | let pi = std::f32::consts::PI; - | ~~~~~~~~~~~~~~~~ + | +++++ error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope --> $DIR/suggest-std-when-using-type.rs:5:14 @@ -18,7 +18,7 @@ LL | str::from_utf8(bytes) help: you are looking for the module in `std`, not the primitive type | LL | std::str::from_utf8(bytes) - | ~~~~~~~~~~~~~~~~~~~ + | +++++ error: aborting due to 2 previous errors