From 351d532a27c56f7af82feaa7d612c412fd5c221c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 28 Oct 2023 15:39:54 +0200 Subject: [PATCH 01/10] interpret: call caller_location logic the same way codegen does, and share some code --- .../rustc_codegen_cranelift/src/common.rs | 11 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 9 +- .../src/const_eval/eval_queries.rs | 2 +- .../src/const_eval/machine.rs | 19 +++ .../rustc_const_eval/src/const_eval/mod.rs | 18 +-- .../src/interpret/eval_context.rs | 62 ++++++++ .../src/interpret/intrinsics.rs | 8 +- .../interpret/intrinsics/caller_location.rs | 136 ------------------ compiler/rustc_const_eval/src/lib.rs | 2 +- .../src/util/caller_location.rs | 63 ++++++++ compiler/rustc_const_eval/src/util/mod.rs | 1 + compiler/rustc_middle/src/mir/consts.rs | 18 +++ 12 files changed, 173 insertions(+), 176 deletions(-) delete mode 100644 compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs create mode 100644 compiler/rustc_const_eval/src/util/caller_location.rs diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 9771f44f62cfb..38dba016c23e1 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -433,16 +433,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { // Note: must be kept in sync with get_caller_location from cg_ssa pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> { let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| { - use rustc_session::RemapFileNameExt; - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = fx.tcx.const_caller_location(( - rustc_span::symbol::Symbol::intern( - &caller.file.name.for_codegen(&fx.tcx.sess).to_string_lossy(), - ), - caller.line as u32, - caller.col_display as u32 + 1, - )); + let const_loc = fx.tcx.span_as_caller_location(span); crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty()) }; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index caade768795f6..cbe3c73689679 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1454,14 +1454,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let tcx = bx.tcx(); let mut span_to_caller_location = |span: Span| { - use rustc_session::RemapFileNameExt; - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = tcx.const_caller_location(( - Symbol::intern(&caller.file.name.for_codegen(self.cx.sess()).to_string_lossy()), - caller.line as u32, - caller.col_display as u32 + 1, - )); + let const_loc = tcx.span_as_caller_location(span); OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) }; diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 46e00d0f176b4..669838308a558 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -90,7 +90,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( /// that inform us about the generic bounds of the constant. E.g., using an associated constant /// of a function's generic parameter will require knowledge about the bounds on the generic /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. -pub(super) fn mk_eval_cx<'mir, 'tcx>( +pub(crate) fn mk_eval_cx<'mir, 'tcx>( tcx: TyCtxt<'tcx>, root_span: Span, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 0d0ebe6f390b0..4b447229c5f61 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -4,6 +4,7 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; use std::borrow::Borrow; use std::hash::Hash; use std::ops::ControlFlow; @@ -181,6 +182,24 @@ impl interpret::MayLeak for ! { } impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { + fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; + ( + Symbol::intern( + &caller + .file + .name + .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy(), + ), + u32::try_from(caller.line).unwrap(), + u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), + ) + } + /// "Intercept" a function call, because we have something special to do for it. /// All `#[rustc_do_not_const_check]` functions should be hooked here. /// If this returns `Some` function, which may be `instance` or a different function with diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index bcbe996be7d88..d6ee6975cddd5 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,12 +1,12 @@ // Not in interpret to make sure we do not use private implementation details use crate::errors::MaxNumNodesInConstErr; -use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar}; +use crate::interpret::InterpCx; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_span::source_map::DUMMY_SP; mod error; mod eval_queries; @@ -20,20 +20,6 @@ pub use fn_queries::*; pub use machine::*; pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; -pub(crate) fn const_caller_location( - tcx: TyCtxt<'_>, - (file, line, col): (Symbol, u32, u32), -) -> mir::ConstValue<'_> { - trace!("const_caller_location: {}:{}:{}", file, line, col); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No); - - let loc_place = ecx.alloc_caller_location(file, line, col); - if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { - bug!("intern_const_alloc_recursive should not error in this case") - } - mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx)) -} - // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index ec0af79459cbf..f3c19495a61f0 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -595,6 +595,68 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a + /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`. + pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { + for frame in self.stack().iter().rev() { + debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); + + // Assert that the frame we look at is actually executing code currently + // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). + let loc = frame.loc.left().unwrap(); + + // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all + // (such as `box`). Use the normal span by default. + let mut source_info = *frame.body.source_info(loc); + + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks[loc.block]; + if loc.statement_index == block.statements.len() { + debug!( + "find_closest_untracked_caller_location: got terminator {:?} ({:?})", + block.terminator(), + block.terminator().kind, + ); + if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind { + source_info.span = fn_span; + } + } + + // Note: this must be kept in sync with get_caller_location from cg_ssa. + + // Walk up the `SourceScope`s, in case some of them are from MIR inlining. + // If so, the starting `source_info.span` is in the innermost inlined + // function, and will be replaced with outer callsite spans as long + // as the inlined functions were `#[track_caller]`. + loop { + let scope_data = &frame.body.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(*self.tcx) { + return source_info.span; + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } + } + + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !frame.instance.def.requires_caller_location(*self.tcx) { + return source_info.span; + } + } + + span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") + } + #[inline(always)] pub fn layout_of_local( &self, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c97207a61ac3b..8fd04a6879157 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -22,8 +22,6 @@ use super::{ use crate::fluent_generated as fluent; -mod caller_location; - fn numeric_intrinsic(name: Symbol, bits: u128, kind: Primitive) -> Scalar { let size = match kind { Primitive::Int(integer, _) => integer.size(), @@ -130,8 +128,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match intrinsic_name { sym::caller_location => { let span = self.find_closest_untracked_caller_location(); - let location = self.alloc_caller_location_for_span(span); - self.write_immediate(location.to_ref(self), dest)?; + let val = self.tcx.span_as_caller_location(span); + let val = + self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?; + self.copy_op(&val, dest, /* allow_transmute */ false)?; } sym::min_align_of_val | sym::size_of_val => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs deleted file mode 100644 index 16b7decf9c43c..0000000000000 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ /dev/null @@ -1,136 +0,0 @@ -use rustc_ast::Mutability; -use rustc_hir::lang_items::LangItem; -use rustc_middle::mir::TerminatorKind; -use rustc_middle::ty::layout::LayoutOf; -use rustc_span::{Span, Symbol}; - -use crate::interpret::{ - intrinsics::{InterpCx, Machine}, - MPlaceTy, MemoryKind, Scalar, -}; - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. - pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { - for frame in self.stack().iter().rev() { - debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); - - // Assert that the frame we look at is actually executing code currently - // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). - let loc = frame.loc.left().unwrap(); - - // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all - // (such as `box`). Use the normal span by default. - let mut source_info = *frame.body.source_info(loc); - - // If this is a `Call` terminator, use the `fn_span` instead. - let block = &frame.body.basic_blocks[loc.block]; - if loc.statement_index == block.statements.len() { - debug!( - "find_closest_untracked_caller_location: got terminator {:?} ({:?})", - block.terminator(), - block.terminator().kind - ); - if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind { - source_info.span = fn_span; - } - } - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &frame.body.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(*self.tcx) { - return source_info.span; - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !frame.instance.def.requires_caller_location(*self.tcx) { - return source_info.span; - } - } - - span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") - } - - /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. - pub(crate) fn alloc_caller_location( - &mut self, - filename: Symbol, - line: u32, - col: u32, - ) -> MPlaceTy<'tcx, M::Provenance> { - let loc_details = self.tcx.sess.opts.unstable_opts.location_detail; - // This can fail if rustc runs out of memory right here. Trying to emit an error would be - // pointless, since that would require allocating more memory than these short strings. - let file = if loc_details.file { - self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not) - .unwrap() - } else { - // FIXME: This creates a new allocation each time. It might be preferable to - // perform this allocation only once, and re-use the `MPlaceTy`. - // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 - self.allocate_str("", MemoryKind::CallerLocation, Mutability::Not).unwrap() - }; - let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; - let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; - - // Allocate memory for `CallerLocation` struct. - let loc_ty = self - .tcx - .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None)) - .instantiate(*self.tcx, self.tcx.mk_args(&[self.tcx.lifetimes.re_erased.into()])); - let loc_layout = self.layout_of(loc_ty).unwrap(); - let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); - - // Initialize fields. - self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(line, &self.project_field(&location, 1).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(col, &self.project_field(&location, 2).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - - location - } - - pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - - use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; - ( - Symbol::intern( - &caller - .file - .name - .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS) - .to_string_lossy(), - ), - u32::try_from(caller.line).unwrap(), - u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), - ) - } - - pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> { - let (file, line, column) = self.location_triple_for_span(span); - self.alloc_caller_location(file, line, column) - } -} diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1fd5723f2775e..7fc87ed888102 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) { const_eval::provide(providers); providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; - providers.const_caller_location = const_eval::const_caller_location; + providers.const_caller_location = util::caller_location::const_caller_location_provider; providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); const_eval::eval_to_valtree(tcx, param_env, raw) diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs new file mode 100644 index 0000000000000..d67863d75d668 --- /dev/null +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -0,0 +1,63 @@ +use rustc_hir::LangItem; +use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_type_ir::Mutability; + +use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext}; +use crate::interpret::*; + +/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. +fn alloc_caller_location<'mir, 'tcx>( + ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, + filename: Symbol, + line: u32, + col: u32, +) -> MPlaceTy<'tcx> { + let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail; + // This can fail if rustc runs out of memory right here. Trying to emit an error would be + // pointless, since that would require allocating more memory than these short strings. + let file = if loc_details.file { + ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap() + } else { + // FIXME: This creates a new allocation each time. It might be preferable to + // perform this allocation only once, and re-use the `MPlaceTy`. + // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 + ecx.allocate_str("", MemoryKind::CallerLocation, Mutability::Not).unwrap() + }; + let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; + let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; + + // Allocate memory for `CallerLocation` struct. + let loc_ty = ecx + .tcx + .type_of(ecx.tcx.require_lang_item(LangItem::PanicLocation, None)) + .instantiate(*ecx.tcx, ecx.tcx.mk_args(&[ecx.tcx.lifetimes.re_erased.into()])); + let loc_layout = ecx.layout_of(loc_ty).unwrap(); + let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); + + // Initialize fields. + ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + ecx.write_scalar(col, &ecx.project_field(&location, 2).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + + location +} + +pub(crate) fn const_caller_location_provider( + tcx: TyCtxt<'_>, + (file, line, col): (Symbol, u32, u32), +) -> mir::ConstValue<'_> { + trace!("const_caller_location: {}:{}:{}", file, line, col); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No); + + let loc_place = alloc_caller_location(&mut ecx, file, line, col); + if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { + bug!("intern_const_alloc_recursive should not error in this case") + } + mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx)) +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 040b3071e6f87..1e58bd645cdde 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,6 +1,7 @@ use rustc_middle::mir; mod alignment; +pub(crate) mod caller_location; mod check_validity_requirement; mod compare_types; mod type_name; diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 4e429f316e806..47865bae0d2d1 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir}; +use rustc_session::RemapFileNameExt; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size}; @@ -529,3 +530,20 @@ impl<'tcx> Display for Const<'tcx> { } } } + +/////////////////////////////////////////////////////////////////////////// +/// Const-related utilities + +impl<'tcx> TyCtxt<'tcx> { + pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); + self.const_caller_location(( + rustc_span::symbol::Symbol::intern( + &caller.file.name.for_codegen(&self.sess).to_string_lossy(), + ), + caller.line as u32, + caller.col_display as u32 + 1, + )) + } +} From 04fa124feb6e3f78fe9bea9fac7b4cbd033c9160 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 28 Oct 2023 16:16:15 +0200 Subject: [PATCH 02/10] share the track_caller handling within a mir::Body --- .../rustc_codegen_cranelift/src/common.rs | 37 +++-------------- compiler/rustc_codegen_ssa/src/mir/block.rs | 36 ++--------------- .../src/interpret/eval_context.rs | 40 +++++-------------- compiler/rustc_middle/src/mir/mod.rs | 34 ++++++++++++++++ 4 files changed, 54 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 38dba016c23e1..63562d335089b 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -430,38 +430,11 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } } - // Note: must be kept in sync with get_caller_location from cg_ssa - pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> { - let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| { - let const_loc = fx.tcx.span_as_caller_location(span); - crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty()) - }; - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &self.mir.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(self.tcx) { - return span_to_caller_location(self, source_info.span); - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - self.caller_location.unwrap_or_else(|| span_to_caller_location(self, source_info.span)) + pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { + self.mir.caller_location_span(source_info, self.caller_location, self.tcx, |span| { + let const_loc = self.tcx.span_as_caller_location(span); + crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + }) } pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index cbe3c73689679..22afb4063267e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1449,40 +1449,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn get_caller_location( &mut self, bx: &mut Bx, - mut source_info: mir::SourceInfo, + source_info: mir::SourceInfo, ) -> OperandRef<'tcx, Bx::Value> { - let tcx = bx.tcx(); - - let mut span_to_caller_location = |span: Span| { - let const_loc = tcx.span_as_caller_location(span); + self.mir.caller_location_span(source_info, self.caller_location, bx.tcx(), |span: Span| { + let const_loc = bx.tcx().span_as_caller_location(span); OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) - }; - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &self.mir.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(tcx) { - return span_to_caller_location(source_info.span); - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span)) + }) } fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f3c19495a61f0..07cab5e3400ef 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -622,35 +622,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - // Note: this must be kept in sync with get_caller_location from cg_ssa. - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &frame.body.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(*self.tcx) { - return source_info.span; - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !frame.instance.def.requires_caller_location(*self.tcx) { - return source_info.span; + let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) { + // We use `Err(())` as indication that we should continue up the call stack since + // this is a `#[track_caller]` function. + Some(Err(())) + } else { + None + }; + if let Ok(span) = + frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok) + { + return span; } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a85af7c3fb511..7054cede2d87c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -578,6 +578,40 @@ impl<'tcx> Body<'tcx> { pub fn is_custom_mir(&self) -> bool { self.injection_phase.is_some() } + + /// For a `Location` in this scope, determine what the "caller location" at that point is. This + /// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions + /// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions, + /// or the function's scope. + pub fn caller_location_span( + &self, + mut source_info: SourceInfo, + caller_location: Option, + tcx: TyCtxt<'tcx>, + from_span: impl FnOnce(Span) -> T, + ) -> T { + loop { + let scope_data = &self.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(tcx) { + return from_span(source_info.span); + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } + } + + // No inlined `SourceScope`s, or all of them were `#[track_caller]`. + caller_location.unwrap_or_else(|| from_span(source_info.span)) + } } #[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] From 552abdce505a3aef1f25654697e4c1d695e11c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 29 Oct 2023 00:00:00 +0000 Subject: [PATCH 03/10] Rename a few remaining references to abort terminator Follow up to e3f2edc75bf2becb57d7d770bba20606da1c4224 --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9eb02be2f155b..8477686d4f577 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1640,7 +1640,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } TerminatorKind::UnwindTerminate(_) => { if !is_cleanup { - span_mirbug!(self, block_data, "abort on non-cleanup block!") + span_mirbug!(self, block_data, "terminate on non-cleanup block!") } } TerminatorKind::Return => { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 12057f5e1cb66..5f4ff22bc49ca 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -785,7 +785,7 @@ impl<'tcx> TerminatorKind<'tcx> { CoroutineDrop => write!(fmt, "coroutine_drop"), UnwindResume => write!(fmt, "resume"), UnwindTerminate(reason) => { - write!(fmt, "abort({})", reason.as_short_str()) + write!(fmt, "terminate({})", reason.as_short_str()) } Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), From 4b14048d6037a2da5601952a2dd36fe67d04218b Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 30 Oct 2023 11:22:32 +0300 Subject: [PATCH 04/10] improve and fix x install Fix: Write access check of `prefix` and `sysconfdir` when DESTDIR is present. Improvement: Instead of repeatedly reading `DESTDIR` within each `fn prepare_dir` usage, read it once and pass it to the `fn prepare_dir`. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/install.rs | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 391995b7c3b84..ed6b1c5154c8c 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -71,16 +71,27 @@ fn install_sh( let prefix = default_path(&builder.config.prefix, "/usr/local"); let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); + let destdir_env = env::var_os("DESTDIR").map(PathBuf::from); - // Sanity check for the user write access on prefix and sysconfdir - assert!( - is_dir_writable_for_user(&prefix), - "User doesn't have write access on `install.prefix` path in the `config.toml`.", - ); - assert!( - is_dir_writable_for_user(&sysconfdir), - "User doesn't have write access on `install.sysconfdir` path in `config.toml`." - ); + // Sanity checks on the write access of user. + // + // When the `DESTDIR` environment variable is present, there is no point to + // check write access for `prefix` and `sysconfdir` individually, as they + // are combined with the path from the `DESTDIR` environment variable. In + // this case, we only need to check the `DESTDIR` path, disregarding the + // `prefix` and `sysconfdir` paths. + if let Some(destdir) = &destdir_env { + assert!(is_dir_writable_for_user(destdir), "User doesn't have write access on DESTDIR."); + } else { + assert!( + is_dir_writable_for_user(&prefix), + "User doesn't have write access on `install.prefix` path in the `config.toml`.", + ); + assert!( + is_dir_writable_for_user(&sysconfdir), + "User doesn't have write access on `install.sysconfdir` path in `config.toml`." + ); + } let datadir = prefix.join(default_path(&builder.config.datadir, "share")); let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust")); @@ -94,13 +105,13 @@ fn install_sh( let mut cmd = Command::new(SHELL); cmd.current_dir(&empty_dir) .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) - .arg(format!("--prefix={}", prepare_dir(prefix))) - .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir))) - .arg(format!("--datadir={}", prepare_dir(datadir))) - .arg(format!("--docdir={}", prepare_dir(docdir))) - .arg(format!("--bindir={}", prepare_dir(bindir))) - .arg(format!("--libdir={}", prepare_dir(libdir))) - .arg(format!("--mandir={}", prepare_dir(mandir))) + .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix))) + .arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir))) + .arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir))) + .arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir))) + .arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir))) + .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir))) + .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir))) .arg("--disable-ldconfig"); builder.run(&mut cmd); t!(fs::remove_dir_all(&empty_dir)); @@ -110,19 +121,16 @@ fn default_path(config: &Option, default: &str) -> PathBuf { config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)) } -fn prepare_dir(mut path: PathBuf) -> String { +fn prepare_dir(destdir_env: &Option, mut path: PathBuf) -> String { // The DESTDIR environment variable is a standard way to install software in a subdirectory // while keeping the original directory structure, even if the prefix or other directories // contain absolute paths. // // More information on the environment variable is available here: // https://www.gnu.org/prep/standards/html_node/DESTDIR.html - if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) { - // Sanity check for the user write access on DESTDIR - assert!(is_dir_writable_for_user(&destdir), "User doesn't have write access on DESTDIR."); - + if let Some(destdir) = destdir_env { let without_destdir = path.clone(); - path = destdir; + path = destdir.clone(); // Custom .join() which ignores disk roots. for part in without_destdir.components() { if let Component::Normal(s) = part { From 745c600617bc5dd1405cb53b1f79e5f421d62b0f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 30 Oct 2023 10:13:12 +0000 Subject: [PATCH 05/10] Talk about `gen fn` in diagnostics about `gen fn` --- compiler/rustc_parse/messages.ftl | 4 ++-- compiler/rustc_parse/src/errors.rs | 4 ++-- compiler/rustc_parse/src/parser/item.rs | 2 +- tests/ui/coroutine/gen_fn.e2024.stderr | 4 ++-- tests/ui/coroutine/gen_fn.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9bedd7b8a338a..9ba1f0a5df914 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -278,8 +278,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}` parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` -parse_gen_block = `gen` blocks are not yet implemented - .help = only the keyword is reserved for now +parse_gen_fn = `gen` functions are not yet implemented + .help = for now you can use `gen {"{}"}` blocks and return `impl Iterator` instead parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index c0e94d15da0bf..e5cd91a32a7bf 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -521,9 +521,9 @@ pub(crate) struct CatchAfterTry { } #[derive(Diagnostic)] -#[diag(parse_gen_block)] +#[diag(parse_gen_fn)] #[help] -pub(crate) struct GenBlock { +pub(crate) struct GenFn { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a9f456d64cd11..55ad3f42938b4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> { } if let Gen::Yes { span, .. } = genness { - self.sess.emit_err(errors::GenBlock { span }); + self.sess.emit_err(errors::GenFn { span }); } if !self.eat_keyword_case(kw::Fn, case) { diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr index 41bd04d976912..388e10fd65e44 100644 --- a/tests/ui/coroutine/gen_fn.e2024.stderr +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -1,10 +1,10 @@ -error: `gen` blocks are not yet implemented +error: `gen` functions are not yet implemented --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} | ^^^ | - = help: only the keyword is reserved for now + = help: for now you can use `gen {}` blocks and return `impl Iterator` instead error: aborting due to previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 9566660dfb566..da515f263b00a 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -3,6 +3,6 @@ gen fn foo() {} //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` -//[e2024]~^^ ERROR: `gen` blocks are not yet implemented +//[e2024]~^^ ERROR: `gen` functions are not yet implemented fn main() {} From bc926f7c33c5fa12a5d42c1c597245d047346fd6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 30 Oct 2023 10:23:57 +0000 Subject: [PATCH 06/10] Add a custom panic message for resuming `gen` blocks after they panicked --- compiler/rustc_middle/messages.ftl | 2 ++ compiler/rustc_middle/src/mir/terminator.rs | 3 +-- tests/ui/coroutine/gen_block_panic.rs | 25 +++++++++++++++++++++ tests/ui/coroutine/gen_block_panic.stderr | 12 ++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/ui/coroutine/gen_block_panic.rs create mode 100644 tests/ui/coroutine/gen_block_panic.stderr diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 37ff5bcf1e2da..27d555d7e26c7 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -12,6 +12,8 @@ middle_assert_coroutine_resume_after_return = coroutine resumed after completion middle_assert_divide_by_zero = attempt to divide `{$val}` by zero +middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked + middle_assert_misaligned_ptr_deref = misaligned pointer dereference: address must be a multiple of {$required} but is {$found} diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c2aa015f4b7e7..9dfbe1733cc51 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -250,8 +250,7 @@ impl AssertKind { middle_assert_coroutine_resume_after_return } ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic, - // FIXME(gen_blocks): custom error message for `gen` blocks - ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_async_resume_after_panic, + ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_gen_resume_after_panic, ResumedAfterPanic(CoroutineKind::Coroutine) => { middle_assert_coroutine_resume_after_panic } diff --git a/tests/ui/coroutine/gen_block_panic.rs b/tests/ui/coroutine/gen_block_panic.rs new file mode 100644 index 0000000000000..33e2b4b265a11 --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.rs @@ -0,0 +1,25 @@ +//compile-flags: --edition 2024 -Zunstable-options +// run-pass +#![feature(gen_blocks)] + +fn main() { + let mut iter = gen { + yield 42; + panic!("foo"); + yield 69; //~ WARN: unreachable statement + }; + assert_eq!(iter.next(), Some(42)); + let mut tmp = std::panic::AssertUnwindSafe(&mut iter); + match std::panic::catch_unwind(move || tmp.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!(*err.downcast::<&'static str>().unwrap(), "foo"), + } + + match std::panic::catch_unwind(move || iter.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!( + *err.downcast::<&'static str>().unwrap(), + "`gen fn` should just keep returning `None` after panicking", + ), + } +} diff --git a/tests/ui/coroutine/gen_block_panic.stderr b/tests/ui/coroutine/gen_block_panic.stderr new file mode 100644 index 0000000000000..33d2a95cab8bc --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.stderr @@ -0,0 +1,12 @@ +warning: unreachable statement + --> $DIR/gen_block_panic.rs:9:9 + | +LL | panic!("foo"); + | ------------- any code following this expression is unreachable +LL | yield 69; + | ^^^^^^^^^ unreachable statement + | + = note: `#[warn(unreachable_code)]` on by default + +warning: 1 warning emitted + From a2486dba3b6cd64b082273a3e24a3e8b6b65dc1b Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 30 Oct 2023 19:04:55 +0530 Subject: [PATCH 07/10] Fix missing leading space in suggestion For a local pattern with no space between `let` and `(` e.g.: let(_a) = 3; we were previously suggesting this illegal code: let_a =3; After this change the suggestion will instead be: let _a =3; (Note the space after `let`) --- compiler/rustc_lint/src/unused.rs | 2 +- tests/ui/lint/lint-unnecessary-parens.fixed | 8 +++ tests/ui/lint/lint-unnecessary-parens.rs | 8 +++ tests/ui/lint/lint-unnecessary-parens.stderr | 74 +++++++++++++++++++- 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 6b31fb079e01b..355855b8e2b3e 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1154,7 +1154,7 @@ impl EarlyLintPass for UnusedParens { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); + self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false)); } ::check_stmt(self, cx, s) diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index bafac05d8daa7..b17914da6e6a3 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -84,6 +84,14 @@ fn main() { _a = 0; //~ ERROR unnecessary parentheses around assigned value _a += 1; //~ ERROR unnecessary parentheses around assigned value + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = baz!(3, 4); let _b = baz!(3); } diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index ce537a4dc1da0..4cbd6562cd395 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -84,6 +84,14 @@ fn main() { _a = (0); //~ ERROR unnecessary parentheses around assigned value _a += (1); //~ ERROR unnecessary parentheses around assigned value + let(mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + let (mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + let( mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + + let(_a) = 3; //~ ERROR unnecessary parentheses around pattern + let (_a) = 3; //~ ERROR unnecessary parentheses around pattern + let( _a) = 3; //~ ERROR unnecessary parentheses around pattern + let _a = baz!(3, 4); let _b = baz!(3); } diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index 2ad07530f8c8d..ba7a78b8da1a9 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -267,5 +267,77 @@ LL - _a += (1); LL + _a += 1; | -error: aborting due to 22 previous errors +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:87:8 + | +LL | let(mut _a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let(mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:88:9 + | +LL | let (mut _a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let (mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:89:8 + | +LL | let( mut _a) = 3; + | ^^ ^ + | +help: remove these parentheses + | +LL - let( mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:91:8 + | +LL | let(_a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let(_a) = 3; +LL + let _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:92:9 + | +LL | let (_a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let (_a) = 3; +LL + let _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:93:8 + | +LL | let( _a) = 3; + | ^^ ^ + | +help: remove these parentheses + | +LL - let( _a) = 3; +LL + let _a = 3; + | + +error: aborting due to 28 previous errors From 82f34fdd2313b27a4f3e28bed74163154e231b9e Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 30 Oct 2023 19:20:56 +0800 Subject: [PATCH 08/10] Fix #117284, Fix unused variables lint issue for args in macro --- compiler/rustc_passes/messages.ftl | 3 ++ compiler/rustc_passes/src/errors.rs | 21 ++++++++++---- compiler/rustc_passes/src/liveness.rs | 25 ++++++++++++---- .../lint/unused/issue-117284-arg-in-macro.rs | 17 +++++++++++ .../unused/issue-117284-arg-in-macro.stderr | 29 +++++++++++++++++++ 5 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 tests/ui/lint/unused/issue-117284-arg-in-macro.rs create mode 100644 tests/ui/lint/unused/issue-117284-arg-in-macro.stderr diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 026186cbe6cd3..a0a98dd853666 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -778,6 +778,8 @@ passes_unused_var_maybe_capture_ref = unused variable: `{$name}` passes_unused_var_remove_field = unused variable: `{$name}` passes_unused_var_remove_field_suggestion = try removing the field +passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable + passes_unused_variable_try_ignore = unused variable: `{$name}` .suggestion = try ignoring the field @@ -785,6 +787,7 @@ passes_unused_variable_try_prefix = unused variable: `{$name}` .label = unused variable .suggestion = if this is intentional, prefix it with an underscore + passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index eca0fb7748b15..bc31b5db021d0 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1768,15 +1768,24 @@ pub struct UnusedVariableTryPrefix { #[subdiagnostic] pub string_interp: Vec, #[subdiagnostic] - pub sugg: UnusedVariableTryPrefixSugg, + pub sugg: UnusedVariableSugg, + pub name: String, } #[derive(Subdiagnostic)] -#[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] -pub struct UnusedVariableTryPrefixSugg { - #[suggestion_part(code = "_{name}")] - pub spans: Vec, - pub name: String, +pub enum UnusedVariableSugg { + #[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] + TryPrefixSugg { + #[suggestion_part(code = "_{name}")] + spans: Vec, + name: String, + }, + #[help(passes_unused_variable_args_in_macro)] + NoSugg { + #[primary_span] + span: Span, + name: String, + }, } pub struct UnusedVariableStringInterp { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index d068fe62473cd..b73fb984c0eac 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1580,7 +1580,6 @@ impl<'tcx> Liveness<'_, 'tcx> { opt_body: Option<&hir::Body<'_>>, ) { let first_hir_id = hir_ids_and_spans[0].0; - if let Some(name) = self.should_warn(var).filter(|name| name != "self") { // annoying: for parameters in funcs like `fn(x: i32) // {ret}`, there is only one node, so asking about @@ -1652,11 +1651,29 @@ impl<'tcx> Liveness<'_, 'tcx> { }, ); } else { + // #117284, when `pat_span` and `ident_span` have different contexts + // we can't provide a good suggestion, instead we pointed out the spans from macro + let from_macro = non_shorthands + .iter() + .find(|(_, pat_span, ident_span)| { + pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion() + }) + .map(|(_, pat_span, _)| *pat_span); let non_shorthands = non_shorthands .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::>(); + let suggestions = self.string_interp_suggestions(&name, opt_body); + let sugg = if let Some(span) = from_macro { + errors::UnusedVariableSugg::NoSugg { span, name: name.clone() } + } else { + errors::UnusedVariableSugg::TryPrefixSugg { + spans: non_shorthands, + name: name.clone(), + } + }; + self.ir.tcx.emit_spanned_lint( lint::builtin::UNUSED_VARIABLES, first_hir_id, @@ -1666,10 +1683,8 @@ impl<'tcx> Liveness<'_, 'tcx> { .collect::>(), errors::UnusedVariableTryPrefix { label: if !suggestions.is_empty() { Some(pat.span) } else { None }, - sugg: errors::UnusedVariableTryPrefixSugg { - spans: non_shorthands, - name, - }, + name, + sugg, string_interp: suggestions, }, ); diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.rs b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs new file mode 100644 index 0000000000000..eea0f4c594dc8 --- /dev/null +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs @@ -0,0 +1,17 @@ +#![deny(unused_variables)] +macro_rules! make_var { + ($struct:ident, $var:ident) => { + let $var = $struct.$var; + }; +} + +#[allow(unused)] +struct MyStruct { + var: i32, +} + +fn main() { + let s = MyStruct { var: 42 }; + make_var!(s, var); //~ ERROR unused variable: `var` + let a = 1; //~ ERROR unused variable: `a` +} diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr new file mode 100644 index 0000000000000..84efaa4f3687b --- /dev/null +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr @@ -0,0 +1,29 @@ +error: unused variable: `var` + --> $DIR/issue-117284-arg-in-macro.rs:15:18 + | +LL | make_var!(s, var); + | ^^^ + | +help: `var` is captured in macro and introduced a unused variable + --> $DIR/issue-117284-arg-in-macro.rs:4:13 + | +LL | let $var = $struct.$var; + | ^^^^ +... +LL | make_var!(s, var); + | ----------------- in this macro invocation +note: the lint level is defined here + --> $DIR/issue-117284-arg-in-macro.rs:1:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `make_var` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unused variable: `a` + --> $DIR/issue-117284-arg-in-macro.rs:16:9 + | +LL | let a = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: aborting due to 2 previous errors + From 8508e658958d02bc0c4bb62646a9cf9098ee53a8 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sun, 29 Oct 2023 17:04:52 -0400 Subject: [PATCH 09/10] Fix bad-c-variadic error being emitted multiple times If a function incorrectly contains multiple `...` args, and is also not foreign or `unsafe extern "C"`, only emit the latter error once. --- .../rustc_ast_passes/src/ast_validation.rs | 18 +++++--- compiler/rustc_ast_passes/src/errors.rs | 2 +- tests/ui/c-variadic/issue-86053-1.stderr | 10 +---- .../variadic-ffi-semantic-restrictions.rs | 2 - .../variadic-ffi-semantic-restrictions.stderr | 42 +++++++------------ 5 files changed, 31 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0477c7b2ba99c..f59f7d389cdab 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -485,6 +485,18 @@ impl<'a> AstValidator<'a> { /// Reject C-variadic type unless the function is foreign, /// or free and `unsafe extern "C"` semantically. fn check_c_variadic_type(&self, fk: FnKind<'a>) { + let variadic_spans: Vec<_> = fk + .decl() + .inputs + .iter() + .filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)) + .map(|arg| arg.span) + .collect(); + + if variadic_spans.is_empty() { + return; + } + match (fk.ctxt(), fk.header()) { (Some(FnCtxt::Foreign), _) => return, (Some(FnCtxt::Free), Some(header)) => match header.ext { @@ -499,11 +511,7 @@ impl<'a> AstValidator<'a> { _ => {} }; - for Param { ty, span, .. } in &fk.decl().inputs { - if let TyKind::CVarArgs = ty.kind { - self.err_handler().emit_err(errors::BadCVariadic { span: *span }); - } - } + self.err_handler().emit_err(errors::BadCVariadic { span: variadic_spans }); } fn check_item_named(&self, ident: Ident, kind: &str) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index e74d94e43474d..579f528e20f94 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -271,7 +271,7 @@ pub struct ExternItemAscii { #[diag(ast_passes_bad_c_variadic)] pub struct BadCVariadic { #[primary_span] - pub span: Span, + pub span: Vec, } #[derive(Diagnostic)] diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index 5a02f4aa93a95..69e19e1d4d26b 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -50,13 +50,7 @@ error: only foreign or `unsafe extern "C"` functions may be C-variadic --> $DIR/issue-86053-1.rs:11:12 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { - | ^^^ - -error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/issue-86053-1.rs:11:36 - | -LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { - | ^^^ + | ^^^ ^^^ error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 @@ -76,6 +70,6 @@ help: you might be missing a type parameter LL | fn ordering4 < 'a , 'b, F > ( a : , self , self , self , | +++ -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 0b61e267da80b..8e382314ca27e 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -49,11 +49,9 @@ impl X { //~| ERROR C-variadic function must be declared with at least one named argument fn i_f3(..., x: isize, ...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic - //~| ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic - //~| ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index f1cbbb279c849..95e381f06fc46 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -116,91 +116,79 @@ error: only foreign or `unsafe extern "C"` functions may be C-variadic --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn i_f3(..., x: isize, ...) {} - | ^^^ - -error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:50:28 - | -LL | fn i_f3(..., x: isize, ...) {} - | ^^^ + | ^^^ ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 - | -LL | fn i_f4(..., x: isize, ...) {} - | ^^^ - -error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 | LL | fn i_f4(..., x: isize, ...) {} - | ^^^ + | ^^^ ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:63:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:61:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:63:13 | LL | fn t_f3(...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:63:13 | LL | fn t_f3(...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:66:13 | LL | fn t_f4(...); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:66:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:69:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:69:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 | LL | fn t_f6(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 | LL | fn t_f6(..., x: isize); | ^^^ -error: aborting due to 34 previous errors +error: aborting due to 32 previous errors From f91b5ceaf2568c38afc5a4b7c82459ca3d857998 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sun, 29 Oct 2023 17:28:56 -0400 Subject: [PATCH 10/10] Explicitly reject const C-variadic functions Trying to use C-variadics in a const function would previously fail with an error like "destructor of `VaListImpl<'_>` cannot be evaluated at compile-time". Add an explicit check for const C-variadics to provide a clearer error: "functions cannot be both `const` and C-variadic". --- compiler/rustc_ast_passes/messages.ftl | 4 + .../rustc_ast_passes/src/ast_validation.rs | 19 +++- compiler/rustc_ast_passes/src/errors.rs | 11 +++ .../variadic-ffi-semantic-restrictions.rs | 15 +++ .../variadic-ffi-semantic-restrictions.stderr | 93 +++++++++++++++---- 5 files changed, 120 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 43020a930788f..d22bae816ef41 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -42,6 +42,10 @@ ast_passes_const_and_async = functions cannot be both `const` and `async` .async = `async` because of this .label = {""} +ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic + .const = `const` because of this + .variadic = C-variadic because of this + ast_passes_const_without_body = free constant item without body .suggestion = provide a definition for the constant diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f59f7d389cdab..477d3732d464f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -482,8 +482,11 @@ impl<'a> AstValidator<'a> { } } - /// Reject C-variadic type unless the function is foreign, - /// or free and `unsafe extern "C"` semantically. + /// Reject invalid C-variadic types. + /// + /// C-variadics must be: + /// - Non-const + /// - Either foreign, or free and `unsafe extern "C"` semantically fn check_c_variadic_type(&self, fk: FnKind<'a>) { let variadic_spans: Vec<_> = fk .decl() @@ -497,6 +500,18 @@ impl<'a> AstValidator<'a> { return; } + if let Some(header) = fk.header() { + if let Const::Yes(const_span) = header.constness { + let mut spans = variadic_spans.clone(); + spans.push(const_span); + self.err_handler().emit_err(errors::ConstAndCVariadic { + spans, + const_span, + variadic_spans: variadic_spans.clone(), + }); + } + } + match (fk.ctxt(), fk.header()) { (Some(FnCtxt::Foreign), _) => return, (Some(FnCtxt::Free), Some(header)) => match header.ext { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 579f528e20f94..d14b62d6bdc66 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -583,6 +583,17 @@ pub struct ConstAndAsync { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_const_and_c_variadic)] +pub struct ConstAndCVariadic { + #[primary_span] + pub spans: Vec, + #[label(ast_passes_const)] + pub const_span: Span, + #[label(ast_passes_variadic)] + pub variadic_spans: Vec, +} + #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = "E0130")] pub struct PatternInForeign { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 8e382314ca27e..b173e23e7a183 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -32,6 +32,18 @@ extern "C" fn f3_3(..., x: isize) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function +const unsafe extern "C" fn f4_1(x: isize, ...) {} +//~^ ERROR functions cannot be both `const` and C-variadic + +const extern "C" fn f4_2(x: isize, ...) {} +//~^ ERROR functions cannot be both `const` and C-variadic +//~| ERROR only foreign or `unsafe extern "C"` functions may be C-variadic + +const extern "C" fn f4_3(..., x: isize, ...) {} +//~^ ERROR functions cannot be both `const` and C-variadic +//~| ERROR only foreign or `unsafe extern "C"` functions may be C-variadic +//~| ERROR `...` must be the last argument of a C-variadic function + extern "C" { fn e_f1(...); //~^ ERROR C-variadic function must be declared with at least one named argument @@ -53,6 +65,9 @@ impl X { fn i_f4(..., x: isize, ...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function + const fn i_f5(x: isize, ...) {} + //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic + //~| ERROR functions cannot be both `const` and C-variadic } trait T { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 95e381f06fc46..18526080e4cb7 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -76,119 +76,172 @@ error: only foreign or `unsafe extern "C"` functions may be C-variadic LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ +error: functions cannot be both `const` and C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:35:1 + | +LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} + | ^^^^^ `const` because of this ^^^ C-variadic because of this + +error: functions cannot be both `const` and C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:38:1 + | +LL | const extern "C" fn f4_2(x: isize, ...) {} + | ^^^^^ `const` because of this ^^^ C-variadic because of this + +error: only foreign or `unsafe extern "C"` functions may be C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:38:36 + | +LL | const extern "C" fn f4_2(x: isize, ...) {} + | ^^^ + +error: `...` must be the last argument of a C-variadic function + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 + | +LL | const extern "C" fn f4_3(..., x: isize, ...) {} + | ^^^ + +error: functions cannot be both `const` and C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 + | +LL | const extern "C" fn f4_3(..., x: isize, ...) {} + | ^^^^^ ^^^ ^^^ C-variadic because of this + | | | + | | C-variadic because of this + | `const` because of this + +error: only foreign or `unsafe extern "C"` functions may be C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 + | +LL | const extern "C" fn f4_3(..., x: isize, ...) {} + | ^^^ ^^^ + error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 | LL | fn e_f1(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:38:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn e_f2(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:45:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:57:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f2(...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ ^^^ +error: functions cannot be both `const` and C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:5 + | +LL | const fn i_f5(x: isize, ...) {} + | ^^^^^ ^^^ C-variadic because of this + | | + | `const` because of this + +error: only foreign or `unsafe extern "C"` functions may be C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:29 + | +LL | const fn i_f5(x: isize, ...) {} + | ^^^ + error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:63:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f3(...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:63:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f3(...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:66:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:81:13 | LL | fn t_f4(...); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:66:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:81:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:69:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:84:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:69:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:84:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:87:13 | LL | fn t_f6(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:87:13 | LL | fn t_f6(..., x: isize); | ^^^ -error: aborting due to 32 previous errors +error: aborting due to 40 previous errors