Auto merge of #123708 - matthiaskrgr:rollup-uf9w1e9, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #121884 (Port exit-code run-make test to use rust)
 - #122200 (Unconditionally show update nightly hint on ICE)
 - #123568 (Clean up tests/ui by removing `does-nothing.rs`)
 - #123609 (Don't use bytepos offsets when computing semicolon span for removal)
 - #123612 (Set target-abi module flag for RISC-V targets)
 - #123633 (Store all args in the unsupported Command implementation)
 - #123668 (async closure coroutine by move body MirPass refactoring)

Failed merges:

 - #123701 (Only assert for child/parent projection compatibility AFTER checking that theyre coming from the same place)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-04-10 02:43:17 +00:00
commit 1c77f7378e
36 changed files with 491 additions and 203 deletions

View File

@ -346,7 +346,7 @@ impl<'a> AstValidator<'a> {
in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
const_context_label: parent_constness,
remove_const_sugg: (
self.session.source_map().span_extend_while(span, |c| c == ' ').unwrap_or(span),
self.session.source_map().span_extend_while_whitespace(span),
match parent_constness {
Some(_) => rustc_errors::Applicability::MachineApplicable,
None => rustc_errors::Applicability::MaybeIncorrect,

View File

@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager(
"LTOPostLink".as_ptr().cast(),
11,
) {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
module.module_llvm.llmod(),
llvm::LLVMModFlagBehavior::Error,
c"LTOPostLink".as_ptr().cast(),

View File

@ -180,13 +180,13 @@ pub unsafe fn create_module<'ll>(
// to ensure intrinsic calls don't use it.
if !sess.needs_plt() {
let avoid_plt = c"RtLibUseGOT".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
}
// Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Override,
canonical_jump_tables,
@ -197,7 +197,7 @@ pub unsafe fn create_module<'ll>(
// Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.)
if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Override,
enable_split_lto_unit,
@ -208,7 +208,7 @@ pub unsafe fn create_module<'ll>(
// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
if sess.is_sanitizer_kcfi_enabled() {
let kcfi = c"kcfi".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
}
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
@ -217,7 +217,7 @@ pub unsafe fn create_module<'ll>(
CFGuard::Disabled => {}
CFGuard::NoChecks => {
// Set `cfguard=1` module flag to emit metadata only.
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Warning,
c"cfguard".as_ptr() as *const _,
@ -226,7 +226,7 @@ pub unsafe fn create_module<'ll>(
}
CFGuard::Checks => {
// Set `cfguard=2` module flag to emit metadata and checks.
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Warning,
c"cfguard".as_ptr() as *const _,
@ -238,26 +238,26 @@ pub unsafe fn create_module<'ll>(
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
if sess.target.arch == "aarch64" {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Min,
c"branch-target-enforcement".as_ptr().cast(),
bti.into(),
);
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Min,
c"sign-return-address".as_ptr().cast(),
pac_ret.is_some().into(),
);
let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Min,
c"sign-return-address-all".as_ptr().cast(),
pac_opts.leaf.into(),
);
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Min,
c"sign-return-address-with-bkey".as_ptr().cast(),
@ -273,7 +273,7 @@ pub unsafe fn create_module<'ll>(
// Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Override,
c"cf-protection-branch".as_ptr().cast(),
@ -281,7 +281,7 @@ pub unsafe fn create_module<'ll>(
)
}
if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Override,
c"cf-protection-return".as_ptr().cast(),
@ -290,7 +290,7 @@ pub unsafe fn create_module<'ll>(
}
if sess.opts.unstable_opts.virtual_function_elimination {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Error,
c"Virtual Function Elim".as_ptr().cast(),
@ -300,7 +300,7 @@ pub unsafe fn create_module<'ll>(
// Set module flag to enable Windows EHCont Guard (/guard:ehcont).
if sess.opts.unstable_opts.ehcont_guard {
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
llmod,
llvm::LLVMModFlagBehavior::Warning,
c"ehcontguard".as_ptr() as *const _,
@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>(
llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1),
);
// Emit RISC-V specific target-abi metadata
// to workaround lld as the LTO plugin not
// correctly setting target-abi for the LTO object
// FIXME: https://github.com/llvm/llvm-project/issues/50591
// If llvm_abiname is empty, emit nothing.
let llvm_abiname = &sess.target.options.llvm_abiname;
if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() {
llvm::LLVMRustAddModuleFlagString(
llmod,
llvm::LLVMModFlagBehavior::Error,
c"target-abi".as_ptr(),
llvm_abiname.as_ptr().cast(),
llvm_abiname.len(),
);
}
// Add module flags specified via -Z llvm_module_flag
for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag {
let key = format!("{key}\0");
@ -341,7 +357,7 @@ pub unsafe fn create_module<'ll>(
// We already checked this during option parsing
_ => unreachable!(),
};
llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value)
llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value)
}
llmod

View File

@ -110,7 +110,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
.unstable_opts
.dwarf_version
.unwrap_or(sess.target.default_dwarf_version);
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
c"Dwarf Version".as_ptr().cast(),
@ -118,7 +118,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
);
} else {
// Indicate that we want CodeView debug information on MSVC
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
c"CodeView".as_ptr().cast(),
@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
}
// Prevent bitcode readers from deleting the debug info.
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
c"Debug Info Version".as_ptr().cast(),

View File

@ -1801,12 +1801,21 @@ extern "C" {
///
/// In order for Rust-C LTO to work, module flags must be compatible with Clang. What
/// "compatible" means depends on the merge behaviors involved.
pub fn LLVMRustAddModuleFlag(
pub fn LLVMRustAddModuleFlagU32(
M: &Module,
merge_behavior: LLVMModFlagBehavior,
name: *const c_char,
value: u32,
);
pub fn LLVMRustAddModuleFlagString(
M: &Module,
merge_behavior: LLVMModFlagBehavior,
name: *const c_char,
value: *const c_char,
value_len: size_t,
);
pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool;
pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>;

View File

@ -1,10 +1,7 @@
driver_impl_ice = the compiler unexpectedly panicked. this is a bug.
driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly
driver_impl_ice_bug_report_outdated =
it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the meantime
.update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists
.url = if the problem still persists, we would appreciate a bug report: {$bug_report_url}
driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
driver_impl_ice_flags = compiler flags: {$flags}

View File

@ -61,7 +61,7 @@ use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::{Instant, SystemTime};
use time::{Date, OffsetDateTime, Time};
use time::OffsetDateTime;
#[allow(unused_macros)]
macro do_not_use_print($($t:tt)*) {
@ -1385,9 +1385,6 @@ pub fn install_ice_hook(
using_internal_features
}
const DATE_FORMAT: &[time::format_description::FormatItem<'static>] =
&time::macros::format_description!("[year]-[month]-[day]");
/// Prints the ICE message, including query stack, but without backtrace.
///
/// The message will point the user at `bug_report_url` to report the ICE.
@ -1416,33 +1413,14 @@ fn report_ice(
dcx.emit_err(session_diagnostics::Ice);
}
use time::ext::NumericalDuration;
// Try to hint user to update nightly if applicable when reporting an ICE.
// Attempt to calculate when current version was released, and add 12 hours
// as buffer. If the current version's release timestamp is older than
// the system's current time + 24 hours + 12 hours buffer if we're on
// nightly.
if let Some("nightly") = option_env!("CFG_RELEASE_CHANNEL")
&& let Some(version) = option_env!("CFG_VERSION")
&& let Some(ver_date_str) = option_env!("CFG_VER_DATE")
&& let Ok(ver_date) = Date::parse(&ver_date_str, DATE_FORMAT)
&& let ver_datetime = OffsetDateTime::new_utc(ver_date, Time::MIDNIGHT)
&& let system_datetime = OffsetDateTime::from(SystemTime::now())
&& system_datetime.checked_sub(36.hours()).is_some_and(|d| d > ver_datetime)
&& !using_internal_features.load(std::sync::atomic::Ordering::Relaxed)
{
dcx.emit_note(session_diagnostics::IceBugReportOutdated {
version,
bug_report_url,
note_update: (),
note_url: (),
});
if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
} else {
if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
} else {
dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
// Only emit update nightly hint for users on nightly builds.
if rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
dcx.emit_note(session_diagnostics::UpdateNightlyNote);
}
}

View File

@ -43,19 +43,12 @@ pub(crate) struct IceBugReport<'a> {
}
#[derive(Diagnostic)]
#[diag(driver_impl_ice_bug_report_internal_feature)]
pub(crate) struct IceBugReportInternalFeature;
#[diag(driver_impl_ice_bug_report_update_note)]
pub(crate) struct UpdateNightlyNote;
#[derive(Diagnostic)]
#[diag(driver_impl_ice_bug_report_outdated)]
pub(crate) struct IceBugReportOutdated<'a> {
pub version: &'a str,
pub bug_report_url: &'a str,
#[note(driver_impl_update)]
pub note_update: (),
#[note(driver_impl_url)]
pub note_url: (),
}
#[diag(driver_impl_ice_bug_report_internal_feature)]
pub(crate) struct IceBugReportInternalFeature;
#[derive(Diagnostic)]
#[diag(driver_impl_ice_version)]

View File

@ -2023,8 +2023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.tcx
.sess
.source_map()
.span_extend_while(range_start.span, |c| c.is_whitespace())
.unwrap_or(range_start.span)
.span_extend_while_whitespace(range_start.span)
.shrink_to_hi()
.to(range_end.span);

View File

@ -15,7 +15,7 @@ use rustc_middle::traits::{
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
use rustc_span::{sym, BytePos, Span};
use rustc_span::{sym, Span};
use crate::errors::{
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
@ -763,8 +763,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
} else {
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
self.tcx
.sess
.source_map()
.span_extend_while_whitespace(last_expr.span)
.shrink_to_hi()
.with_hi(last_stmt.span.hi())
};
Some((span, needs_box))
}
@ -867,10 +873,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!(" {ident} ")
};
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
(
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
sugg,
)
(sm.span_extend_while_whitespace(left_span), sugg)
};
Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
}

View File

@ -215,10 +215,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
if let Some(deletion_span) = deletion_span {
let msg = "elide the single-use lifetime";
let (use_span, replace_lt) = if elide {
let use_span = sess
.source_map()
.span_extend_while(use_span, char::is_whitespace)
.unwrap_or(use_span);
let use_span = sess.source_map().span_extend_while_whitespace(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())

View File

@ -817,7 +817,7 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
extern "C" void LLVMRustAddModuleFlag(
extern "C" void LLVMRustAddModuleFlagU32(
LLVMModuleRef M,
Module::ModFlagBehavior MergeBehavior,
const char *Name,
@ -825,6 +825,16 @@ extern "C" void LLVMRustAddModuleFlag(
unwrap(M)->addModuleFlag(MergeBehavior, Name, Value);
}
extern "C" void LLVMRustAddModuleFlagString(
LLVMModuleRef M,
Module::ModFlagBehavior MergeBehavior,
const char *Name,
const char *Value,
size_t ValueLen) {
unwrap(M)->addModuleFlag(MergeBehavior, Name,
MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen)));
}
extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name,
size_t Len) {
return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr;

View File

@ -126,59 +126,33 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
let mut field_remapping = UnordMap::default();
// One parent capture may correspond to several child captures if we end up
// refining the set of captures via edition-2021 precise captures. We want to
// match up any number of child captures with one parent capture, so we keep
// peeking off this `Peekable` until the child doesn't match anymore.
let mut parent_captures =
tcx.closure_captures(parent_def_id).iter().copied().enumerate().peekable();
// Make sure we use every field at least once, b/c why are we capturing something
// if it's not used in the inner coroutine.
let mut field_used_at_least_once = false;
for (child_field_idx, child_capture) in tcx
let mut child_captures = tcx
.closure_captures(coroutine_def_id)
.iter()
.copied()
// By construction we capture all the args first.
.skip(num_args)
.enumerate()
.peekable();
// One parent capture may correspond to several child captures if we end up
// refining the set of captures via edition-2021 precise captures. We want to
// match up any number of child captures with one parent capture, so we keep
// peeking off this `Peekable` until the child doesn't match anymore.
for (parent_field_idx, parent_capture) in
tcx.closure_captures(parent_def_id).iter().copied().enumerate()
{
loop {
let Some(&(parent_field_idx, parent_capture)) = parent_captures.peek() else {
bug!("we ran out of parent captures!")
};
// Make sure we use every field at least once, b/c why are we capturing something
// if it's not used in the inner coroutine.
let mut field_used_at_least_once = false;
let PlaceBase::Upvar(parent_base) = parent_capture.place.base else {
bug!("expected capture to be an upvar");
};
let PlaceBase::Upvar(child_base) = child_capture.place.base else {
bug!("expected capture to be an upvar");
};
assert!(
child_capture.place.projections.len() >= parent_capture.place.projections.len()
);
// A parent matches a child they share the same prefix of projections.
// The child may have more, if it is capturing sub-fields out of
// something that is captured by-move in the parent closure.
if parent_base.var_path.hir_id != child_base.var_path.hir_id
|| !std::iter::zip(
&child_capture.place.projections,
&parent_capture.place.projections,
)
.all(|(child, parent)| child.kind == parent.kind)
{
// Make sure the field was used at least once.
assert!(
field_used_at_least_once,
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
);
field_used_at_least_once = false;
// Skip this field.
let _ = parent_captures.next().unwrap();
continue;
}
// A parent matches a child if they share the same prefix of projections.
// The child may have more, if it is capturing sub-fields out of
// something that is captured by-move in the parent closure.
while child_captures.peek().map_or(false, |(_, child_capture)| {
child_prefix_matches_parent_projections(parent_capture, child_capture)
}) {
let (child_field_idx, child_capture) = child_captures.next().unwrap();
// Store this set of additional projections (fields and derefs).
// We need to re-apply them later.
@ -221,15 +195,15 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
);
field_used_at_least_once = true;
break;
}
}
// Pop the last parent capture
if field_used_at_least_once {
let _ = parent_captures.next().unwrap();
// Make sure the field was used at least once.
assert!(
field_used_at_least_once,
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
);
}
assert_eq!(parent_captures.next(), None, "leftover parent captures?");
assert_eq!(child_captures.next(), None, "leftover child captures?");
if coroutine_kind == ty::ClosureKind::FnOnce {
assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len());
@ -251,6 +225,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
let mut by_move_body = body.clone();
MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body);
dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(()));
// FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body.
by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim {
coroutine_def_id: coroutine_def_id.to_def_id(),
});
@ -258,6 +233,23 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
}
}
fn child_prefix_matches_parent_projections(
parent_capture: &ty::CapturedPlace<'_>,
child_capture: &ty::CapturedPlace<'_>,
) -> bool {
let PlaceBase::Upvar(parent_base) = parent_capture.place.base else {
bug!("expected capture to be an upvar");
};
let PlaceBase::Upvar(child_base) = child_capture.place.base else {
bug!("expected capture to be an upvar");
};
assert!(child_capture.place.projections.len() >= parent_capture.place.projections.len());
parent_base.var_path.hir_id == child_base.var_path.hir_id
&& std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections)
.all(|(child, parent)| child.kind == parent.kind)
}
struct MakeByMoveBody<'tcx> {
tcx: TyCtxt<'tcx>,
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,

View File

@ -654,6 +654,12 @@ impl SourceMap {
})
}
/// Extends the span to include any trailing whitespace, or returns the original
/// span if a `SpanSnippetError` was encountered.
pub fn span_extend_while_whitespace(&self, span: Span) -> Span {
self.span_extend_while(span, char::is_whitespace).unwrap_or(span)
}
/// Extends the given `Span` to previous character while the previous character matches the predicate
pub fn span_extend_prev_while(
&self,
@ -1034,12 +1040,9 @@ impl SourceMap {
/// // ^^^^^^ input
/// ```
pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> {
let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?;
let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?));
if self.span_to_snippet(span).as_deref() != Ok(";") {
return None;
}
Some(span)
let span = self.span_extend_while_whitespace(mac_call);
let span = self.next_point(span);
if self.span_to_snippet(span).as_deref() == Ok(";") { Some(span) } else { None }
}
}

View File

@ -1592,8 +1592,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
.tcx
.sess
.source_map()
.span_extend_while(expr_span, char::is_whitespace)
.unwrap_or(expr_span)
.span_extend_while_whitespace(expr_span)
.shrink_to_hi()
.to(await_expr.span.shrink_to_hi());
err.span_suggestion(
@ -4851,10 +4850,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
return None;
};
let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
else {
return None;
};
let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span);
let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else {

View File

@ -1,7 +1,6 @@
use crate::ffi::OsStr;
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
use crate::num::NonZero;
use crate::path::Path;
use crate::sys::fs::File;
@ -16,7 +15,14 @@ pub use crate::ffi::OsString as EnvKey;
////////////////////////////////////////////////////////////////////////////////
pub struct Command {
program: OsString,
args: Vec<OsString>,
env: CommandEnv,
cwd: Option<OsString>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
// passed back to std::process with the pipes connected to the child, if any
@ -27,39 +33,62 @@ pub struct StdioPipes {
pub stderr: Option<AnonPipe>,
}
// FIXME: This should be a unit struct, so we can always construct it
// The value here should be never used, since we cannot spawn processes.
#[derive(Debug)]
pub enum Stdio {
Inherit,
Null,
MakePipe,
ParentStdout,
ParentStderr,
#[allow(dead_code)] // This variant exists only for the Debug impl
InheritFile(File),
}
impl Command {
pub fn new(_program: &OsStr) -> Command {
Command { env: Default::default() }
pub fn new(program: &OsStr) -> Command {
Command {
program: program.to_owned(),
args: vec![program.to_owned()],
env: Default::default(),
cwd: None,
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg(&mut self, _arg: &OsStr) {}
pub fn arg(&mut self, arg: &OsStr) {
self.args.push(arg.to_owned());
}
pub fn env_mut(&mut self) -> &mut CommandEnv {
&mut self.env
}
pub fn cwd(&mut self, _dir: &OsStr) {}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(dir.to_owned());
}
pub fn stdin(&mut self, _stdin: Stdio) {}
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, _stdout: Stdio) {}
pub fn stdout(&mut self, stdout: Stdio) {
self.stdout = Some(stdout);
}
pub fn stderr(&mut self, _stderr: Stdio) {}
pub fn stderr(&mut self, stderr: Stdio) {
self.stderr = Some(stderr);
}
pub fn get_program(&self) -> &OsStr {
panic!("unsupported")
&self.program
}
pub fn get_args(&self) -> CommandArgs<'_> {
CommandArgs { _p: PhantomData }
let mut iter = self.args.iter();
iter.next();
CommandArgs { iter }
}
pub fn get_envs(&self) -> CommandEnvs<'_> {
@ -67,7 +96,7 @@ impl Command {
}
pub fn get_current_dir(&self) -> Option<&Path> {
None
self.cwd.as_ref().map(|cs| Path::new(cs))
}
pub fn spawn(
@ -91,31 +120,83 @@ impl From<AnonPipe> for Stdio {
impl From<io::Stdout> for Stdio {
fn from(_: io::Stdout) -> Stdio {
// FIXME: This is wrong.
// Instead, the Stdio we have here should be a unit struct.
panic!("unsupported")
Stdio::ParentStdout
}
}
impl From<io::Stderr> for Stdio {
fn from(_: io::Stderr) -> Stdio {
// FIXME: This is wrong.
// Instead, the Stdio we have here should be a unit struct.
panic!("unsupported")
Stdio::ParentStderr
}
}
impl From<File> for Stdio {
fn from(_file: File) -> Stdio {
// FIXME: This is wrong.
// Instead, the Stdio we have here should be a unit struct.
panic!("unsupported")
fn from(file: File) -> Stdio {
Stdio::InheritFile(file)
}
}
impl fmt::Debug for Command {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
// show all attributes
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
let mut debug_command = f.debug_struct("Command");
debug_command.field("program", &self.program).field("args", &self.args);
if !self.env.is_unchanged() {
debug_command.field("env", &self.env);
}
if self.cwd.is_some() {
debug_command.field("cwd", &self.cwd);
}
if self.stdin.is_some() {
debug_command.field("stdin", &self.stdin);
}
if self.stdout.is_some() {
debug_command.field("stdout", &self.stdout);
}
if self.stderr.is_some() {
debug_command.field("stderr", &self.stderr);
}
debug_command.finish()
} else {
if let Some(ref cwd) = self.cwd {
write!(f, "cd {cwd:?} && ")?;
}
if self.env.does_clear() {
write!(f, "env -i ")?;
// Altered env vars will be printed next, that should exactly work as expected.
} else {
// Removed env vars need the command to be wrapped in `env`.
let mut any_removed = false;
for (key, value_opt) in self.get_envs() {
if value_opt.is_none() {
if !any_removed {
write!(f, "env ")?;
any_removed = true;
}
write!(f, "-u {} ", key.to_string_lossy())?;
}
}
}
// Altered env vars can just be added in front of the program.
for (key, value_opt) in self.get_envs() {
if let Some(value) = value_opt {
write!(f, "{}={value:?} ", key.to_string_lossy())?;
}
}
if self.program != self.args[0] {
write!(f, "[{:?}] ", self.program)?;
}
write!(f, "{:?}", self.args[0])?;
for arg in &self.args[1..] {
write!(f, " {:?}", arg)?;
}
Ok(())
}
}
}
@ -217,23 +298,30 @@ impl Process {
}
pub struct CommandArgs<'a> {
_p: PhantomData<&'a ()>,
iter: crate::slice::Iter<'a, OsString>,
}
impl<'a> Iterator for CommandArgs<'a> {
type Item = &'a OsStr;
fn next(&mut self) -> Option<&'a OsStr> {
None
self.iter.next().map(|os| &**os)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(0))
self.iter.size_hint()
}
}
impl<'a> ExactSizeIterator for CommandArgs<'a> {}
impl<'a> ExactSizeIterator for CommandArgs<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
impl<'a> fmt::Debug for CommandArgs<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().finish()
f.debug_list().entries(self.iter.clone()).finish()
}
}

View File

@ -294,8 +294,7 @@ fn elision_suggestions(
let span = cx
.sess()
.source_map()
.span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace())
.unwrap_or(usage.ident.span);
.span_extend_while_whitespace(usage.ident.span);
(span, String::new())
},

View File

@ -819,6 +819,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"needs-dynamic-linking",
"needs-git-hash",
"needs-llvm-components",
"needs-matching-clang",
"needs-profiler-support",
"needs-relocation-model-pic",
"needs-run-enabled",

View File

@ -86,6 +86,18 @@ impl Rustc {
self
}
/// This flag defers LTO optimizations to the linker.
pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self {
self.cmd.arg(format!("-Clinker-plugin-lto={option}"));
self
}
/// Specify what happens when the code panics.
pub fn panic(&mut self, option: &str) -> &mut Self {
self.cmd.arg(format!("-Cpanic={option}"));
self
}
/// Specify number of codegen units
pub fn codegen_units(&mut self, units: usize) -> &mut Self {
self.cmd.arg(format!("-Ccodegen-units={units}"));
@ -183,6 +195,18 @@ impl Rustc {
output
}
#[track_caller]
pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();
let output = self.cmd.output().unwrap();
if output.status.code().unwrap() != code {
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
}
output
}
/// Inspect what the underlying [`Command`] is up to the current construction.
pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self {
f(&self.cmd);

View File

@ -1,4 +1,5 @@
use std::env;
use std::ffi::OsStr;
use std::path::Path;
use std::process::{Command, Output};
@ -58,9 +59,8 @@ impl Rustdoc {
self
}
/// Fallback argument provider. Consider adding meaningfully named methods instead of using
/// this method.
pub fn arg(&mut self, arg: &str) -> &mut Self {
/// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`.
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.cmd.arg(arg);
self
}
@ -77,4 +77,16 @@ impl Rustdoc {
}
output
}
#[track_caller]
pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output {
let caller_location = std::panic::Location::caller();
let caller_line_number = caller_location.line();
let output = self.cmd.output().unwrap();
if output.status.code().unwrap() != code {
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
}
output
}
}

View File

@ -59,7 +59,6 @@ run-make/emit/Makefile
run-make/env-dep-info/Makefile
run-make/error-found-staticlib-instead-crate/Makefile
run-make/error-writing-dependencies/Makefile
run-make/exit-code/Makefile
run-make/export-executable-symbols/Makefile
run-make/extern-diff-internal-name/Makefile
run-make/extern-flag-disambiguates/Makefile

View File

@ -18,7 +18,7 @@ const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1722;
const ROOT_ENTRY_LIMIT: usize = 861;
const ROOT_ENTRY_LIMIT: usize = 859;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files

View File

@ -0,0 +1,20 @@
//@ revisions:riscv64gc riscv32gc riscv32imac
//@[riscv64gc] compile-flags: --target=riscv64gc-unknown-linux-gnu
//@[riscv64gc] needs-llvm-components: riscv
// riscv64gc: !{i32 1, !"target-abi", !"lp64d"}
//@[riscv32gc] compile-flags: --target=riscv32gc-unknown-linux-musl
//@[riscv32gc] needs-llvm-components: riscv
// riscv32gc: !{i32 1, !"target-abi", !"ilp32d"}
//@[riscv32imac] compile-flags: --target=riscv32imac-unknown-none-elf
//@[riscv32imac] needs-llvm-components: riscv
// riscv32imac-NOT: !"target-abi"
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_core]
#[lang = "sized"]
trait Sized {}

View File

@ -0,0 +1,5 @@
extern void hello();
void _start() {
hello();
}

View File

@ -0,0 +1,9 @@
#![allow(internal_features)]
#![feature(no_core, lang_items)]
#![no_core]
#[lang = "sized"]
trait Sized {}
#[no_mangle]
pub fn hello() {}

View File

@ -0,0 +1,74 @@
//! Make sure that cross-language LTO works on riscv targets,
//! which requires extra abi metadata to be emitted.
//@ needs-matching-clang
//@ needs-llvm-components riscv
extern crate run_make_support;
use run_make_support::{bin_name, rustc, tmp_dir};
use std::{
env,
path::PathBuf,
process::{Command, Output},
str,
};
fn handle_failed_output(output: Output) {
eprintln!("output status: `{}`", output.status);
eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
std::process::exit(1)
}
fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: bool) {
eprintln!("Checking target {target}");
// Rust part
rustc()
.input("riscv-xlto.rs")
.crate_type("rlib")
.target(target)
.panic("abort")
.linker_plugin_lto("on")
.run();
// C part
let clang = env::var("CLANG").unwrap();
let mut cmd = Command::new(clang);
let executable = tmp_dir().join("riscv-xlto");
cmd.arg("-target")
.arg(clang_target)
.arg(format!("-march={carch}"))
.arg(format!("-flto=thin"))
.arg(format!("-fuse-ld=lld"))
.arg("-nostdlib")
.arg("-o")
.arg(&executable)
.arg("cstart.c")
.arg(tmp_dir().join("libriscv_xlto.rlib"));
eprintln!("{cmd:?}");
let output = cmd.output().unwrap();
if !output.status.success() {
handle_failed_output(output);
}
// Check that the built binary has correct float abi
let llvm_readobj =
PathBuf::from(env::var("LLVM_BIN_DIR").unwrap()).join(bin_name("llvm-readobj"));
let mut cmd = Command::new(llvm_readobj);
cmd.arg("--file-header").arg(executable);
eprintln!("{cmd:?}");
let output = cmd.output().unwrap();
if output.status.success() {
assert!(
!(is_double_float
^ dbg!(str::from_utf8(&output.stdout).unwrap())
.contains("EF_RISCV_FLOAT_ABI_DOUBLE"))
)
} else {
handle_failed_output(output);
}
}
fn main() {
check_target("riscv64gc-unknown-linux-gnu", "riscv64-linux-gnu", "rv64gc", true);
check_target("riscv64imac-unknown-none-elf", "riscv64-unknown-elf", "rv64imac", false);
check_target("riscv32imac-unknown-none-elf", "riscv32-unknown-elf", "rv32imac", false);
check_target("riscv32gc-unknown-linux-gnu", "riscv32-linux-gnu", "rv32gc", true);
}

View File

@ -1,12 +0,0 @@
# ignore-cross-compile
include ../tools.mk
all:
$(RUSTC) success.rs; [ $$? -eq 0 ]
$(RUSTC) --invalid-arg-foo; [ $$? -eq 1 ]
$(RUSTC) compile-error.rs; [ $$? -eq 1 ]
RUSTC_ICE=0 $(RUSTC) -Ztreat-err-as-bug compile-error.rs; [ $$? -eq 101 ]
$(RUSTDOC) -o $(TMPDIR)/exit-code success.rs; [ $$? -eq 0 ]
$(RUSTDOC) --invalid-arg-foo; [ $$? -eq 1 ]
$(RUSTDOC) compile-error.rs; [ $$? -eq 1 ]
$(RUSTDOC) lint-failure.rs; [ $$? -eq 1 ]

View File

@ -0,0 +1,43 @@
// Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations
extern crate run_make_support;
use run_make_support::{rustc, rustdoc, tmp_dir};
fn main() {
rustc()
.arg("success.rs")
.run();
rustc()
.arg("--invalid-arg-foo")
.run_fail_assert_exit_code(1);
rustc()
.arg("compile-error.rs")
.run_fail_assert_exit_code(1);
rustc()
.env("RUSTC_ICE", "0")
.arg("-Ztreat-err-as-bug")
.arg("compile-error.rs")
.run_fail_assert_exit_code(101);
rustdoc()
.arg("success.rs")
.arg("-o")
.arg(tmp_dir().join("exit-code"))
.run();
rustdoc()
.arg("--invalid-arg-foo")
.run_fail_assert_exit_code(1);
rustdoc()
.arg("compile-error.rs")
.run_fail_assert_exit_code(1);
rustdoc()
.arg("lint-failure.rs")
.run_fail_assert_exit_code(1);
}

View File

@ -12,6 +12,8 @@ error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md
note: please make sure that you have updated to the latest nightly
note: rustc {version} running on {platform}
query stack during panic:

View File

@ -4,6 +4,8 @@ error: internal compiler error[E0080]: evaluation of constant value failed
LL | const X: i32 = 1 / 0;
| ^^^^^ attempt to divide `1_i32` by zero
note: please make sure that you have updated to the latest nightly
query stack during panic:
#0 [eval_to_allocation_raw] const-evaluating + checking `X`
#1 [eval_to_const_value_raw] simplifying constant for the type system `X`

View File

@ -1,2 +0,0 @@
fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); }
//~^ ERROR cannot find value `this_does_nothing_what_the` in this scope

View File

@ -1,9 +0,0 @@
error[E0425]: cannot find value `this_does_nothing_what_the` in this scope
--> $DIR/does-nothing.rs:1:32
|
LL | fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`.

View File

@ -20,6 +20,8 @@ error: the compiler unexpectedly panicked. this is a bug.
query stack during panic:
#0 [resolver_for_lowering_raw] getting the resolver for lowering
end of query stack

View File

@ -30,6 +30,8 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md
note: please make sure that you have updated to the latest nightly
note: rustc $VERSION running on $TARGET
note: compiler flags: ... -Z ui-testing ... -Z track-diagnostics

View File

@ -0,0 +1,11 @@
// Ensures our "remove semicolon" suggestion isn't hardcoded with a character width,
// in case it was accidentally mixed up with a greek question mark.
// issue: rust-lang/rust#123607
pub fn square(num: i32) -> i32 {
//~^ ERROR mismatched types
num * num;
//~^ ERROR unknown start of token
}
fn main() {}

View File

@ -0,0 +1,25 @@
error: unknown start of token: \u{37e}
--> $DIR/remove-semi-but-confused-char.rs:7:14
|
LL | num * num;
| ^
|
help: Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not
|
LL | num * num;
| ~
error[E0308]: mismatched types
--> $DIR/remove-semi-but-confused-char.rs:5:28
|
LL | pub fn square(num: i32) -> i32 {
| ------ ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
LL |
LL | num * num;
| - help: remove this semicolon to return this value
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.