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
This commit is contained in:
commit
c80dde43f9
72 changed files with 830 additions and 830 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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![]
|
||||
}
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`" });
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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`:
|
||||
|
|
|
@ -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))));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 } => {
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(_) => {}
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue