Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ impl<'a> AstValidator<'a> {
match fn_ctxt {
FnCtxt::Foreign => return,
FnCtxt::Free | FnCtxt::Assoc(_) => {
if !self.sess.target.arch.supports_c_variadic_definitions() {
if !self.sess.target.supports_c_variadic_definitions() {
self.dcx().emit_err(errors::CVariadicNotSupported {
variadic_span: variadic_param.span,
target: &*self.sess.target.llvm_target,
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_codegen_llvm/src/va_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,9 +1186,10 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
// Clang uses the LLVM implementation for these architectures.
bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
}
Arch::Other(_) => {
// For custom targets, use the LLVM va_arg instruction as a fallback.
bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
Arch::Other(ref arch) => {
// Just to be safe we error out explicitly here, instead of crossing our fingers that
// the default LLVM implementation has the correct behavior for this target.
bug!("c-variadic functions are not currently implemented for custom target {arch}")
}
}
}
79 changes: 65 additions & 14 deletions compiler/rustc_lint/src/lifetime_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,26 +387,24 @@ fn emit_mismatch_diagnostic<'tcx>(
build_mismatch_suggestion(info.lifetime.ident.as_str(), &suggest_change_to_explicit_bound)
});

let is_bound_static = bound_lifetime.is_some_and(|info| info.lifetime.is_static());

tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion, ?is_bound_static);
tracing::debug!(?bound_lifetime, ?explicit_bound_suggestion);

let should_suggest_mixed =
// Do we have a mixed case?
(saw_a_reference && saw_a_path) &&
// Is there anything to change?
(!suggest_change_to_mixed_implicit.is_empty() ||
!suggest_change_to_mixed_explicit_anonymous.is_empty()) &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
// If we have a named lifetime, prefer consistent naming.
bound_lifetime.is_none();

let mixed_suggestion = should_suggest_mixed.then(|| {
let implicit_suggestions = make_implicit_suggestions(&suggest_change_to_mixed_implicit);

let explicit_anonymous_suggestions = suggest_change_to_mixed_explicit_anonymous
.iter()
.map(|info| info.suggestion("'_"))
.collect();
let explicit_anonymous_suggestions = build_mismatch_suggestions_for_lifetime(
"'_",
&suggest_change_to_mixed_explicit_anonymous,
);

lints::MismatchedLifetimeSyntaxesSuggestion::Mixed {
implicit_suggestions,
Expand All @@ -426,8 +424,8 @@ fn emit_mismatch_diagnostic<'tcx>(
!suggest_change_to_implicit.is_empty() &&
// We never want to hide the lifetime in a path (or similar).
allow_suggesting_implicit &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
// If we have a named lifetime, prefer consistent naming.
bound_lifetime.is_none();

let implicit_suggestion = should_suggest_implicit.then(|| {
let suggestions = make_implicit_suggestions(&suggest_change_to_implicit);
Expand All @@ -448,8 +446,10 @@ fn emit_mismatch_diagnostic<'tcx>(
let should_suggest_explicit_anonymous =
// Is there anything to change?
!suggest_change_to_explicit_anonymous.is_empty() &&
// If we have `'static`, we don't want to remove it.
!is_bound_static;
// If we already have a mixed suggestion, avoid overlapping alternatives.
mixed_suggestion.is_none() &&
// If we have a named lifetime, prefer consistent naming.
bound_lifetime.is_none();

let explicit_anonymous_suggestion = should_suggest_explicit_anonymous
.then(|| build_mismatch_suggestion("'_", &suggest_change_to_explicit_anonymous));
Expand Down Expand Up @@ -483,7 +483,7 @@ fn build_mismatch_suggestion(
) -> lints::MismatchedLifetimeSyntaxesSuggestion {
let lifetime_name = lifetime_name.to_owned();

let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
let suggestions = build_mismatch_suggestions_for_lifetime(&lifetime_name, infos);

lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
lifetime_name,
Expand All @@ -492,6 +492,57 @@ fn build_mismatch_suggestion(
}
}

fn build_mismatch_suggestions_for_lifetime(
lifetime_name: &str,
infos: &[&Info<'_>],
) -> Vec<(Span, String)> {
use hir::{AngleBrackets, LifetimeSource, LifetimeSyntax};

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum PathSuggestionKind {
Missing,
Empty,
Full,
}

let mut suggestions = Vec::new();
let mut path_counts: FxIndexMap<(hir::HirId, PathSuggestionKind), (Span, usize)> =
FxIndexMap::default();

for info in infos {
let lifetime = info.lifetime;
if matches!(lifetime.syntax, LifetimeSyntax::Implicit) {
if let LifetimeSource::Path { angle_brackets } = lifetime.source {
let (span, kind) = match angle_brackets {
AngleBrackets::Missing => {
(lifetime.ident.span.shrink_to_hi(), PathSuggestionKind::Missing)
}
AngleBrackets::Empty => (lifetime.ident.span, PathSuggestionKind::Empty),
AngleBrackets::Full => (lifetime.ident.span, PathSuggestionKind::Full),
};
let entry = path_counts.entry((info.ty.hir_id, kind)).or_insert((span, 0));
entry.1 += 1;
continue;
}
}
suggestions.push(info.suggestion(lifetime_name));
}

for ((_ty_hir_id, kind), (span, count)) in path_counts {
let repeated = std::iter::repeat(lifetime_name).take(count).collect::<Vec<_>>().join(", ");

let suggestion = match kind {
PathSuggestionKind::Missing => format!("<{repeated}>"),
PathSuggestionKind::Empty => repeated,
PathSuggestionKind::Full => format!("{repeated}, "),
};

suggestions.push((span, suggestion));
}

suggestions
}

#[derive(Debug)]
struct Info<'tcx> {
lifetime: &'tcx hir::Lifetime,
Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::num::NonZero;

use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::codes::*;
use rustc_errors::formatting::DiagMessageAddArg;
use rustc_errors::{
Expand Down Expand Up @@ -3231,8 +3232,23 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MismatchedLifetimeSyntaxes
diag.span_label(s, msg!("the lifetime is named here"));
}

let mut hidden_output_counts: FxIndexMap<Span, usize> = FxIndexMap::default();
for s in self.outputs.hidden {
diag.span_label(s, msg!("the same lifetime is hidden here"));
*hidden_output_counts.entry(s).or_insert(0) += 1;
}
for (span, count) in hidden_output_counts {
let label = msg!(
"the same {$count ->
[one] lifetime
*[other] lifetimes
} {$count ->
[one] is
*[other] are
} hidden here"
)
.arg("count", count)
.format();
diag.span_label(span, label);
}
for s in self.outputs.elided {
diag.span_label(s, msg!("the same lifetime is elided here"));
Expand Down
16 changes: 8 additions & 8 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
unsafe { transmute_unchecked::<MaybeUninit<T::Storage>, T>(data) }
}

// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate
// impls for fat reference types.
impl<T> Erasable for &'_ T {
type Storage = [u8; size_of::<&'_ ()>()];
}
Expand All @@ -119,12 +117,14 @@ impl<T> Erasable for &'_ [T] {
type Storage = [u8; size_of::<&'_ [()]>()];
}

impl<T> Erasable for &'_ ty::List<T> {
type Storage = [u8; size_of::<&'_ ty::List<()>>()];
}

impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> {
type Storage = [u8; size_of::<&'_ ty::ListWithCachedTypeInfo<()>>()];
// Note: this impl does not overlap with the impl for `&'_ T` above because `RawList` is unsized
// and does not satisfy the implicit `T: Sized` bound.
//
// Furthermore, even if that implicit bound was removed (by adding `T: ?Sized`) this impl still
// wouldn't overlap because `?Sized` is equivalent to `MetaSized` and `RawList` does not satisfy
// `MetaSized` because it contains an extern type.
impl<H, T> Erasable for &'_ ty::RawList<H, T> {
type Storage = [u8; size_of::<&'_ ty::RawList<(), ()>>()];
}

impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
Expand Down
38 changes: 30 additions & 8 deletions compiler/rustc_middle/src/ty/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,18 @@ pub type List<T> = RawList<(), T>;
#[repr(C)]
pub struct RawList<H, T> {
skel: ListSkeleton<H, T>,
opaque: OpaqueListContents,

// `List`/`RawList` is variable-sized. So we want it to be an unsized
// type because calling `size_of::<List<Foo>>` would be dangerous.
//
// We also want `&List`/`&RawList` to be thin pointers.
//
// A field with an extern type is a hacky way to achieve this. (See
// https://github.com/rust-lang/rust/pull/154399#issuecomment-4157036415
// for some discussion.) This field is never directly manipulated because
// `RawList` instances are created with manual memory layout in
// `from_arena`.
_extern_ty: ExternTy,
}

/// A [`RawList`] without the unsized tail. This type is used for layout computation
Expand All @@ -47,7 +58,8 @@ struct ListSkeleton<H, T> {
header: H,
len: usize,
/// Although this claims to be a zero-length array, in practice `len`
/// elements are actually present.
/// elements are actually present. This is achieved with manual memory
/// layout in `from_arena`. See also the comment on `RawList::_extern_ty`.
data: [T; 0],
}

Expand All @@ -58,9 +70,7 @@ impl<T> Default for &List<T> {
}

unsafe extern "C" {
/// A dummy type used to force `List` to be unsized while not requiring
/// references to it be wide pointers.
type OpaqueListContents;
type ExternTy;
}

impl<H, T> RawList<H, T> {
Expand Down Expand Up @@ -257,12 +267,13 @@ impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> {

unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {}

// We need this since `List` uses extern type `OpaqueListContents`.
// We need this because `List` uses the extern type `ExternTy`.
unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {}

// Safety:
// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo opaque tail,
// thus aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo the
// `_extern_ty` field (which is never instantiated in practice). Therefore,
// aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
unsafe impl<H, T> Aligned for RawList<H, T> {
#[cfg(bootstrap)]
const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>();
Expand Down Expand Up @@ -310,3 +321,14 @@ impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
}
}
}

#[cfg(target_pointer_width = "64")]
mod size_asserts {
use rustc_data_structures::static_assert_size;

use super::*;
// tidy-alphabetical-start
static_assert_size!(&List<u32>, 8); // thin pointer
static_assert_size!(&RawList<u8, u32>, 8); // thin pointer
// tidy-alphabetical-end
}
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub use self::context::{
};
pub use self::fold::*;
pub use self::instance::{Instance, InstanceKind, ReifyReason};
pub(crate) use self::list::RawList;
pub use self::list::{List, ListWithCachedTypeInfo};
pub use self::opaque_types::OpaqueTypeKey;
pub use self::pattern::{Pattern, PatternKind};
Expand Down
45 changes: 27 additions & 18 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1952,24 +1952,6 @@ impl Arch {
}
}

pub fn supports_c_variadic_definitions(&self) -> bool {
use Arch::*;

match self {
// These targets just do not support c-variadic definitions.
Bpf | SpirV => false,

// We don't know if the target supports c-variadic definitions, but we don't want
// to needlessly restrict custom target.json configurations.
Other(_) => true,

AArch64 | AmdGpu | Arm | Arm64EC | Avr | CSky | Hexagon | LoongArch32 | LoongArch64
| M68k | Mips | Mips32r6 | Mips64 | Mips64r6 | Msp430 | Nvptx64 | PowerPC
| PowerPC64 | RiscV32 | RiscV64 | S390x | Sparc | Sparc64 | Wasm32 | Wasm64 | X86
| X86_64 | Xtensa => true,
}
}

/// Whether `#[rustc_scalable_vector]` is supported for a target architecture
pub fn supports_scalable_vectors(&self) -> bool {
use Arch::*;
Expand Down Expand Up @@ -2214,6 +2196,33 @@ impl Target {

Ok(dl)
}

pub fn supports_c_variadic_definitions(&self) -> bool {
use Arch::*;

match self.arch {
// The c-variadic ABI for this target may change in the future, per this comment in
// clang:
//
// > To be compatible with GCC's behaviors, we force arguments with
// > 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`,
// > `unsigned long long` and `double` to have 4-byte alignment. This
// > behavior may be changed when RV32E/ILP32E is ratified.
RiscV32 if self.llvm_abiname == LlvmAbi::Ilp32e => false,

// These targets just do not support c-variadic definitions.
Bpf | SpirV => false,

// We don't know how c-variadics work for this target. Using the default LLVM
// fallback implementation may work, but just to be safe we disallow this.
Other(_) => false,

AArch64 | AmdGpu | Arm | Arm64EC | Avr | CSky | Hexagon | LoongArch32 | LoongArch64
| M68k | Mips | Mips32r6 | Mips64 | Mips64r6 | Msp430 | Nvptx64 | PowerPC
| PowerPC64 | RiscV32 | RiscV64 | S390x | Sparc | Sparc64 | Wasm32 | Wasm64 | X86
| X86_64 | Xtensa => true,
}
}
}

pub trait HasTargetSpec {
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@
//!
//! While counter-intuitive, it's often the easier choice: if you do not expose a
//! <code>[Pin]<[&mut] Field></code>, you do not need to be careful about other code
//! moving out of that field, you just have to ensure is that you never create pinning
//! moving out of that field, you just have to ensure that you never create a pinning
//! reference to that field. This does of course also mean that if you decide a field does not
//! have structural pinning, you must not write [`unsafe`] code that assumes (invalidly) that the
//! field *is* structurally pinned!
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/net/connection/socket/hermit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl Socket {
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
let linger = netc::linger {
l_onoff: linger.is_some() as i32,
l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
l_linger: cmp::min(linger.unwrap_or_default().as_secs(), c_int::MAX as u64) as c_int,
};

unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) }
Expand Down
3 changes: 2 additions & 1 deletion library/std/src/sys/net/connection/socket/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ impl Socket {
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
let linger = netc::linger {
l_onoff: linger.is_some() as netc::c_int,
l_linger: linger.unwrap_or_default().as_secs() as netc::c_int,
l_linger: cmp::min(linger.unwrap_or_default().as_secs(), netc::c_int::MAX as u64)
as netc::c_int,
};

unsafe { setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) }
Expand Down
7 changes: 4 additions & 3 deletions library/std/src/sys/net/connection/socket/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,8 @@ impl Socket {
#[cfg(not(target_os = "cygwin"))]
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
let linger = libc::linger {
l_onoff: linger.is_some() as libc::c_int,
l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
l_onoff: linger.is_some() as c_int,
l_linger: cmp::min(linger.unwrap_or_default().as_secs(), c_int::MAX as u64) as c_int,
};

unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) }
Expand All @@ -443,7 +443,8 @@ impl Socket {
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
let linger = libc::linger {
l_onoff: linger.is_some() as libc::c_ushort,
l_linger: linger.unwrap_or_default().as_secs() as libc::c_ushort,
l_linger: cmp::min(linger.unwrap_or_default().as_secs(), libc::c_ushort::MAX as u64)
as libc::c_ushort,
};

unsafe { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) }
Expand Down
Loading
Loading