diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 187861ba127bd..87db08ef5b510 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -813,17 +813,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if *outlived_f != ty::ReStatic { return; } + let suitable_region = self.infcx.tcx.is_suitable_region(f); + let Some(suitable_region) = suitable_region else { return; }; - let fn_returns = self - .infcx - .tcx - .is_suitable_region(f) - .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id)) - .unwrap_or_default(); - - if fn_returns.is_empty() { - return; - } + let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id); let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) { param @@ -839,15 +832,43 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let captures = format!("captures data from {arg}"); - return nice_region_error::suggest_new_region_bound( - self.infcx.tcx, - diag, - fn_returns, - lifetime.to_string(), - Some(arg), - captures, - Some((param.param_ty_span, param.param_ty.to_string())), - self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id), + if !fn_returns.is_empty() { + nice_region_error::suggest_new_region_bound( + self.infcx.tcx, + diag, + fn_returns, + lifetime.to_string(), + Some(arg), + captures, + Some((param.param_ty_span, param.param_ty.to_string())), + Some(suitable_region.def_id), + ); + return; + } + + let Some((alias_tys, alias_span)) = self + .infcx + .tcx + .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; }; + + // in case the return type of the method is a type alias + let mut spans_suggs: Vec<_> = Vec::new(); + for alias_ty in alias_tys { + if alias_ty.span.desugaring_kind().is_some() { + // Skip `async` desugaring `impl Future`. + () + } + if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { + spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); + } + } + spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string())); + diag.multipart_suggestion_verbose( + &format!( + "to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias" + ), + spans_suggs, + Applicability::MaybeIncorrect, ); } } diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index 8f063f5082c95..a3e2002da781c 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -238,6 +238,7 @@ parse_const_let_mutually_exclusive = `const` and `let` are mutually exclusive parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed +parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable .suggestion = initialize the variable diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d6566860f8170..b456bd08048db 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3524,6 +3524,13 @@ impl<'hir> Node<'hir> { } } + pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> { + match self { + Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty), + _ => None, + } + } + pub fn body_id(&self) -> Option { match self { Node::TraitItem(TraitItem { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bbf7b81a2cc66..d5f37abb8b455 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1619,6 +1619,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } } + fn note_unreachable_loop_return( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index bd1626dff7951..812dd4054b545 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -59,7 +59,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) - || self.suggest_floating_point_literal(err, expr, expected); + || self.suggest_floating_point_literal(err, expr, expected) + || self.note_result_coercion(err, expr, expected, expr_ty); if !suggested { self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected); } @@ -697,6 +698,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + pub(crate) fn note_result_coercion( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let ty::Adt(e, substs_e) = expected.kind() else { return false; }; + let ty::Adt(f, substs_f) = found.kind() else { return false; }; + if e.did() != f.did() { + return false; + } + if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { + return false; + } + let map = self.tcx.hir(); + if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) + && let hir::ExprKind::Ret(_) = expr.kind + { + // `return foo;` + } else if map.get_return_block(expr.hir_id).is_some() { + // Function's tail expression. + } else { + return false; + } + let e = substs_e.type_at(1); + let f = substs_f.type_at(1); + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [f, e], + self.param_env, + ) + .must_apply_modulo_regions() + { + err.multipart_suggestion( + "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ + in `Ok` so the expression remains of type `Result`", + vec![ + (expr.span.shrink_to_lo(), "Ok(".to_string()), + (expr.span.shrink_to_hi(), "?)".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 44da3fbe300ca..bb2dd290c6d5d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -985,7 +985,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let vis = self.get_visibility(id); let span = self.get_span(id, sess); let macro_rules = match kind { - DefKind::Macro(..) => self.root.tables.macro_rules.get(self, id).is_some(), + DefKind::Macro(..) => self.root.tables.is_macro_rules.get(self, id), _ => false, }; @@ -1283,7 +1283,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef { match self.def_kind(id) { DefKind::Macro(_) => { - let macro_rules = self.root.tables.macro_rules.get(self, id).is_some(); + let macro_rules = self.root.tables.is_macro_rules.get(self, id); let body = self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess)); ast::MacroDef { macro_rules, body: ast::ptr::P(body) } @@ -1595,11 +1595,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_attr_flags(self, index: DefIndex) -> AttrFlags { - self.root.tables.attr_flags.get(self, index).unwrap_or(AttrFlags::empty()) + self.root.tables.attr_flags.get(self, index) } fn get_is_intrinsic(self, index: DefIndex) -> bool { - self.root.tables.is_intrinsic.get(self, index).is_some() + self.root.tables.is_intrinsic.get(self, index) } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 2fa645cd9e33d..eebc2f21dfe4e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -226,12 +226,7 @@ provide! { tcx, def_id, other, cdata, deduced_param_attrs => { table } is_type_alias_impl_trait => { debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy); - cdata - .root - .tables - .is_type_alias_impl_trait - .get(cdata, def_id.index) - .is_some() + cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index) } collect_return_position_impl_trait_in_trait_tys => { Ok(cdata diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2ecaa33d4d315..97f0457ba7116 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -483,7 +483,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) } - fn encode_source_map(&mut self) -> LazyTable> { + fn encode_source_map(&mut self) -> LazyTable>> { let source_map = self.tcx.sess.source_map(); let all_source_files = source_map.files(); @@ -1130,7 +1130,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } if !attr_flags.is_empty() { - self.tables.attr_flags.set(def_id.local_def_index, attr_flags); + self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags); } } @@ -1387,7 +1387,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); if tcx.is_intrinsic(def_id) { - self.tables.is_intrinsic.set(def_id.index, ()); + self.tables.is_intrinsic.set_nullable(def_id.index, true); } } } @@ -1519,7 +1519,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } hir::ItemKind::Macro(ref macro_def, _) => { if macro_def.macro_rules { - self.tables.macro_rules.set(def_id.index, ()); + self.tables.is_macro_rules.set_nullable(def_id.index, true); } record!(self.tables.macro_definition[def_id] <- &*macro_def.body); } @@ -1529,7 +1529,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::OpaqueTy(ref opaque) => { self.encode_explicit_item_bounds(def_id); if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) { - self.tables.is_type_alias_impl_trait.set(def_id.index, ()); + self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true); } } hir::ItemKind::Enum(..) => { @@ -1636,7 +1636,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let hir::ItemKind::Fn(..) = item.kind { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); if tcx.is_intrinsic(def_id) { - self.tables.is_intrinsic.set(def_id.index, ()); + self.tables.is_intrinsic.set_nullable(def_id.index, true); } } if let hir::ItemKind::Impl { .. } = item.kind { @@ -2038,7 +2038,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let hir::ForeignItemKind::Fn(..) = nitem.kind { if tcx.is_intrinsic(def_id) { - self.tables.is_intrinsic.set(def_id.index, ()); + self.tables.is_intrinsic.set_nullable(def_id.index, true); } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 69690264ae4ea..698b2ebc4732a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -185,9 +185,9 @@ enum LazyState { Previous(NonZeroUsize), } -type SyntaxContextTable = LazyTable>; -type ExpnDataTable = LazyTable>; -type ExpnHashTable = LazyTable>; +type SyntaxContextTable = LazyTable>>; +type ExpnDataTable = LazyTable>>; +type ExpnHashTable = LazyTable>>; #[derive(MetadataEncodable, MetadataDecodable)] pub(crate) struct ProcMacroData { @@ -253,7 +253,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, - source_map: LazyTable>, + source_map: LazyTable>>, compiler_builtins: bool, needs_allocator: bool, @@ -315,21 +315,27 @@ pub(crate) struct IncoherentImpls { /// Define `LazyTables` and `TableBuilders` at the same time. macro_rules! define_tables { - ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => { + ( + - nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+ + - optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+ + ) => { #[derive(MetadataEncodable, MetadataDecodable)] pub(crate) struct LazyTables { - $($name: LazyTable<$IDX, $T>),+ + $($name1: LazyTable<$IDX1, $T1>,)+ + $($name2: LazyTable<$IDX2, Option<$T2>>,)+ } #[derive(Default)] struct TableBuilders { - $($name: TableBuilder<$IDX, $T>),+ + $($name1: TableBuilder<$IDX1, $T1>,)+ + $($name2: TableBuilder<$IDX2, Option<$T2>>,)+ } impl TableBuilders { fn encode(&self, buf: &mut FileEncoder) -> LazyTables { LazyTables { - $($name: self.$name.encode(buf)),+ + $($name1: self.$name1.encode(buf),)+ + $($name2: self.$name2.encode(buf),)+ } } } @@ -337,9 +343,15 @@ macro_rules! define_tables { } define_tables! { +- nullable: + is_intrinsic: Table, + is_macro_rules: Table, + is_type_alias_impl_trait: Table, + attr_flags: Table, + +- optional: attributes: Table>, children: Table>, - opt_def_kind: Table, visibility: Table>>, def_span: Table>, @@ -370,7 +382,6 @@ define_tables! { impl_parent: Table, impl_polarity: Table, constness: Table, - is_intrinsic: Table, impl_defaultness: Table, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table>, @@ -380,7 +391,6 @@ define_tables! { fn_arg_names: Table>, generator_kind: Table>, trait_def: Table>, - trait_item_def_id: Table, inherent_impls: Table>, expn_that_defined: Table>, @@ -395,18 +405,12 @@ define_tables! { def_path_hashes: Table, proc_macro_quoted_spans: Table>, generator_diagnostic_data: Table>>, - attr_flags: Table, variant_data: Table>, assoc_container: Table, - // Slot is full when macro is macro_rules. - macro_rules: Table, macro_definition: Table>, proc_macro: Table, module_reexports: Table>, deduced_param_attrs: Table>, - // Slot is full when opaque is TAIT. - is_type_alias_impl_trait: Table, - trait_impl_trait_tys: Table>>>, } @@ -419,6 +423,7 @@ struct VariantData { } bitflags::bitflags! { + #[derive(Default)] pub struct AttrFlags: u8 { const MAY_HAVE_DOC_LINKS = 1 << 0; const IS_DOC_HIDDEN = 1 << 1; diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index dc003227d40bd..70dbf6476e2fa 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -16,6 +16,7 @@ use std::num::NonZeroUsize; /// but this has no impact on safety. pub(super) trait FixedSizeEncoding: Default { /// This should be `[u8; BYTE_LEN]`; + /// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations. type ByteArray; fn from_bytes(b: &Self::ByteArray) -> Self; @@ -199,31 +200,31 @@ impl FixedSizeEncoding for Option { } } -impl FixedSizeEncoding for Option { +impl FixedSizeEncoding for AttrFlags { type ByteArray = [u8; 1]; #[inline] fn from_bytes(b: &[u8; 1]) -> Self { - (b[0] != 0).then(|| AttrFlags::from_bits_truncate(b[0])) + AttrFlags::from_bits_truncate(b[0]) } #[inline] fn write_to_bytes(self, b: &mut [u8; 1]) { - b[0] = self.map_or(0, |flags| flags.bits()) + b[0] = self.bits(); } } -impl FixedSizeEncoding for Option<()> { +impl FixedSizeEncoding for bool { type ByteArray = [u8; 1]; #[inline] fn from_bytes(b: &[u8; 1]) -> Self { - (b[0] != 0).then(|| ()) + b[0] != 0 } #[inline] fn write_to_bytes(self, b: &mut [u8; 1]) { - b[0] = self.is_some() as u8 + b[0] = self as u8 } } @@ -273,44 +274,38 @@ impl FixedSizeEncoding for Option> { } /// Helper for constructing a table's serialization (also see `Table`). -pub(super) struct TableBuilder -where - Option: FixedSizeEncoding, -{ - blocks: IndexVec as FixedSizeEncoding>::ByteArray>, +pub(super) struct TableBuilder { + blocks: IndexVec, _marker: PhantomData, } -impl Default for TableBuilder -where - Option: FixedSizeEncoding, -{ +impl Default for TableBuilder { fn default() -> Self { TableBuilder { blocks: Default::default(), _marker: PhantomData } } } -impl TableBuilder +impl TableBuilder> where - Option: FixedSizeEncoding, + Option: FixedSizeEncoding, { - pub(crate) fn set(&mut self, i: I, value: T) - where - Option: FixedSizeEncoding, - { + pub(crate) fn set(&mut self, i: I, value: T) { + self.set_nullable(i, Some(value)) + } +} + +impl> TableBuilder { + pub(crate) fn set_nullable(&mut self, i: I, value: T) { // FIXME(eddyb) investigate more compact encodings for sparse tables. // On the PR @michaelwoerister mentioned: // > Space requirements could perhaps be optimized by using the HAMT `popcnt` // > trick (i.e. divide things into buckets of 32 or 64 items and then // > store bit-masks of which item in each bucket is actually serialized). self.blocks.ensure_contains_elem(i, || [0; N]); - Some(value).write_to_bytes(&mut self.blocks[i]); + value.write_to_bytes(&mut self.blocks[i]); } - pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable - where - Option: FixedSizeEncoding, - { + pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable { let pos = buf.position(); for block in &self.blocks { buf.emit_raw_bytes(block); @@ -323,34 +318,27 @@ where } } -impl LazyTable +impl + ParameterizedOverTcx> + LazyTable where - Option: FixedSizeEncoding, + for<'tcx> T::Value<'tcx>: FixedSizeEncoding, { /// Given the metadata, extract out the value at a particular index (if any). #[inline(never)] - pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>, const N: usize>( - &self, - metadata: M, - i: I, - ) -> Option> - where - Option>: FixedSizeEncoding, - { + pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> { debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size); let start = self.position.get(); let bytes = &metadata.blob()[start..start + self.encoded_size]; let (bytes, []) = bytes.as_chunks::() else { panic!() }; - let bytes = bytes.get(i.index())?; - FixedSizeEncoding::from_bytes(bytes) + match bytes.get(i.index()) { + Some(bytes) => FixedSizeEncoding::from_bytes(bytes), + None => FixedSizeEncoding::from_bytes(&[0; N]), + } } /// Size of the table in entries, including possible gaps. - pub(super) fn size(&self) -> usize - where - for<'tcx> Option>: FixedSizeEncoding, - { + pub(super) fn size(&self) -> usize { self.encoded_size / N } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ce04d8d21f4cd..0b16270ea9874 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -997,6 +997,30 @@ impl<'tcx> TyCtxt<'tcx> { v.0 } + /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used + pub fn return_type_impl_or_dyn_traits_with_type_alias( + self, + scope_def_id: LocalDefId, + ) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> { + let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); + let mut v = TraitObjectVisitor(vec![], self.hir()); + // when the return type is a type alias + if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id) + && let hir::TyKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind + && let Some(local_id) = def_id.as_local() + && let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias + && let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics() + { + v.visit_ty(alias_ty); + if !v.0.is_empty() { + return Some((v.0, alias_generics.span)); + } + } + return None; + } + pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { // `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures. match self.hir().get_by_def_id(scope_def_id) { diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06b970ad97977..40763da0bb547 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -337,7 +337,9 @@ pub(crate) struct IfExpressionMissingThenBlock { #[primary_span] pub if_span: Span, #[subdiagnostic] - pub sub: IfExpressionMissingThenBlockSub, + pub missing_then_block_sub: IfExpressionMissingThenBlockSub, + #[subdiagnostic] + pub let_else_sub: Option, } #[derive(Subdiagnostic)] @@ -348,6 +350,13 @@ pub(crate) enum IfExpressionMissingThenBlockSub { AddThenBlock(#[primary_span] Span), } +#[derive(Subdiagnostic)] +#[help(parse_extra_if_in_let_else)] +pub(crate) struct IfExpressionLetSomeSub { + #[primary_span] + pub if_span: Span, +} + #[derive(Diagnostic)] #[diag(parse_if_expression_missing_condition)] pub(crate) struct IfExpressionMissingCondition { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index bf93a89f06555..3225a309a319b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -11,15 +11,15 @@ use crate::errors::{ ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, - IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, - InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, - InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, - InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, - LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, - MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, - MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, - OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock, + IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, + InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, + InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, + LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, + MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, + MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, + MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator, + NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf, UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, @@ -2251,9 +2251,10 @@ impl<'a> Parser<'a> { if let ExprKind::Block(_, None) = right.kind => { self.sess.emit_err(IfExpressionMissingThenBlock { if_span: lo, - sub: IfExpressionMissingThenBlockSub::UnfinishedCondition( - cond_span.shrink_to_lo().to(*binop_span) - ), + missing_then_block_sub: + IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)), + let_else_sub: None, + }); std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi())) }, @@ -2279,9 +2280,15 @@ impl<'a> Parser<'a> { if let Some(block) = recover_block_from_condition(self) { block } else { + let let_else_sub = matches!(cond.kind, ExprKind::Let(..)) + .then(|| IfExpressionLetSomeSub { if_span: lo }); + self.sess.emit_err(IfExpressionMissingThenBlock { if_span: lo, - sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()), + missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock( + cond_span.shrink_to_hi(), + ), + let_else_sub, }); self.mk_block_err(cond_span.shrink_to_hi()) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 40b9bedc84fd3..d59fa71406c31 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,5 +1,6 @@ use std::mem; +use super::{Certainty, InferCtxtEvalExt}; use rustc_infer::{ infer::InferCtxt, traits::{ @@ -8,8 +9,6 @@ use rustc_infer::{ }, }; -use super::{search_graph, Certainty, EvalCtxt}; - /// A trait engine using the new trait solver. /// /// This is mostly identical to how `evaluate_all` works inside of the @@ -66,9 +65,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx); - let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph); - let (changed, certainty) = match ecx.evaluate_goal(goal) { + let (changed, certainty) = match infcx.evaluate_root_goal(goal) { Ok(result) => result, Err(NoSolution) => { errors.push(FulfillmentError { diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index da2a1a19957e1..70f094014453e 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -152,6 +152,36 @@ impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> { } } +pub trait InferCtxtEvalExt<'tcx> { + /// Evaluates a goal from **outside** of the trait solver. + /// + /// Using this while inside of the solver is wrong as it uses a new + /// search graph which would break cycle detection. + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty), NoSolution>; +} + +impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty), NoSolution> { + let mut search_graph = search_graph::SearchGraph::new(self.tcx); + + let result = EvalCtxt { + search_graph: &mut search_graph, + infcx: self, + var_values: CanonicalVarValues::dummy(), + } + .evaluate_goal(goal); + + assert!(search_graph.is_empty()); + result + } +} + struct EvalCtxt<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, var_values: CanonicalVarValues<'tcx>, @@ -164,18 +194,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.infcx.tcx } - /// Creates a new evaluation context outside of the trait solver. - /// - /// With this solver making a canonical response doesn't make much sense. - /// The `search_graph` for this solver has to be completely empty. - fn new_outside_solver( - infcx: &'a InferCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, - ) -> EvalCtxt<'a, 'tcx> { - assert!(search_graph.is_empty()); - EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph } - } - #[instrument(level = "debug", skip(tcx, search_graph), ret)] fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 7757068a4f2b9..f74e563f1b9c0 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -298,3 +298,7 @@ impl const From for OnceCell { OnceCell { inner: UnsafeCell::new(Some(value)) } } } + +// Just like for `Cell` this isn't needed, but results in nicer error messages. +#[unstable(feature = "once_cell", issue = "74465")] +impl !Sync for OnceCell {} diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2a7ec544f9e2e..0145ba5e3cde6 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -489,9 +489,26 @@ pub struct Arguments<'a> { } impl<'a> Arguments<'a> { - /// Get the formatted string, if it has no arguments to be formatted. + /// Get the formatted string, if it has no arguments to be formatted at runtime. /// - /// This can be used to avoid allocations in the most trivial case. + /// This can be used to avoid allocations in some cases. + /// + /// # Guarantees + /// + /// For `format_args!("just a literal")`, this function is guaranteed to + /// return `Some("just a literal")`. + /// + /// For most cases with placeholders, this function will return `None`. + /// + /// However, the compiler may perform optimizations that can cause this + /// function to return `Some(_)` even if the format string contains + /// placeholders. For example, `format_args!("Hello, {}!", "world")` may be + /// optimized to `format_args!("Hello, world!")`, such that `as_str()` + /// returns `Some("Hello, world!")`. + /// + /// The behavior for anything but the trivial case (without placeholders) + /// is not guaranteed, and should not be relied upon for anything other + /// than optimization. /// /// # Examples /// @@ -512,7 +529,7 @@ impl<'a> Arguments<'a> { /// ```rust /// assert_eq!(format_args!("hello").as_str(), Some("hello")); /// assert_eq!(format_args!("").as_str(), Some("")); - /// assert_eq!(format_args!("{}", 1).as_str(), None); + /// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None); /// ``` #[stable(feature = "fmt_as_str", since = "1.52.0")] #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1326fc9ab096f..74055602ec2e6 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -469,6 +469,62 @@ pub macro Copy($item:item) { #[cfg_attr(not(test), rustc_diagnostic_item = "Sync")] #[lang = "sync"] #[rustc_on_unimplemented( + on( + _Self = "std::cell::OnceCell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead" + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`", + ), + on( + _Self = "std::cell::RefCell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead", + ), message = "`{Self}` cannot be shared between threads safely", label = "`{Self}` cannot be shared between threads safely" )] diff --git a/tests/ui/async-await/issue-68112.drop_tracking.stderr b/tests/ui/async-await/issue-68112.drop_tracking.stderr index f2802698fd5b6..bd648de30672d 100644 --- a/tests/ui/async-await/issue-68112.drop_tracking.stderr +++ b/tests/ui/async-await/issue-68112.drop_tracking.stderr @@ -5,6 +5,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:34:17 | @@ -23,6 +24,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:43:17 | @@ -43,6 +45,7 @@ LL | require_send(send_fut); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this `async fn` body --> $DIR/issue-68112.rs:50:31 diff --git a/tests/ui/async-await/issue-68112.no_drop_tracking.stderr b/tests/ui/async-await/issue-68112.no_drop_tracking.stderr index 38eb85b302fd5..35b7341f63a4d 100644 --- a/tests/ui/async-await/issue-68112.no_drop_tracking.stderr +++ b/tests/ui/async-await/issue-68112.no_drop_tracking.stderr @@ -5,6 +5,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:34:17 | @@ -23,6 +24,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:43:17 | @@ -43,6 +45,7 @@ LL | require_send(send_fut); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this `async fn` body --> $DIR/issue-68112.rs:50:31 diff --git a/tests/ui/generator/issue-68112.rs b/tests/ui/generator/issue-68112.rs index 21026f45cb823..9def544e3d25c 100644 --- a/tests/ui/generator/issue-68112.rs +++ b/tests/ui/generator/issue-68112.rs @@ -40,6 +40,7 @@ fn test1() { require_send(send_gen); //~^ ERROR generator cannot be sent between threads //~| NOTE not `Send` + //~| NOTE use `std::sync::RwLock` instead } pub fn make_gen2(t: T) -> impl Generator { @@ -66,6 +67,7 @@ fn test2() { //~| NOTE required for //~| NOTE required by a bound introduced by this call //~| NOTE captures the following types + //~| NOTE use `std::sync::RwLock` instead } fn main() {} diff --git a/tests/ui/generator/issue-68112.stderr b/tests/ui/generator/issue-68112.stderr index eb99d42c92068..b42bc93d01f66 100644 --- a/tests/ui/generator/issue-68112.stderr +++ b/tests/ui/generator/issue-68112.stderr @@ -5,6 +5,7 @@ LL | require_send(send_gen); | ^^^^^^^^ generator is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: generator is not `Send` as this value is used across a yield --> $DIR/issue-68112.rs:36:9 | @@ -23,7 +24,7 @@ LL | fn require_send(_: impl Send) {} | ^^^^ required by this bound in `require_send` error[E0277]: `RefCell` cannot be shared between threads safely - --> $DIR/issue-68112.rs:63:18 + --> $DIR/issue-68112.rs:64:18 | LL | require_send(send_gen); | ------------ ^^^^^^^^ `RefCell` cannot be shared between threads safely @@ -31,25 +32,26 @@ LL | require_send(send_gen); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this generator - --> $DIR/issue-68112.rs:48:5 + --> $DIR/issue-68112.rs:49:5 | LL | || { | ^^ note: required because it appears within the type `impl Generator>>` - --> $DIR/issue-68112.rs:45:30 + --> $DIR/issue-68112.rs:46:30 | LL | pub fn make_gen2(t: T) -> impl Generator { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `impl Generator>>` - --> $DIR/issue-68112.rs:53:34 + --> $DIR/issue-68112.rs:54:34 | LL | fn make_non_send_generator2() -> impl Generator>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: required because it captures the following types: `impl Generator>>`, `()` note: required because it's used within this generator - --> $DIR/issue-68112.rs:59:20 + --> $DIR/issue-68112.rs:60:20 | LL | let send_gen = || { | ^^ diff --git a/tests/ui/generator/not-send-sync.stderr b/tests/ui/generator/not-send-sync.stderr index a821c57b923a0..1711df729b8c0 100644 --- a/tests/ui/generator/not-send-sync.stderr +++ b/tests/ui/generator/not-send-sync.stderr @@ -12,6 +12,7 @@ LL | | }); | |_____^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `&Cell` to implement `Send` note: required because it's used within this generator --> $DIR/not-send-sync.rs:16:17 @@ -36,6 +37,7 @@ LL | | }); | |_____^ generator is not `Sync` | = help: within `[generator@$DIR/not-send-sync.rs:9:17: 9:19]`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: generator is not `Sync` as this value is used across a yield --> $DIR/not-send-sync.rs:12:9 | diff --git a/tests/ui/generator/print/generator-print-verbose-1.stderr b/tests/ui/generator/print/generator-print-verbose-1.stderr index ebf35be581c60..45d018b8ebad5 100644 --- a/tests/ui/generator/print/generator-print-verbose-1.stderr +++ b/tests/ui/generator/print/generator-print-verbose-1.stderr @@ -5,6 +5,7 @@ LL | require_send(send_gen); | ^^^^^^^^ generator is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: generator is not `Send` as this value is used across a yield --> $DIR/generator-print-verbose-1.rs:35:9 | @@ -29,6 +30,7 @@ LL | require_send(send_gen); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this generator --> $DIR/generator-print-verbose-1.rs:42:5 diff --git a/tests/ui/generator/print/generator-print-verbose-2.stderr b/tests/ui/generator/print/generator-print-verbose-2.stderr index 909e49c38b8d1..59112ce0a79e6 100644 --- a/tests/ui/generator/print/generator-print-verbose-2.stderr +++ b/tests/ui/generator/print/generator-print-verbose-2.stderr @@ -12,6 +12,7 @@ LL | | }); | |_____^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `&'_#4r Cell` to implement `Send` note: required because it's used within this generator --> $DIR/generator-print-verbose-2.rs:19:17 @@ -36,6 +37,7 @@ LL | | }); | |_____^ generator is not `Sync` | = help: within `[main::{closure#0} upvar_tys=() {Cell, ()}]`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: generator is not `Sync` as this value is used across a yield --> $DIR/generator-print-verbose-2.rs:15:9 | diff --git a/tests/ui/issues/issue-7364.stderr b/tests/ui/issues/issue-7364.stderr index 5dc8c2b607e61..aee73380f15e7 100644 --- a/tests/ui/issues/issue-7364.stderr +++ b/tests/ui/issues/issue-7364.stderr @@ -5,6 +5,7 @@ LL | static boxed: Box> = Box::new(RefCell::new(0)); | ^^^^^^^^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Unique>` to implement `Sync` = note: required because it appears within the type `Box>` = note: shared static variables must have a type that implements `Sync` diff --git a/tests/ui/let-else/accidental-if.rs b/tests/ui/let-else/accidental-if.rs new file mode 100644 index 0000000000000..3fba630435c71 --- /dev/null +++ b/tests/ui/let-else/accidental-if.rs @@ -0,0 +1,6 @@ +fn main() { + let x = Some(123); + if let Some(y) = x else { //~ ERROR this `if` expression is missing a block + return; + }; +} diff --git a/tests/ui/let-else/accidental-if.stderr b/tests/ui/let-else/accidental-if.stderr new file mode 100644 index 0000000000000..5474a67aac45a --- /dev/null +++ b/tests/ui/let-else/accidental-if.stderr @@ -0,0 +1,19 @@ +error: this `if` expression is missing a block after the condition + --> $DIR/accidental-if.rs:3:5 + | +LL | if let Some(y) = x else { + | ^^ + | +help: add a block here + --> $DIR/accidental-if.rs:3:23 + | +LL | if let Some(y) = x else { + | ^ +help: remove the `if` if you meant to write a `let...else` statement + --> $DIR/accidental-if.rs:3:5 + | +LL | if let Some(y) = x else { + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed new file mode 100644 index 0000000000000..aa3bce2945b68 --- /dev/null +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +trait Greeter0 { + fn greet(&self); +} + +trait Greeter1 { + fn greet(&self); +} + +type BoxedGreeter<'a> = (Box, Box); +//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias + +struct FixedGreeter<'a>(pub &'a str); + +impl Greeter0 for FixedGreeter<'_> { + fn greet(&self) { + println!("0 {}", self.0) + } +} + +impl Greeter1 for FixedGreeter<'_> { + fn greet(&self) { + println!("1 {}", self.0) + } +} + +struct Greetings(pub Vec); + +impl Greetings { + pub fn get(&self, i: usize) -> BoxedGreeter { + (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i]))) + //~^ ERROR lifetime may not live long enough + } +} + +fn main() { + let mut g = Greetings {0 : vec!()}; + g.0.push("a".to_string()); + g.0.push("b".to_string()); + g.get(0).0.greet(); + g.get(0).1.greet(); + g.get(1).0.greet(); + g.get(1).1.greet(); +} diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs new file mode 100644 index 0000000000000..20c88ec69813a --- /dev/null +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs @@ -0,0 +1,45 @@ +// run-rustfix + +trait Greeter0 { + fn greet(&self); +} + +trait Greeter1 { + fn greet(&self); +} + +type BoxedGreeter = (Box, Box); +//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias + +struct FixedGreeter<'a>(pub &'a str); + +impl Greeter0 for FixedGreeter<'_> { + fn greet(&self) { + println!("0 {}", self.0) + } +} + +impl Greeter1 for FixedGreeter<'_> { + fn greet(&self) { + println!("1 {}", self.0) + } +} + +struct Greetings(pub Vec); + +impl Greetings { + pub fn get(&self, i: usize) -> BoxedGreeter { + (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i]))) + //~^ ERROR lifetime may not live long enough + } +} + +fn main() { + let mut g = Greetings {0 : vec!()}; + g.0.push("a".to_string()); + g.0.push("b".to_string()); + g.get(0).0.greet(); + g.get(0).1.greet(); + g.get(1).0.greet(); + g.get(1).1.greet(); +} diff --git a/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr new file mode 100644 index 0000000000000..808d8bb905885 --- /dev/null +++ b/tests/ui/lifetimes/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:32:9 + | +LL | pub fn get(&self, i: usize) -> BoxedGreeter { + | - let's call the lifetime of this reference `'1` +LL | (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i]))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias + | +LL | type BoxedGreeter<'a> = (Box, Box); + | ++++ ++++ ++++ + +error: aborting due to previous error + diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr index 498a112fa9bb3..f34ccecdd45e6 100644 --- a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr +++ b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr @@ -37,6 +37,11 @@ help: add a block here | LL | if let Some(n) = opt else { | ^ +help: remove the `if` if you meant to write a `let...else` statement + --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5 + | +LL | if let Some(n) = opt else { + | ^^ error: this `if` expression is missing a block after the condition --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5 diff --git a/tests/ui/stdlib-unit-tests/not-sync.stderr b/tests/ui/stdlib-unit-tests/not-sync.stderr index 1ee358ba8368e..4e34e10e37789 100644 --- a/tests/ui/stdlib-unit-tests/not-sync.stderr +++ b/tests/ui/stdlib-unit-tests/not-sync.stderr @@ -5,6 +5,7 @@ LL | test::>(); | ^^^^^^^^^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: required by a bound in `test` --> $DIR/not-sync.rs:5:12 | @@ -18,6 +19,7 @@ LL | test::>(); | ^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: required by a bound in `test` --> $DIR/not-sync.rs:5:12 | diff --git a/tests/ui/mutexguard-sync.rs b/tests/ui/sync/mutexguard-sync.rs similarity index 100% rename from tests/ui/mutexguard-sync.rs rename to tests/ui/sync/mutexguard-sync.rs diff --git a/tests/ui/mutexguard-sync.stderr b/tests/ui/sync/mutexguard-sync.stderr similarity index 83% rename from tests/ui/mutexguard-sync.stderr rename to tests/ui/sync/mutexguard-sync.stderr index 3fbb2ddf183d3..4dc5571196c14 100644 --- a/tests/ui/mutexguard-sync.stderr +++ b/tests/ui/sync/mutexguard-sync.stderr @@ -7,6 +7,7 @@ LL | test_sync(guard); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `MutexGuard<'_, Cell>` to implement `Sync` note: required by a bound in `test_sync` --> $DIR/mutexguard-sync.rs:5:17 diff --git a/tests/ui/sync/suggest-cell.rs b/tests/ui/sync/suggest-cell.rs new file mode 100644 index 0000000000000..3284eae7be181 --- /dev/null +++ b/tests/ui/sync/suggest-cell.rs @@ -0,0 +1,31 @@ +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `Cell<()>` cannot be shared between threads safely + //~| NOTE `Cell<()>` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead +} diff --git a/tests/ui/sync/suggest-cell.stderr b/tests/ui/sync/suggest-cell.stderr new file mode 100644 index 0000000000000..c232c1ccd53dd --- /dev/null +++ b/tests/ui/sync/suggest-cell.stderr @@ -0,0 +1,59 @@ +error[E0277]: `Cell<()>` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:12:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^ `Cell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:17:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:22:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:27:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/sync/suggest-once-cell.rs b/tests/ui/sync/suggest-once-cell.rs new file mode 100644 index 0000000000000..82fca45b1a47f --- /dev/null +++ b/tests/ui/sync/suggest-once-cell.rs @@ -0,0 +1,12 @@ +#![feature(once_cell)] + +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `OnceCell<()>` cannot be shared between threads safely + //~| NOTE `OnceCell<()>` cannot be shared between threads safely + //~| NOTE use `std::sync::OnceLock` instead +} diff --git a/tests/ui/sync/suggest-once-cell.stderr b/tests/ui/sync/suggest-once-cell.stderr new file mode 100644 index 0000000000000..fadf05374d8ca --- /dev/null +++ b/tests/ui/sync/suggest-once-cell.stderr @@ -0,0 +1,17 @@ +error[E0277]: `OnceCell<()>` cannot be shared between threads safely + --> $DIR/suggest-once-cell.rs:8:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ `OnceCell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `OnceCell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-once-cell.rs:3:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/sync/suggest-ref-cell.rs b/tests/ui/sync/suggest-ref-cell.rs new file mode 100644 index 0000000000000..6b972ae09622e --- /dev/null +++ b/tests/ui/sync/suggest-ref-cell.rs @@ -0,0 +1,12 @@ +#![feature(once_cell)] + +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `RefCell<()>` cannot be shared between threads safely + //~| NOTE `RefCell<()>` cannot be shared between threads safely + //~| NOTE use `std::sync::RwLock` instead +} diff --git a/tests/ui/sync/suggest-ref-cell.stderr b/tests/ui/sync/suggest-ref-cell.stderr new file mode 100644 index 0000000000000..9e8b8fcb42ed0 --- /dev/null +++ b/tests/ui/sync/suggest-ref-cell.stderr @@ -0,0 +1,17 @@ +error[E0277]: `RefCell<()>` cannot be shared between threads safely + --> $DIR/suggest-ref-cell.rs:8:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ `RefCell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `RefCell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-ref-cell.rs:3:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.rs b/tests/ui/type/type-check/coerce-result-return-value-2.rs new file mode 100644 index 0000000000000..23bafa6c5c94c --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.rs @@ -0,0 +1,24 @@ +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo4(x: Result<(), A>) -> Result<(), B> { + match true { + true => x, //~ ERROR mismatched types + false => x, + } +} +fn foo5(x: Result<(), A>) -> Result<(), B> { + match true { + true => return x, //~ ERROR mismatched types + false => return x, + } +} +fn main() { + let _ = foo4(Ok(())); + let _ = foo5(Ok(())); + let _: Result<(), B> = { //~ ERROR mismatched types + Err(A); + }; +} diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.stderr b/tests/ui/type/type-check/coerce-result-return-value-2.stderr new file mode 100644 index 0000000000000..5992162341e6e --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.stderr @@ -0,0 +1,47 @@ +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:8:17 + | +LL | fn foo4(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | true => Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:14:24 + | +LL | fn foo5(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => return x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | true => return Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:21:28 + | +LL | let _: Result<(), B> = { + | ____________________________^ +LL | | Err(A); +LL | | }; + | |_____^ expected enum `Result`, found `()` + | + = note: expected enum `Result<(), B>` + found unit type `()` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed new file mode 100644 index 0000000000000..8a05407070dad --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.fixed @@ -0,0 +1,24 @@ +// run-rustfix +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo1(x: Result<(), A>) -> Result<(), B> { + Ok(x?) //~ ERROR mismatched types +} +fn foo2(x: Result<(), A>) -> Result<(), B> { + return Ok(x?); //~ ERROR mismatched types +} +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + Ok(x?) //~ ERROR mismatched types + } else { + Ok(x?) //~ ERROR mismatched types + } +} +fn main() { + let _ = foo1(Ok(())); + let _ = foo2(Ok(())); + let _ = foo3(Ok(())); +} diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs new file mode 100644 index 0000000000000..442203addb787 --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.rs @@ -0,0 +1,24 @@ +// run-rustfix +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo1(x: Result<(), A>) -> Result<(), B> { + x //~ ERROR mismatched types +} +fn foo2(x: Result<(), A>) -> Result<(), B> { + return x; //~ ERROR mismatched types +} +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + x //~ ERROR mismatched types + } else { + x //~ ERROR mismatched types + } +} +fn main() { + let _ = foo1(Ok(())); + let _ = foo2(Ok(())); + let _ = foo3(Ok(())); +} diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr new file mode 100644 index 0000000000000..550153520782c --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.stderr @@ -0,0 +1,65 @@ +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:8:5 + | +LL | fn foo1(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | Ok(x?) + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:11:12 + | +LL | fn foo2(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | return x; + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | return Ok(x?); + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:15:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | if true { +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | Ok(x?) + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:17:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +... +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` + | +LL | Ok(x?) + | +++ ++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`.