diff options
| author | bors <bors@rust-lang.org> | 2022-07-13 17:13:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-07-13 17:13:27 +0000 |
| commit | c80dde43f992f3eb419899a34551b84c6301f8e8 (patch) | |
| tree | 8cdaab3defdcc525a68f7dba82c23e62be66fa9c /compiler | |
| parent | 42bd138126b0a9d38d65bc9973e72de3c5b6c37f (diff) | |
| parent | 3933b2b310e9358b77269e69b0656d12a126e14a (diff) | |
| download | rust-c80dde43f992f3eb419899a34551b84c6301f8e8.tar.gz rust-c80dde43f992f3eb419899a34551b84c6301f8e8.zip | |
Auto merge of #99210 - Dylan-DPC:rollup-879cp1t, r=Dylan-DPC
Rollup of 5 pull requests Successful merges: - #98574 (Lower let-else in MIR) - #99011 (`UnsafeCell` blocks niches inside its nested type from being available outside) - #99030 (diagnostics: error messages when struct literals fail to parse) - #99155 (Keep unstable target features for asm feature checking) - #99199 (Refactor: remove an unnecessary `span_to_snippet`) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
43 files changed, 480 insertions, 336 deletions
diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 9444fffc331..7cbfe143b4d 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,8 +1,8 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; -use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; +use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; use rustc_session::parse::feature_err; -use rustc_span::{sym, DesugaringKind}; +use rustc_span::sym; use smallvec::SmallVec; @@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); - match &local.kind { - LocalKind::InitElse(init, els) => { - let e = self.lower_let_else(hir_id, local, init, els, tail); - expr = Some(e); - // remaining statements are in let-else expression - break; - } - _ => { - let local = self.lower_local(local); - self.alias_attrs(hir_id, local.hir_id); - let kind = hir::StmtKind::Local(local); - let span = self.lower_span(s.span); - stmts.push(hir::Stmt { hir_id, kind, span }); - } - } + let local = self.lower_local(local); + self.alias_attrs(hir_id, local.hir_id); + let kind = hir::StmtKind::Local(local); + let span = self.lower_span(s.span); + stmts.push(hir::Stmt { hir_id, kind, span }); } StmtKind::Item(ref it) => { stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map( @@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); let pat = self.lower_pat(&l.pat); + let els = if let LocalKind::InitElse(_, els) = &l.kind { + if !self.tcx.features().let_else { + feature_err( + &self.tcx.sess.parse_sess, + sym::let_else, + l.span, + "`let...else` statements are unstable", + ) + .emit(); + } + Some(self.lower_block(els, false)) + } else { + None + }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source }) + self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { @@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - - fn lower_let_else( - &mut self, - stmt_hir_id: hir::HirId, - local: &Local, - init: &Expr, - els: &Block, - tail: &[Stmt], - ) -> &'hir hir::Expr<'hir> { - let ty = local - .ty - .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); - let span = self.lower_span(local.span); - let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); - let init = self.lower_expr(init); - let local_hir_id = self.lower_node_id(local.id); - self.lower_attrs(local_hir_id, &local.attrs); - let let_expr = { - let lex = self.arena.alloc(hir::Let { - hir_id: local_hir_id, - pat: self.lower_pat(&local.pat), - ty, - init, - span, - }); - self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new())) - }; - let then_expr = { - let (stmts, expr) = self.lower_stmts(tail); - let block = self.block_all(span, stmts, expr); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - let else_expr = { - let block = self.lower_block(els, false); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - self.alias_attrs(let_expr.hir_id, local_hir_id); - self.alias_attrs(else_expr.hir_id, local_hir_id); - let if_expr = self.arena.alloc(hir::Expr { - hir_id: stmt_hir_id, - span, - kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), - }); - if !self.tcx.features().let_else { - feature_err( - &self.tcx.sess.parse_sess, - sym::let_else, - local.span, - "`let...else` statements are unstable", - ) - .emit(); - } - if_expr - } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fdf60e60914..4da3096f7c3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2146,7 +2146,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } - let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None }; + let local = hir::Local { + hir_id, + init, + pat, + els: None, + source, + span: self.lower_span(span), + ty: None, + }; self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local))) } diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index ce7c0eb72cd..6673d75d99d 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -856,7 +856,6 @@ pub enum ReprAttr { ReprSimd, ReprTransparent, ReprAlign(u32), - ReprNoNiche, } #[derive(Eq, PartialEq, Debug, Copy, Clone)] @@ -904,7 +903,6 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { sym::packed => Some(ReprPacked(1)), sym::simd => Some(ReprSimd), sym::transparent => Some(ReprTransparent), - sym::no_niche => Some(ReprNoNiche), sym::align => { let mut err = struct_span_err!( diagnostic, @@ -943,7 +941,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { Ok(literal) => acc.push(ReprPacked(literal)), Err(message) => literal_error = Some(message), }; - } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche) + } else if matches!(name, sym::C | sym::simd | sym::transparent) || int_type_of_word(name).is_some() { recognised = true; @@ -1001,7 +999,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { } else { if matches!( meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche + sym::C | sym::simd | sym::transparent ) || int_type_of_word(meta_item.name_or_empty()).is_some() { recognised = true; @@ -1039,7 +1037,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { .emit(); } else if matches!( meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche + sym::C | sym::simd | sym::transparent ) || int_type_of_word(meta_item.name_or_empty()).is_some() { recognised = true; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 687ff0fb505..0ceb63477c8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1598,21 +1598,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let return_ty = tcx.erase_regions(return_ty); // to avoid panics - if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) { - if self + if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) + && self .infcx .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env) .must_apply_modulo_regions() - { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { - err.span_suggestion_hidden( - return_span, - "use `.collect()` to allocate the iterator", - format!("{snippet}.collect::<Vec<_>>()"), - Applicability::MaybeIncorrect, - ); - } - } + { + err.span_suggestion_hidden( + return_span.shrink_to_hi(), + "use `.collect()` to allocate the iterator", + ".collect::<Vec<_>>()", + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index be2d3108c5f..3ed3453c6c7 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -167,7 +167,7 @@ impl CodegenBackend for CraneliftCodegenBackend { } } - fn target_features(&self, _sess: &Session) -> Vec<rustc_span::Symbol> { + fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> { vec![] } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 399830de84c..c21e0c5a35b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -140,8 +140,8 @@ impl CodegenBackend for GccCodegenBackend { ) } - fn target_features(&self, sess: &Session) -> Vec<Symbol> { - target_features(sess) + fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features(sess, allow_unstable) } } @@ -298,12 +298,12 @@ pub fn target_cpu(sess: &Session) -> &str { } } -pub fn target_features(sess: &Session) -> Vec<Symbol> { +pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { supported_target_features(sess) .iter() .filter_map( |&(feature, gate)| { - if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } + if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None } }, ) .filter(|_feature| { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a7dd8e16d28..fb196ee9f5d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -324,8 +324,8 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::print_version(); } - fn target_features(&self, sess: &Session) -> Vec<Symbol> { - target_features(sess) + fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features(sess, allow_unstable) } fn codegen_crate<'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index ce6c6e3215c..5b3b7db12b7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -233,26 +233,29 @@ pub fn check_tied_features( // Used to generate cfg variables and apply features // Must express features in the way Rust understands them -pub fn target_features(sess: &Session) -> Vec<Symbol> { +pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { let target_machine = create_informational_target_machine(sess); - let mut features: Vec<Symbol> = - supported_target_features(sess) - .iter() - .filter_map(|&(feature, gate)| { - if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } - }) - .filter(|feature| { - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { - return false; - } + let mut features: Vec<Symbol> = supported_target_features(sess) + .iter() + .filter_map(|&(feature, gate)| { + if sess.is_nightly_build() || allow_unstable || gate.is_none() { + Some(feature) + } else { + None + } + }) + .filter(|feature| { + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { + return false; } - true - }) - .map(|feature| Symbol::intern(feature)) - .collect(); + } + true + }) + .map(|feature| Symbol::intern(feature)) + .collect(); // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64 // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 1e53c73d1bb..779bd3ea278 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -59,7 +59,7 @@ impl<'tcx, T> Backend<'tcx> for T where pub trait CodegenBackend { fn init(&self, _sess: &Session) {} fn print(&self, _req: PrintRequest, _sess: &Session) {} - fn target_features(&self, _sess: &Session) -> Vec<Symbol> { + fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } fn print_passes(&self) {} diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 9dfdafcb38e..7616e7a63d1 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -217,7 +217,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } if let Some(def) = mplace.layout.ty.ty_adt_def() { - if Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() { + if def.is_unsafe_cell() { // We are crossing over an `UnsafeCell`, we can mutate again. This means that // References we encounter inside here are interned as pointing to mutable // allocations. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5114ce5d452..53bc2cc8a69 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -821,7 +821,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Special check preventing `UnsafeCell` in the inner part of constants if let Some(def) = op.layout.ty.ty_adt_def() { if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) - && Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() + && def.is_unsafe_cell() { throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 9941afe5a12..29464cf8c4e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -96,13 +96,13 @@ impl Qualif for HasMutInterior { } fn in_adt_inherently<'tcx>( - cx: &ConstCx<'_, 'tcx>, + _cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>, _: SubstsRef<'tcx>, ) -> bool { // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently. // It arises structurally for all other types. - Some(adt.did()) == cx.tcx.lang_items().unsafe_cell_type() + adt.is_unsafe_cell() } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index b54f0ef361a..117bdad971a 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -156,9 +156,6 @@ declare_features! ( (active, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. (active, lang_items, "1.0.0", None, None), - /// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`, - /// it is not on path for eventual stabilization). - (active, no_niche, "1.42.0", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 04f585df34c..ed874ae829b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1316,6 +1316,8 @@ pub struct Local<'hir> { pub ty: Option<&'hir Ty<'hir>>, /// Initializer expression to set the value, if any. pub init: Option<&'hir Expr<'hir>>, + /// Else block for a `let...else` binding. + pub els: Option<&'hir Block<'hir>>, pub hir_id: HirId, pub span: Span, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 531d9f14040..b5d9769c578 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { walk_list!(visitor, visit_expr, &local.init); visitor.visit_id(local.hir_id); visitor.visit_pat(&local.pat); + if let Some(els) = local.els { + visitor.visit_block(els); + } walk_list!(visitor, visit_ty, &local.ty); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 50acb0270b0..e3c97ec357e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -883,7 +883,12 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::SubItem(ii.hir_id())) } - pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { + pub fn print_local( + &mut self, + init: Option<&hir::Expr<'_>>, + els: Option<&hir::Block<'_>>, + decl: impl Fn(&mut Self), + ) { self.space_if_not_bol(); self.ibox(INDENT_UNIT); self.word_nbsp("let"); @@ -897,6 +902,13 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(init); } + + if let Some(els) = els { + self.nbsp(); + self.word_space("else"); + self.print_block(els); + } + self.end() } @@ -904,7 +916,7 @@ impl<'a> State<'a> { self.maybe_print_comment(st.span.lo()); match st.kind { hir::StmtKind::Local(loc) => { - self.print_local(loc.init, |this| this.print_local_decl(loc)); + self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc)); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), hir::StmtKind::Expr(expr) => { @@ -1404,7 +1416,7 @@ impl<'a> State<'a> { // Print `let _t = $init;`: let temp = Ident::from_str("_t"); - self.print_local(Some(init), |this| this.print_ident(temp)); + self.print_local(Some(init), None, |this| this.print_ident(temp)); self.word(";"); // Print `_t`: diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 8796ad5a33c..f4b51b5a442 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -48,7 +48,10 @@ pub fn add_configuration( ) { let tf = sym::target_feature; - let target_features = codegen_backend.target_features(sess); + let unstable_target_features = codegen_backend.target_features(sess, true); + sess.unstable_target_features.extend(unstable_target_features.iter().cloned()); + + let target_features = codegen_backend.target_features(sess, false); sess.target_features.extend(target_features.iter().cloned()); cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index be4843c7ff1..aca481df2e1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -703,9 +703,8 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi return true; } - // Types with a `#[repr(no_niche)]` attribute have their niche hidden. - // The attribute is used by the UnsafeCell for example (the only use so far). - if def.repr().hide_niche() { + // `UnsafeCell` has its niche hidden. + if def.is_unsafe_cell() { return false; } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3fe6394ad7e..3e5f6bb8f0b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> { /// `let pat: ty = <INIT>` initializer: Option<ExprId>, + /// `let pat: ty = <INIT> else { <ELSE> } + else_block: Option<Block>, + /// The lint level for this `let` statement. lint_level: LintLevel, }, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c8d09875c28..97249fdd175 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm init_scope: _, ref pattern, lint_level: _, + else_block, } => { if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } visitor.visit_pat(pattern); + if let Some(block) = else_block { + visitor.visit_block(block) + } } } } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 4cac7670735..809406aff18 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -52,6 +52,8 @@ bitflags! { /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. /// (i.e., this flag is never set unless this ADT is an enum). const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + /// Indicates whether the type is `UnsafeCell`. + const IS_UNSAFE_CELL = 1 << 9; } } @@ -247,6 +249,9 @@ impl AdtDefData { if Some(did) == tcx.lang_items().manually_drop() { flags |= AdtFlags::IS_MANUALLY_DROP; } + if Some(did) == tcx.lang_items().unsafe_cell_type() { + flags |= AdtFlags::IS_UNSAFE_CELL; + } AdtDefData { did, variants, flags, repr } } @@ -333,6 +338,12 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_BOX) } + /// Returns `true` if this is UnsafeCell<T>. + #[inline] + pub fn is_unsafe_cell(self) -> bool { + self.flags().contains(AdtFlags::IS_UNSAFE_CELL) + } + /// Returns `true` if this is `ManuallyDrop<T>`. #[inline] pub fn is_manually_drop(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f87b6e4212d..1ed41db099c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -542,14 +542,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { debug!("univariant offset: {:?} field: {:#?}", offset, field); offsets[i as usize] = offset; - if !repr.hide_niche() { - if let Some(mut niche) = field.largest_niche { - let available = niche.available(dl); - if available > largest_niche_available { - largest_niche_available = available; - niche.offset += offset; - largest_niche = Some(niche); - } + if let Some(mut niche) = field.largest_niche { + let available = niche.available(dl); + if available > largest_niche_available { + largest_niche_available = available; + niche.offset += offset; + largest_niche = Some(niche); } } @@ -1078,6 +1076,29 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr(), kind)?; st.variants = Variants::Single { index: v }; + + if def.is_unsafe_cell() { + let hide_niches = |scalar: &mut _| match scalar { + Scalar::Initialized { value, valid_range } => { + *valid_range = WrappingRange::full(value.size(dl)) + } + // Already doesn't have any niches + Scalar::Union { .. } => {} + }; + match &mut st.abi { + Abi::Uninhabited => {} + Abi::Scalar(scalar) => hide_niches(scalar), + Abi::ScalarPair(a, b) => { + hide_niches(a); + hide_niches(b); + } + Abi::Vector { element, count: _ } => hide_niches(element), + Abi::Aggregate { sized: _ } => {} + } + st.largest_niche = None; + return Ok(tcx.intern_layout(st)); + } + let (start, end) = self.tcx.layout_scalar_valid_range(def.did()); match st.abi { Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { @@ -1106,11 +1127,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } // Update `largest_niche` if we have introduced a larger niche. - let niche = if def.repr().hide_niche() { - None - } else { - Niche::from_scalar(dl, Size::ZERO, *scalar) - }; + let niche = Niche::from_scalar(dl, Size::ZERO, *scalar); if let Some(niche) = niche { match st.largest_niche { Some(largest_niche) => { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f15108fb750..c2c7b3df844 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1720,11 +1720,9 @@ bitflags! { const IS_TRANSPARENT = 1 << 2; // Internal only for now. If true, don't reorder fields. const IS_LINEAR = 1 << 3; - // If true, don't expose any niche to type's context. - const HIDE_NICHE = 1 << 4; // If true, the type's layout can be randomized using // the seed stored in `ReprOptions.layout_seed` - const RANDOMIZE_LAYOUT = 1 << 5; + const RANDOMIZE_LAYOUT = 1 << 4; // Any of these flags being set prevent field reordering optimisation. const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | ReprFlags::IS_SIMD.bits @@ -1781,7 +1779,6 @@ impl ReprOptions { ReprFlags::empty() } attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, - attr::ReprNoNiche => ReprFlags::HIDE_NICHE, attr::ReprSimd => ReprFlags::IS_SIMD, attr::ReprInt(i) => { size = Some(i); @@ -1834,11 +1831,6 @@ impl ReprOptions { self.flags.contains(ReprFlags::IS_LINEAR) } - #[inline] - pub fn hide_niche(&self) -> bool { - self.flags.contains(ReprFlags::HIDE_NICHE) - } - /// Returns the discriminant type, given these `repr` options. /// This must only be called on enums! pub fn discr_type(&self) -> attr::IntType { diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index a83328c0cab..cb8be51a085 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref pattern, initializer, lint_level, + else_block, } => { let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let scope = (*init_scope, source_info); this.in_scope(scope, *lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern.clone(), init) + if let Some(else_block) = else_block { + this.ast_let_else( + block, + init, + initializer_span, + else_block, + visibility_scope, + remainder_span, + pattern, + ) + } else { + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern + } }) - } + }, ) - ); + ) } else { let scope = (*init_scope, source_info); unpack!(this.in_scope(scope, *lint_level, |this| { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 1628f1a4b85..7067a48b783 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // those N possible outcomes, create a (initially empty) // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair); let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![]; target_candidates.resize_with(test.targets(), Default::default); @@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // at least the first candidate ought to be tested assert!(total_candidate_count > candidates.len()); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); + debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("test_candidates: untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("declare_binding: vars={:?}", locals); self.var_indices.insert(var_id, locals); } + + pub(crate) fn ast_let_else( + &mut self, + mut block: BasicBlock, + init: &Expr<'tcx>, + initializer_span: Span, + else_block: &Block, + visibility_scope: Option<SourceScope>, + remainder_span: Span, + pattern: &Pat<'tcx>, + ) -> BlockAnd<()> { + let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); + let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; + let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); + self.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); + let fake_borrow_temps = self.lower_match_tree( + block, + initializer_span, + pattern.span, + false, + &mut [&mut candidate, &mut wildcard], + ); + // This block is for the matching case + let matching = self.bind_pattern( + self.source_info(pattern.span), + candidate, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This block is for the failure case + let failure = self.bind_pattern( + self.source_info(else_block.span), + wildcard, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This place is not really used because this destination place + // should never be used to take values at the end of the failure + // block. + let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() }; + let failure_block; + unpack!( + failure_block = self.ast_block( + dummy_place, + failure, + else_block, + self.source_info(else_block.span), + ) + ); + self.cfg.terminate( + failure_block, + self.source_info(else_block.span), + TerminatorKind::Unreachable, + ); + matching.unit() + } } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 59750d5d0b8..dccaa61ed89 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> { )), }; + let else_block = local.els.map(|els| self.mirror_block(els)); + let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); @@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> { }, pattern, initializer: local.init.map(|init| self.mirror_expr(init)), + else_block, lint_level: LintLevel::Explicit(local.hir_id), }, opt_destruction_scope: opt_dxn_ext, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 75fd156ebfd..5bd1fad0bcb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; +use rustc_span::{BytePos, Span}; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -77,6 +77,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) { intravisit::walk_local(self, loc); + let els = loc.els; + if let Some(init) = loc.init && els.is_some() { + self.check_let(&loc.pat, init, loc.span); + } let (msg, sp) = match loc.source { hir::LocalSource::Normal => ("local binding", Some(loc.span)), @@ -84,7 +88,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { hir::LocalSource::AwaitDesugar => ("`await` future binding", None), hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; - self.check_irrefutable(&loc.pat, msg, sp); + if els.is_none() { + self.check_irrefutable(&loc.pat, msg, sp); + } } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -1125,17 +1131,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L }) if Some(*hir_id) == pat_id => { return LetSource::IfLetGuard; } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => { - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind { - return LetSource::LetElse(expn_data.call_site); - } - } _ => {} } let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); + if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = + parent_parent_node + { + return LetSource::LetElse(*span); + } let parent_parent_parent = hir.get_parent_node(parent_parent); let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index efa45883eab..c48aa9a90ef 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>( let field_needs_drop_and_init = |(f, f_ty, mpi)| { let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f)); let Some(mpi) = child else { - return f_ty.needs_drop(tcx, param_env); + return Ty::needs_drop(f_ty, tcx, param_env); }; is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f9387e29262..0d9c57908c1 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3028,6 +3028,11 @@ impl<'a> Parser<'a> { } }; + let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand); + // A shorthand field can be turned into a full field with `:`. + // We should point this out. + self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon)); + match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) { Ok(_) => { if let Some(f) = parsed_field.or(recovery_field) { @@ -3048,6 +3053,19 @@ impl<'a> Parser<'a> { ",", Applicability::MachineApplicable, ); + } else if is_shorthand + && (AssocOp::from_token(&self.token).is_some() + || matches!(&self.token.kind, token::OpenDelim(_)) + || self.token.kind == token::Dot) + { + // Looks like they tried to write a shorthand, complex expression. + let ident = parsed_field.expect("is_shorthand implies Some").ident; + e.span_suggestion( + ident.span.shrink_to_lo(), + "try naming a field", + &format!("{ident}: "), + Applicability::HasPlaceholders, + ); } } if !recover { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0723c68a77..20d03d797fe 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1808,21 +1808,6 @@ impl CheckAttrVisitor<'_> { _ => ("a", "struct, enum, or union"), } } - sym::no_niche => { - if !self.tcx.features().enabled(sym::no_niche) { - feature_err( - &self.tcx.sess.parse_sess, - sym::no_niche, - hint.span(), - "the attribute `repr(no_niche)` is currently unstable", - ) - .emit(); - } - match target { - Target::Struct | Target::Enum => continue, - _ => ("a", "struct or enum"), - } - } sym::i8 | sym::u8 | sym::i16 @@ -1870,10 +1855,8 @@ impl CheckAttrVisitor<'_> { // This is not ideal, but tracking precisely which ones are at fault is a huge hassle. let hint_spans = hints.iter().map(|hint| hint.span()); - // Error on repr(transparent, <anything else apart from no_niche>). - let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche; - let non_no_niche_count = hints.iter().filter(non_no_niche).count(); - if is_transparent && non_no_niche_count > 1 { + // Error on repr(transparent, <anything else>). + if is_transparent && hints.len() > 1 { let hint_spans: Vec<_> = hint_spans.clone().collect(); struct_span_err!( self.tcx.sess, diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 0070c0699a4..eed3e1579a2 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> { pats.extend(inner_pat.iter()); } Struct(_, fields, _) => { - let (short, not_short): (Vec<&_>, Vec<&_>) = + let (short, not_short): (Vec<_>, _) = fields.iter().partition(|f| f.is_shorthand); shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id)); pats.extend(not_short.iter().map(|f| f.pat)); @@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> { } } - return shorthand_field_ids; + shorthand_field_ids } fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { @@ -368,6 +368,9 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.add_from_pat(&local.pat); + if local.els.is_some() { + self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); + } intravisit::walk_local(self, local); } @@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // initialization, which is mildly more complex than checking // once at the func header but otherwise equivalent. - let succ = self.propagate_through_opt_expr(local.init, succ); - self.define_bindings_in_pat(&local.pat, succ) + if let Some(els) = local.els { + // Eventually, `let pat: ty = init else { els };` is mostly equivalent to + // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };` + // except that extended lifetime applies at the `init` location. + // + // (e) + // | + // v + // (expr) + // / \ + // | | + // v v + // bindings els + // | + // v + // ( succ ) + // + if let Some(init) = local.init { + let else_ln = self.propagate_through_block(els, succ); + let ln = self.live_node(local.hir_id, local.span); + self.init_from_succ(ln, succ); + self.merge_from_succ(ln, else_ln); + let succ = self.propagate_through_expr(init, ln); + self.define_bindings_in_pat(&local.pat, succ) + } else { + span_bug!( + stmt.span, + "variable is uninitialized but an unexpected else branch is found" + ) + } + } else { + let succ = self.propagate_through_opt_expr(local.init, succ); + self.define_bindings_in_pat(&local.pat, succ) + } } hir::StmtKind::Item(..) => succ, hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { @@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // (rvalue) || (rvalue) // | || | // v || v - // (write of place) || (place components) + // (write of place) || (place components) // | || | // v || v // (succ) || (succ) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2851b08cd93..a820f700869 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -508,7 +508,7 @@ impl<'a> Resolver<'a> { E0401, "can't use generic parameters from outer function", ); - err.span_label(span, "use of generic parameter from outer function".to_string()); + err.span_label(span, "use of generic parameter from outer function"); let sm = self.session.source_map(); match outer_res { @@ -990,7 +990,7 @@ impl<'a> Resolver<'a> { E0735, "generic parameters cannot use `Self` in their defaults" ); - err.span_label(span, "`Self` in generic parameter default".to_string()); + err.span_label(span, "`Self` in generic parameter default"); err } ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 6eb2f2d929d..a4175f4c5f3 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> { pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); let dumper = Dumper::new(save_ctxt.config.clone()); - DumpVisitor { - tcx: save_ctxt.tcx, - save_ctxt, - dumper, - span: span_utils, - // mac_defs: FxHashSet::default(), - // macro_calls: FxHashSet::default(), - } + DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils } } pub fn analysis(&self) -> &rls_data::Analysis { @@ -1425,9 +1418,10 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.process_macro_use(l.span); self.process_var_decl(&l.pat); - // Just walk the initializer and type (don't want to walk the pattern again). + // Just walk the initializer, the else branch and type (don't want to walk the pattern again). walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); + walk_list!(self, visit_block, l.els); } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1cccef2f64f..16f4a099d80 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -194,6 +194,9 @@ pub struct Session { /// Set of enabled features for the current target. pub target_features: FxHashSet<Symbol>, + + /// Set of enabled features for the current target, including unstable ones. + pub unstable_target_features: FxHashSet<Symbol>, } pub struct PerfStats { @@ -1390,6 +1393,7 @@ pub fn build_session( miri_unleashed_features: Lock::new(Default::default()), asm_arch, target_features: FxHashSet::default(), + unstable_target_features: FxHashSet::default(), }; validate_commandline_args_with_session_available(&sess); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3df4dfb74b3..29879c48b04 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1141,7 +1141,6 @@ pub enum DesugaringKind { Async, Await, ForLoop, - LetElse, WhileLoop, } @@ -1157,7 +1156,6 @@ impl DesugaringKind { DesugaringKind::YeetExpr => "`do yeet` expression", DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop => "`for` loop", - DesugaringKind::LetElse => "`let...else`", DesugaringKind::WhileLoop => "`while` loop", } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9b6967621f1..99912b491cb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -980,7 +980,6 @@ symbols! { no_link, no_main, no_mangle, - no_niche, no_sanitize, no_stack_check, no_start, @@ -1153,7 +1152,6 @@ symbols! { repr128, repr_align, repr_align_enum, - repr_no_niche, repr_packed, repr_simd, repr_transparent, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 33585de6001..b08fc482186 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1311,7 +1311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { visitor.visit_body(&body); let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); - let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; }; + let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor .returns diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1d3608048f2..30b76b922c7 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -997,26 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce(self, &self.misc(sp), then_expr, then_ty); if let Some(else_expr) = opt_else_expr { - let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) { - // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)` - // for errors that point to the offending expression rather than the entire block. - // We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no - // way to detect that the expected type originated from let-else and provide - // a customized error. - let else_ty = self.check_expr(else_expr); - let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse); - - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - self.tcx.ty_error() - } else { - else_ty - } - } else { - self.check_expr_with_expectation(else_expr, expected) - }; + let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a7c7089234a..60ee2233ed9 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1215,6 +1215,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); + + if let Some(blk) = decl.els { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_block_with_expected(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Some(mut err) = + self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); + } + self.diverges.set(previous_diverges); + } } /// Type check a `let` statement. @@ -1236,8 +1248,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_has_errors = self.has_errors.replace(false); match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); + hir::StmtKind::Local(l) => { + self.check_decl_local(l); } // Ignore for now. hir::StmtKind::Item(_) => {} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 576dc6f127c..8f34a970f6f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -16,19 +16,20 @@ pub(super) struct Declaration<'a> { pub ty: Option<&'a hir::Ty<'a>>, pub span: Span, pub init: Option<&'a hir::Expr<'a>>, + pub els: Option<&'a hir::Block<'a>>, } impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, .. } = *local; - Declaration { hir_id, pat, ty, span, init } + let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; + Declaration { hir_id, pat, ty, span, init, els } } } impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { fn from(let_expr: &'a hir::Let<'a>) -> Self { let hir::Let { hir_id, pat, ty, span, init } = *let_expr; - Declaration { hir_id, pat, ty, span, init: Some(init) } + Declaration { hir_id, pat, ty, span, init: Some(init), els: None } } } @@ -101,7 +102,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.declare(local.into()); - intravisit::walk_local(self, local); + intravisit::walk_local(self, local) } fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) { diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 9f1368a3e07..a1a92c62ad2 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -460,6 +460,7 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, + els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -537,13 +538,18 @@ fn resolve_local<'tcx>( } } - // Make sure we visit the initializer first, so expr_and_pat_count remains correct + // Make sure we visit the initializer first, so expr_and_pat_count remains correct. + // The correct order, as shared between generator_interior, drop_ranges and intravisitor, + // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. if let Some(expr) = init { visitor.visit_expr(expr); } if let Some(pat) = pat { visitor.visit_pat(pat); } + if let Some(els) = els { + visitor.visit_block(els); + } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value)); + resolve_local(self, None, Some(&body.value), None); } if body.generator_kind.is_some() { @@ -791,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init); + resolve_local(self, Some(&l.pat), l.init, l.els) } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 44b9c8392f8..9795be1a912 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -3196,7 +3196,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { /// Computes the set of target features used in a function for the purposes of /// inline assembly. fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> { - let mut target_features = tcx.sess.target_features.clone(); + let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); target_features.extend(&attrs.target_features); diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 920e3d578c8..9d7420acd26 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -2,7 +2,10 @@ //! normal visitor, which just walks the entire body in one shot, the //! `ExprUseVisitor` determines how expressions are being used. +use std::slice::from_ref; + use hir::def::DefKind; +use hir::Expr; // Export these here so that Clippy can use them. pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; @@ -252,96 +255,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } hir::ExprKind::Let(hir::Let { pat, init, .. }) => { - self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow)); + self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(discr)); - - // Matching should not always be considered a use of the place, hence - // discr does not necessarily need to be borrowed. - // We only want to borrow discr if the pattern contain something other - // than wildcards. - let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; - let mut needs_to_be_read = false; - for arm in arms.iter() { - return_if_err!(mc.cat_pattern(discr_place.clone(), arm.pat, |place, pat| { - match &pat.kind { - PatKind::Binding(.., opt_sub_pat) => { - // If the opt_sub_pat is None, than the binding does not count as - // a wildcard for the purpose of borrowing discr. - if opt_sub_pat.is_none() { - needs_to_be_read = true; - } - } - PatKind::Path(qpath) => { - // A `Path` pattern is just a name like `Foo`. This is either a - // named constant or else it refers to an ADT variant - - let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); - match res { - Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { - // Named constants have to be equated with the value - // being matched, so that's a read of the value being matched. - // - // FIXME: We don't actually reads for ZSTs. - needs_to_be_read = true; - } - _ => { - // Otherwise, this is a struct/enum variant, and so it's - // only a read if we need to read the discriminant. - needs_to_be_read |= is_multivariant_adt(place.place.ty()); - } - } - } - PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { - // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching - // against a multivariant enum or struct. In that case, we have to read - // the discriminant. Otherwise this kind of pattern doesn't actually - // read anything (we'll get invoked for the `...`, which may indeed - // perform some reads). - - let place_ty = place.place.ty(); - needs_to_be_read |= is_multivariant_adt(place_ty); - } - PatKind::Lit(_) | PatKind::Range(..) => { - // If the PatKind is a Lit or a Range then we want - // to borrow discr. - needs_to_be_read = true; - } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Slice(..) - | PatKind::Ref(..) - | PatKind::Wild => { - // If the PatKind is Or, Box, Slice or Ref, the decision is made later - // as these patterns contains subpatterns - // If the PatKind is Wild, the decision is made based on the other patterns being - // examined - } - } - })); - } - - if needs_to_be_read { - self.borrow_expr(discr, ty::ImmBorrow); - } else { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), - _ => None, - }; - - self.delegate.fake_read( - &discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); - - // We always want to walk the discriminant. We want to make sure, for instance, - // that the discriminant has been initialized. - self.walk_expr(discr); - } + self.maybe_read_scrutinee( + discr, + discr_place.clone(), + arms.iter().map(|arm| arm.pat), + ); // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -453,8 +376,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => { - self.walk_local(expr, pat, |_| {}); + hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => { + self.walk_local(expr, pat, *els, |_| {}) } hir::StmtKind::Local(_) => {} @@ -470,13 +393,114 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - fn walk_local<F>(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F) - where + fn maybe_read_scrutinee<'t>( + &mut self, + discr: &Expr<'_>, + discr_place: PlaceWithHirId<'tcx>, + pats: impl Iterator<Item = &'t hir::Pat<'t>>, + ) { + // Matching should not always be considered a use of the place, hence + // discr does not necessarily need to be borrowed. + // We only want to borrow discr if the pattern contain something other + // than wildcards. + let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; + let mut needs_to_be_read = false; + for pat in pats { + return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { + match &pat.kind { + PatKind::Binding(.., opt_sub_pat) => { + // If the opt_sub_pat is None, than the binding does not count as + // a wildcard for the purpose of borrowing discr. + if opt_sub_pat.is_none() { + needs_to_be_read = true; + } + } + PatKind::Path(qpath) => { + // A `Path` pattern is just a name like `Foo`. This is either a + // named constant or else it refers to an ADT variant + + let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); + match res { + Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + // Named constants have to be equated with the value + // being matched, so that's a read of the value being matched. + // + // FIXME: We don't actually reads for ZSTs. + needs_to_be_read = true; + } + _ => { + // Otherwise, this is a struct/enum variant, and so it's + // only a read if we need to read the discriminant. + needs_to_be_read |= is_multivariant_adt(place.place.ty()); + } + } + } + PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { + // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching + // against a multivariant enum or struct. In that case, we have to read + // the discriminant. Otherwise this kind of pattern doesn't actually + // read anything (we'll get invoked for the `...`, which may indeed + // perform some reads). + + let place_ty = place.place.ty(); + needs_to_be_read |= is_multivariant_adt(place_ty); + } + PatKind::Lit(_) | PatKind::Range(..) => { + // If the PatKind is a Lit or a Range then we want + // to borrow discr. + needs_to_be_read = true; + } + PatKind::Or(_) + | PatKind::Box(_) + | PatKind::Slice(..) + | PatKind::Ref(..) + | PatKind::Wild => { + // If the PatKind is Or, Box, Slice or Ref, the decision is made later + // as these patterns contains subpatterns + // If the PatKind is Wild, the decision is made based on the other patterns being + // examined + } + } + })); + } + + if needs_to_be_read { + self.borrow_expr(discr, ty::ImmBorrow); + } else { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + &discr_place, + FakeReadCause::ForMatchedPlace(closure_def_id), + discr_place.hir_id, + ); + + // We always want to walk the discriminant. We want to make sure, for instance, + // that the discriminant has been initialized. + self.walk_expr(discr); + } + } + + fn walk_local<F>( + &mut self, + expr: &hir::Expr<'_>, + pat: &hir::Pat<'_>, + els: Option<&hir::Block<'_>>, + mut f: F, + ) where F: FnMut(&mut Self), { self.walk_expr(expr); let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); + if let Some(els) = els { + // borrowing because we need to test the descriminant + self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); + self.walk_block(els) + } self.walk_irrefutable_pat(&expr_place, &pat); } @@ -667,7 +691,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); + debug!("walk_pat: binding place={:?} pat={:?}", place, pat); if let Some(bm) = mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) { |
