Auto merge of #155796 - JonathanBrouwer:rollup-uKXw9ZB, r=JonathanBrouwer

Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#146181 (Add intrinsic for launch-sized workgroup memory on GPUs)
 - rust-lang/rust#154803 (Fix ICE from cfg_attr_trace )
 - rust-lang/rust#155065 (Error on invalid macho section specifier)
 - rust-lang/rust#155485 (Add an edge-case test for `--remap-path-prefix` for `rustc` & `rustdoc`)
 - rust-lang/rust#155659 (cleanup, restructure and merge `tests/ui/deriving` into `tests/ui/derives`)
 - rust-lang/rust#155676 ( Reject implementing const Drop for types that are not const `Destruct` already)
 - rust-lang/rust#155696 (Add a higher-level API for parsing attributes)
 - rust-lang/rust#155769 (triagebot.toml: Ping Enselic when tests/debuginfo/basic-stepping.rs changes)
 - rust-lang/rust#155783 (Do not suggest internal cfg trace attributes)
This commit is contained in:
bors
2026-04-25 21:08:31 +00:00
176 changed files with 937 additions and 571 deletions
+3
View File
@@ -1753,6 +1753,9 @@ pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize>
impl AddressSpace {
/// LLVM's `0` address space.
pub const ZERO: Self = AddressSpace(0);
/// The address space for workgroup memory on nvptx and amdgpu.
/// See e.g. the `gpu_launch_sized_workgroup_mem` intrinsic for details.
pub const GPU_WORKGROUP: Self = AddressSpace(3);
}
/// How many scalable vectors are in a `BackendRepr::ScalableVector`?
@@ -81,7 +81,7 @@ fn parse_unstable<S: Stage>(
) -> impl IntoIterator<Item = Symbol> {
let mut res = Vec::new();
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
cx.emit_err(session_diagnostics::ExpectsFeatureList {
span: cx.attr_span,
name: symbol.to_ident_string(),
@@ -44,13 +44,9 @@ pub fn parse_cfg<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let Some(single) = list.single() else {
let Some(single) = list.as_single() else {
let target = cx.target;
let mut adcx = cx.adcx();
if list.is_empty() {
@@ -93,7 +89,7 @@ pub fn parse_cfg_entry<S: Stage>(
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
ArgParser::List(list) => match meta.path().word_sym() {
Some(sym::not) => {
let Some(single) = list.single() else {
let Some(single) = list.as_single() else {
return Err(cx.adcx().expected_single_argument(list.span, list.len()));
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
@@ -136,7 +132,7 @@ fn parse_cfg_entry_version<S: Stage>(
meta_span: Span,
) -> Result<CfgEntry, ErrorGuaranteed> {
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
let Some(version) = list.single() else {
let Some(version) = list.as_single() else {
return Err(
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span })
);
@@ -24,7 +24,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
Some(sym::size) => OptimizeAttr::Size,
@@ -75,7 +75,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let arg = cx.single_element_list(args, cx.attr_span)?;
let arg = cx.expect_single_element_list(args, cx.attr_span)?;
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
@@ -371,8 +371,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
let used_by = match args {
ArgParser::NoArgs => UsedBy::Default,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
let Some(l) = cx.expect_single(list) else {
return;
};
@@ -463,9 +462,7 @@ fn parse_tf_attribute<S: Stage>(
args: &ArgParser,
) -> impl IntoIterator<Item = (Symbol, Span)> {
let mut features = Vec::new();
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return features;
};
if list.is_empty() {
@@ -588,11 +585,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut on_set = SanitizerSet::empty();
let mut off_set = SanitizerSet::empty();
@@ -719,11 +712,7 @@ impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(meta_item_list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let meta_item_list = cx.expect_list(args, cx.attr_span)?;
let mut prefix = None;
let mut entry = None;
@@ -12,11 +12,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
&[sym::rustc_confusables],
template!(List: &[r#""name1", "name2", ..."#]),
|this, cx, args| {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return;
};
let Some(list) = cx.expect_list(args, cx.attr_span) else { return };
if list.is_empty() {
cx.emit_err(EmptyConfusables { span: cx.attr_span });
@@ -314,9 +314,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return Vec::new();
};
@@ -362,9 +360,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return Vec::new();
};
@@ -20,7 +20,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(mi) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), None);
return None;
@@ -344,7 +344,7 @@ fn parse_directive_items<'p, S: Stage>(
}
(Mode::RustcOnUnimplemented, sym::on) => {
if is_root {
let items = or_malformed!(item.args().list()?);
let items = or_malformed!(item.args().as_list()?);
let mut iter = items.mixed();
let condition: &MetaItemOrLitParser = match iter.next() {
Some(c) => c,
@@ -554,7 +554,7 @@ fn parse_predicate(input: &MetaItemOrLitParser) -> Result<Predicate, InvalidOnCl
sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)),
sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)),
sym::not => {
if let Some(single) = mis.single() {
if let Some(single) = mis.as_single() {
Ok(Predicate::Not(Box::new(parse_predicate(single)?)))
} else {
Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span })
@@ -199,7 +199,7 @@ fn parse_single_test_doc_attr_item<S: Stage>(
self.attribute.no_crate_inject = Some(path.span())
}
Some(sym::attr) => {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
@@ -587,7 +587,7 @@ macro_rules! string_arg_and_crate_level {
}),
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
Some(sym::test) => {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|dcx, level| DocTestTakesList.into_diag(dcx, level),
@@ -37,10 +37,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
match args {
ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let l = cx.expect_single(list)?;
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
@@ -78,10 +75,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let l = cx.expect_single(list)?;
let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
cx.adcx().expected_string_literal(l.span(), l.lit());
@@ -19,7 +19,7 @@ impl<S: Stage> SingleAttributeParser<S> for InstructionSetParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
const POSSIBLE_SYMBOLS: &[Symbol] = &[sym::arm_a32, sym::arm_t32];
const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32];
let maybe_meta_item = cx.single_element_list(args, cx.attr_span)?;
let maybe_meta_item = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(meta_item) = maybe_meta_item.meta_item() else {
cx.adcx().expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS);
@@ -16,8 +16,9 @@
use crate::session_diagnostics::{
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
};
pub(crate) struct LinkNameParser;
@@ -390,7 +391,7 @@ fn parse_link_cfg<S: Stage>(
cx.adcx().duplicate_key(item.span(), sym::cfg);
return true;
}
let Some(link_cfg) = cx.single_element_list(item.args(), item.span()) else {
let Some(link_cfg) = cx.expect_single_element_list(item.args(), item.span()) else {
return true;
};
if !features.link_cfg() {
@@ -462,6 +463,29 @@ fn parse_link_import_name_type<S: Stage>(
pub(crate) struct LinkSectionParser;
fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> {
let mut parts = name.as_str().split(',').map(|s| s.trim());
// The segment can be empty.
let _segment = parts.next();
// But the section is required.
let section = match parts.next() {
None | Some("") => return Err(InvalidMachoSectionReason::MissingSection),
Some(section) => section,
};
if section.len() > 16 {
return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() });
}
// LLVM also checks the other components of the section specifier, but that logic is hard to
// keep in sync. We skip it here for now, assuming that if you got that far you'll be able
// to interpret the LLVM errors.
Ok(())
}
impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
const PATH: &[Symbol] = &[sym::link_section];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
@@ -495,6 +519,18 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
return None;
}
// We (currently) only validate macho section specifiers.
match cx.sess.target.binary_format {
BinaryFormat::MachO => match check_link_section_macho(name) {
Ok(()) => {}
Err(reason) => {
cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason });
return None;
}
},
BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {}
}
Some(LinkSection { name, span: cx.attr_span })
}
}
@@ -142,7 +142,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
let local_inner_macros = match args {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let Some(l) = list.as_single() else {
cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
};
@@ -174,7 +174,7 @@ impl<S: Stage> SingleAttributeParser<S> for CollapseDebugInfoParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(mi) = single.meta_item() else {
cx.adcx().expected_not_literal(single.span());
return None;
@@ -57,7 +57,7 @@ fn parse_derive_like<S: Stage>(
args: &ArgParser,
trait_name_mandatory: bool,
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
// For #[rustc_builtin_macro], it is permitted to leave out the trait name
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
@@ -101,10 +101,7 @@ fn parse_derive_like<S: Stage>(
cx.adcx().expected_specific_argument(attrs.span(), &[sym::attributes]);
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.adcx().expected_list(attrs.span(), attr_list.args());
return None;
};
let attr_list = cx.expect_list(attr_list.args(), attrs.span())?;
// Parse item in `attributes(...)` argument
for attr in attr_list.mixed() {
@@ -22,11 +22,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut dialect = None;
let mut phase = None;
@@ -32,9 +32,7 @@ fn extend(
) -> impl IntoIterator<Item = Self::Item> {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return reprs;
};
@@ -197,7 +195,7 @@ fn parse_repr_align<S: Stage>(
) -> Option<ReprAttr> {
use AlignKind::*;
let Some(align) = list.single() else {
let Some(align) = list.as_single() else {
match align_kind {
Packed => {
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
@@ -296,8 +294,7 @@ fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParse
cx.adcx().expected_list(attr_span, args);
}
ArgParser::List(list) => {
let Some(align) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
let Some(align) = cx.expect_single(list) else {
return;
};
@@ -96,9 +96,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(items) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(items) = cx.expect_list(args, cx.attr_span) else {
return vec![];
};
@@ -30,11 +30,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcMustImplementOneOfParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const TEMPLATE: AttributeTemplate = template!(List: &["function1, function2, ..."]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let span = cx.attr_span;
cx.adcx().expected_list(span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut fn_names = ThinVec::new();
@@ -130,11 +126,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLegacyConstGenericsParser {
const TEMPLATE: AttributeTemplate = template!(List: &["N"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let ArgParser::List(meta_items) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let meta_items = cx.expect_list(args, cx.attr_span)?;
let mut parsed_indexes = ThinVec::new();
let mut errored = false;
@@ -185,7 +177,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLintOptDenyFieldAccessParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]);
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let arg = cx.single_element_list(args, cx.attr_span)?;
let arg = cx.expect_single_element_list(args, cx.attr_span)?;
let MetaItemOrLitParser::Lit(MetaItemLit { kind: LitKind::Str(lint_message, _), .. }) = arg
else {
@@ -210,11 +202,7 @@ fn parse_cgu_fields<S: Stage>(
args: &ArgParser,
accepts_kind: bool,
) -> Option<(Symbol, Symbol, Option<CguKind>)> {
let Some(args) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let args = cx.expect_list(args, cx.attr_span)?;
let mut cfg = None::<(Symbol, Span)>;
let mut module = None::<(Symbol, Span)>;
@@ -359,7 +347,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDeprecatedSafe2024Parser {
const TEMPLATE: AttributeTemplate = template!(List: &[r#"audit_that = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(arg) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), None);
@@ -418,11 +406,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcNeverTypeOptionsParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut fallback = None::<Ident>;
let mut diverging_block_default = None::<Ident>;
@@ -703,9 +687,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return ThinVec::new();
};
@@ -825,11 +807,8 @@ fn extend(
if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
}
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut except = None;
let mut loaded_from_disk = None;
let mut cfg = None;
@@ -926,11 +905,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
match args {
ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)),
ArgParser::List(list) => {
let Some(item) = list.single() else {
let attr_span = cx.attr_span;
cx.adcx().expected_single_argument(attr_span, list.len());
return None;
};
let item = cx.expect_single(list)?;
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
@@ -988,7 +963,7 @@ fn extend(
if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
}
let item = cx.single_element_list(args, cx.attr_span)?;
let item = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
@@ -311,11 +311,7 @@ pub(crate) fn parse_stability<S: Stage>(
let mut feature = None;
let mut since = None;
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let param_span = param.span();
@@ -383,11 +379,7 @@ pub(crate) fn parse_unstability<S: Stage>(
let mut implied_by = None;
let mut old_name = None;
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let Some(param) = param.meta_item() else {
@@ -503,11 +495,7 @@ fn extend(
return None;
}
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let Some(param) = param.meta_item() else {
@@ -28,7 +28,8 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
Some(str_value)
}
ArgParser::List(list) => {
let help = list.single().and_then(|item| item.meta_item()).and_then(|item| {
let help =
list.as_single().and_then(|item| item.meta_item()).and_then(|item| {
item.args().no_args().ok()?;
Some(item.path().to_string())
});
@@ -71,10 +72,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
Some(str_value)
}
ArgParser::List(list) => {
let Some(single) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let single = cx.expect_single(list)?;
let Some(single) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), Some(sym::expected));
return None;
@@ -140,17 +138,13 @@ impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(args) = args.list() else {
let Some(args) = args.as_list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::assert_eq, sym::debug]);
return None;
};
let Some(arg) = args.single() else {
let attr_span = cx.attr_span;
cx.adcx().expected_single_argument(attr_span, args.len());
return None;
};
let arg = cx.expect_single(args)?;
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
@@ -203,7 +197,7 @@ impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(meta) = single.meta_item() else {
cx.adcx().expected_not_literal(single.span());
@@ -17,11 +17,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcSkipDuringMethodDispatchParser
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let mut array = false;
let mut boxed_slice = false;
let Some(args) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let args = cx.expect_list(args, cx.attr_span)?;
if args.is_empty() {
cx.adcx().expected_at_least_one_argument(args.span);
return None;
@@ -41,7 +41,7 @@ pub(crate) fn parse_single_integer<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> Option<u128> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(lit) = single.lit() else {
cx.adcx().expected_integer_literal(single.span());
return None;
+49 -3
View File
@@ -61,7 +61,7 @@
use crate::attributes::traits::*;
use crate::attributes::transparency::*;
use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, MetaItemOrLitParser, RefPathParser};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, RefPathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions,
ParsedDescription,
@@ -554,7 +554,7 @@ pub(crate) fn adcx(&mut self) -> AttributeDiagnosticContext<'_, 'f, 'sess, S> {
///
/// The provided span is used as a fallback for diagnostic generation in case `arg` does not
/// contain any. It should be the span of the node that contains `arg`.
pub(crate) fn single_element_list<'arg>(
pub(crate) fn expect_single_element_list<'arg>(
&mut self,
arg: &'arg ArgParser,
span: Span,
@@ -564,13 +564,59 @@ pub(crate) fn single_element_list<'arg>(
return None;
};
let Some(single) = l.single() else {
let Some(single) = l.as_single() else {
self.adcx().expected_single_argument(l.span, l.len());
return None;
};
Some(single)
}
/// Asserts that an [`ArgParser`] is a list and returns it, or emits an error and returns
/// `None`.
///
/// Some examples:
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
///
/// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`]. That
/// allows using `?` when the attribute parsing function allows it. You may still want to use
/// [`ArgParser::as_list`] for the following reasons:
///
/// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]).
/// - The attribute can be parsed in multiple ways and it does not make sense to emit an error.
pub(crate) fn expect_list<'arg>(
&mut self,
args: &'arg ArgParser,
span: Span,
) -> Option<&'arg MetaItemListParser> {
let list = args.as_list();
if list.is_none() {
self.adcx().expected_list(span, args);
}
list
}
/// Asserts that a [`MetaItemListParser`] contains a single element and returns it, or emits an
/// error and returns `None`.
///
/// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`].
/// That allows using `?` to early return. You may still want to use
/// [`MetaItemListParser::as_single`] for the following reasons:
///
/// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]).
/// - The attribute can be parsed in multiple ways and it does not make sense to emit an error.
pub(crate) fn expect_single<'arg>(
&mut self,
list: &'arg MetaItemListParser,
) -> Option<&'arg MetaItemOrLitParser> {
let single = list.as_single();
if single.is_none() {
self.adcx().expected_single_argument(list.span, list.len());
}
single
}
}
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
+4 -3
View File
@@ -176,7 +176,7 @@ pub fn from_attr_args<'sess>(
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
pub fn list(&self) -> Option<&MetaItemListParser> {
pub fn as_list(&self) -> Option<&MetaItemListParser> {
match self {
Self::List(l) => Some(l),
Self::NameValue(_) | Self::NoArgs => None,
@@ -255,6 +255,7 @@ pub fn meta_item(&self) -> Option<&MetaItemParser> {
}
}
// FIXME(scrabsha): once #155696 is merged, update this and mention the higher-level APIs.
/// Utility that deconstructs a MetaItem into usable parts.
///
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
@@ -263,7 +264,7 @@ pub fn meta_item(&self) -> Option<&MetaItemParser> {
/// MetaItems consist of some path, and some args. The args could be empty. In other words:
///
/// - `name` -> args are empty
/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the parentheses
/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
/// `= value` part
///
@@ -694,7 +695,7 @@ pub fn is_empty(&self) -> bool {
/// Returns Some if the list contains only a single element.
///
/// Inside the Some is the parser to parse this single element.
pub fn single(&self) -> Option<&MetaItemOrLitParser> {
pub fn as_single(&self) -> Option<&MetaItemOrLitParser> {
let mut iter = self.mixed();
iter.next().filter(|_| iter.next().is_none())
}
@@ -1137,3 +1137,22 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature {
#[label("the stability attribute annotates this item")]
pub item_span: Span,
}
#[derive(Diagnostic)]
#[diag("invalid Mach-O section specifier")]
pub(crate) struct InvalidMachoSection {
#[primary_span]
#[label("not a valid Mach-O section specifier")]
pub name_span: Span,
#[subdiagnostic]
pub reason: InvalidMachoSectionReason,
}
#[derive(Subdiagnostic)]
pub(crate) enum InvalidMachoSectionReason {
#[note("a Mach-O section specifier requires a segment and a section, separated by a comma")]
#[help("an example of a valid Mach-O section specifier is `__TEXT,__cstring`")]
MissingSection,
#[note("section name `{$section}` is longer than 16 bytes")]
SectionTooLong { section: String },
}
@@ -14,6 +14,7 @@
use std::borrow::Borrow;
use itertools::Itertools;
use rustc_abi::AddressSpace;
use rustc_codegen_ssa::traits::{MiscCodegenMethods, TypeMembershipCodegenMethods};
use rustc_data_structures::fx::FxIndexSet;
use rustc_middle::ty::{Instance, Ty};
@@ -104,6 +105,28 @@ pub(crate) fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value {
)
}
}
/// Declare a global value in a specific address space.
///
/// If theres a value with the same name already declared, the function will
/// return its Value instead.
pub(crate) fn declare_global_in_addrspace(
&self,
name: &str,
ty: &'ll Type,
addr_space: AddressSpace,
) -> &'ll Value {
debug!("declare_global(name={name:?}, addrspace={addr_space:?})");
unsafe {
llvm::LLVMRustGetOrInsertGlobalInAddrspace(
(**self).borrow().llmod,
name.as_c_char_ptr(),
name.len(),
ty,
addr_space.0,
)
}
}
}
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
+45 -4
View File
@@ -3,8 +3,8 @@
use std::{assert_matches, iter, ptr};
use rustc_abi::{
Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, Size,
WrappingRange,
AddressSpace, Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive,
Size, WrappingRange,
};
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
@@ -178,6 +178,7 @@ fn codegen_intrinsic_call(
span: Span,
) -> Result<(), ty::Instance<'tcx>> {
let tcx = self.tcx;
let llvm_version = crate::llvm_util::get_version();
let name = tcx.item_name(instance.def_id());
let fn_args = instance.args;
@@ -194,7 +195,7 @@ fn codegen_intrinsic_call(
| sym::maximum_number_nsz_f64
| sym::maximum_number_nsz_f128
// Need at least LLVM 22 for `min/maximumnum` to not crash LLVM.
if crate::llvm_util::get_version() >= (22, 0, 0) =>
if llvm_version >= (22, 0, 0) =>
{
let intrinsic_name = if name.as_str().starts_with("min") {
"llvm.minimumnum"
@@ -420,7 +421,7 @@ fn codegen_intrinsic_call(
}
// FIXME move into the branch below when LLVM 22 is the lowest version we support.
sym::carryless_mul if crate::llvm_util::get_version() >= (22, 0, 0) => {
sym::carryless_mul if llvm_version >= (22, 0, 0) => {
let ty = args[0].layout.ty;
if !ty.is_integral() {
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
@@ -620,6 +621,46 @@ fn codegen_intrinsic_call(
return Ok(());
}
sym::gpu_launch_sized_workgroup_mem => {
// Generate an anonymous global per call, with these properties:
// 1. The global is in the address space for workgroup memory
// 2. It is an `external` global
// 3. It is correctly aligned for the pointee `T`
// All instances of extern addrspace(gpu_workgroup) globals are merged in the LLVM backend.
// The name is irrelevant.
// See https://docs.nvidia.com/cuda/cuda-c-programming-guide/#shared
let name = if llvm_version < (23, 0, 0) && tcx.sess.target.arch == Arch::Nvptx64 {
// The auto-assigned name for extern shared globals in the nvptx backend does
// not compile in ptxas. Workaround this issue by assigning a name.
// Fixed in LLVM 23.
"gpu_launch_sized_workgroup_mem"
} else {
""
};
let global = self.declare_global_in_addrspace(
name,
self.type_array(self.type_i8(), 0),
AddressSpace::GPU_WORKGROUP,
);
let ty::RawPtr(inner_ty, _) = result.layout.ty.kind() else { unreachable!() };
// The alignment of the global is used to specify the *minimum* alignment that
// must be obeyed by the GPU runtime.
// When multiple of these global variables are used by a kernel, the maximum alignment is taken.
// See https://github.com/llvm/llvm-project/blob/a271d07488a85ce677674bbe8101b10efff58c95/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp#L821
let alignment = self.align_of(*inner_ty).bytes() as u32;
unsafe {
// FIXME Workaround the above issue by taking maximum alignment if the global existed
if tcx.sess.target.arch == Arch::Nvptx64 {
if alignment > llvm::LLVMGetAlignment(global) {
llvm::LLVMSetAlignment(global, alignment);
}
} else {
llvm::LLVMSetAlignment(global, alignment);
}
}
self.cx().const_pointercast(global, self.type_ptr())
}
sym::amdgpu_dispatch_ptr => {
let val = self.call_intrinsic("llvm.amdgcn.dispatch.ptr", &[], &[]);
// Relying on `LLVMBuildPointerCast` to produce an addrspacecast
@@ -2003,6 +2003,13 @@ pub(crate) fn LLVMRustGetOrInsertGlobal<'a>(
NameLen: size_t,
T: &'a Type,
) -> &'a Value;
pub(crate) fn LLVMRustGetOrInsertGlobalInAddrspace<'a>(
M: &'a Module,
Name: *const c_char,
NameLen: size_t,
T: &'a Type,
AddressSpace: c_uint,
) -> &'a Value;
pub(crate) fn LLVMRustGetNamedValue(
M: &Module,
Name: *const c_char,
@@ -111,6 +111,7 @@ pub fn codegen_intrinsic_call(
sym::abort
| sym::unreachable
| sym::cold_path
| sym::gpu_launch_sized_workgroup_mem
| sym::breakpoint
| sym::amdgpu_dispatch_ptr
| sym::assert_zero_valid
@@ -1098,8 +1098,8 @@ pub enum AttributeKind {
/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
LinkSection {
name: Symbol,
span: Span,
name: Symbol,
},
/// Represents `#[linkage]`.
+9
View File
@@ -1304,6 +1304,15 @@ pub fn is_parsed_attr(&self) -> bool {
Attribute::Unparsed(_) => false,
}
}
pub fn is_prefix_attr_for_suggestions(&self) -> bool {
match self {
Attribute::Unparsed(attr) => attr.span.desugaring_kind().is_none(),
// Other parsed attributes that can appear on expressions originate from source and
// should make suggestions treat the expression like a prefixed form.
Attribute::Parsed(_) => true,
}
}
}
impl AttributeExt for Attribute {
@@ -11,7 +11,7 @@
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::span_bug;
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode};
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCtxt};
@@ -65,6 +65,8 @@ pub(crate) fn check_drop_impl(
adt_to_impl_args,
)?;
ensure_all_fields_are_const_destruct(tcx, drop_impl_did, adt_def.did())?;
ensure_impl_predicates_are_implied_by_item_defn(
tcx,
drop_impl_did,
@@ -173,6 +175,64 @@ fn ensure_impl_params_and_item_params_correspond<'tcx>(
Err(err.emit())
}
fn ensure_all_fields_are_const_destruct<'tcx>(
tcx: TyCtxt<'tcx>,
impl_def_id: LocalDefId,
adt_def_id: DefId,
) -> Result<(), ErrorGuaranteed> {
if !tcx.is_conditionally_const(impl_def_id) {
return Ok(());
}
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let impl_span = tcx.def_span(impl_def_id.to_def_id());
let env =
ty::EarlyBinder::bind(tcx.param_env(impl_def_id)).instantiate_identity().skip_norm_wip();
let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
let destruct_trait = tcx.lang_items().destruct_trait().unwrap();
for field in tcx.adt_def(adt_def_id).all_fields() {
let field_ty = field.ty(tcx, args);
let cause = traits::ObligationCause::new(
tcx.def_span(field.did),
impl_def_id,
ObligationCauseCode::Misc,
);
ocx.register_obligation(traits::Obligation::new(
tcx,
cause,
env,
ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref: ty::TraitRef::new(tcx, destruct_trait, [field_ty]),
constness: ty::BoundConstness::Maybe,
}),
));
}
ocx.evaluate_obligations_error_on_ambiguity()
.into_iter()
.map(|error| {
let ty::ClauseKind::HostEffect(eff) =
error.root_obligation.predicate.expect_clause().kind().no_bound_vars().unwrap()
else {
unreachable!()
};
let field_ty = eff.trait_ref.self_ty();
let diag = struct_span_code_err!(
tcx.dcx(),
error.root_obligation.cause.span,
E0367,
"`{field_ty}` does not implement `[const] Destruct`",
)
.with_span_note(impl_span, "required for this `Drop` impl");
if field_ty.has_param() {
// FIXME: suggest adding `[const] Destruct` by teaching
// `suggest_restricting_param_bound` about const traits.
}
Err(diag.emit())
})
.collect()
}
/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be
/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are
/// implied by the ADT being well formed.
@@ -130,6 +130,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::forget
| sym::frem_algebraic
| sym::fsub_algebraic
| sym::gpu_launch_sized_workgroup_mem
| sym::is_val_statically_known
| sym::log2f16
| sym::log2f32
@@ -297,6 +298,7 @@ pub(crate) fn check_intrinsic_type(
sym::field_offset => (1, 0, vec![], tcx.types.usize),
sym::rustc_peek => (1, 0, vec![param(0)], param(0)),
sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()),
sym::gpu_launch_sized_workgroup_mem => (1, 0, vec![], Ty::new_mut_ptr(tcx, param(0))),
sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => {
(1, 0, vec![], tcx.types.unit)
}
+1 -19
View File
@@ -57,25 +57,7 @@
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
let has_attr = |id: HirId| -> bool {
for attr in self.tcx.hir_attrs(id) {
// For the purpose of rendering suggestions, disregard attributes
// that originate from desugaring of any kind. For example, `x?`
// desugars to `#[allow(unreachable_code)] match ...`. Failing to
// ignore the prefix attribute in the desugaring would cause this
// suggestion:
//
// let y: u32 = x?.try_into().unwrap();
// ++++++++++++++++++++
//
// to be rendered as:
//
// let y: u32 = (x?).try_into().unwrap();
// + +++++++++++++++++++++
if attr.span().desugaring_kind().is_none() {
return true;
}
}
false
self.tcx.hir_attrs(id).iter().any(hir::Attribute::is_prefix_attr_for_suggestions)
};
// Special case: range expressions are desugared to struct literals in HIR,
+1 -6
View File
@@ -845,12 +845,7 @@ pub fn get_associated_type(
/// be used for pretty-printing HIR by rustc_hir_pretty.
pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
let has_attr = |id: hir::HirId| -> bool {
for attr in self.tcx.hir_attrs(id) {
if attr.span().desugaring_kind().is_none() {
return true;
}
}
false
self.tcx.hir_attrs(id).iter().any(hir::Attribute::is_prefix_attr_for_suggestions)
};
expr.precedence(&has_attr)
}
@@ -299,10 +299,12 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
.getCallee());
}
extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M,
const char *Name,
size_t NameLen,
LLVMTypeRef Ty) {
// Get the global variable with the given name if it exists or create a new
// external global.
extern "C" LLVMValueRef
LLVMRustGetOrInsertGlobalInAddrspace(LLVMModuleRef M, const char *Name,
size_t NameLen, LLVMTypeRef Ty,
unsigned int AddressSpace) {
Module *Mod = unwrap(M);
auto NameRef = StringRef(Name, NameLen);
@@ -313,10 +315,24 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M,
GlobalVariable *GV = Mod->getGlobalVariable(NameRef, true);
if (!GV)
GV = new GlobalVariable(*Mod, unwrap(Ty), false,
GlobalValue::ExternalLinkage, nullptr, NameRef);
GlobalValue::ExternalLinkage, nullptr, NameRef,
nullptr, GlobalValue::NotThreadLocal, AddressSpace);
return wrap(GV);
}
// Get the global variable with the given name if it exists or create a new
// external global.
extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M,
const char *Name,
size_t NameLen,
LLVMTypeRef Ty) {
Module *Mod = unwrap(M);
unsigned int AddressSpace =
Mod->getDataLayout().getDefaultGlobalsAddressSpace();
return LLVMRustGetOrInsertGlobalInAddrspace(M, Name, NameLen, Ty,
AddressSpace);
}
// Must match the layout of `rustc_codegen_llvm::llvm::ffi::AttributeKind`.
enum class LLVMRustAttributeKind {
AlwaysInline = 0,
@@ -1281,6 +1281,11 @@ pub(crate) fn add_scope_set_candidates(
suggestions.extend(
BUILTIN_ATTRIBUTES
.iter()
// These trace attributes are compiler-generated and have
// deliberately invalid names.
.filter(|attr| {
!matches!(attr.name, sym::cfg_trace | sym::cfg_attr_trace)
})
.map(|attr| TypoSuggestion::typo_from_name(attr.name, res)),
);
}
+1
View File
@@ -1033,6 +1033,7 @@
global_asm,
global_registration,
globs,
gpu_launch_sized_workgroup_mem,
gt,
guard,
guard_patterns,
+45
View File
@@ -5,6 +5,51 @@
#![unstable(feature = "gpu_intrinsics", issue = "none")]
/// Returns the pointer to workgroup memory allocated at launch-time on GPUs.
///
/// Workgroup memory is a memory region that is shared between all threads in
/// the same workgroup. It is faster to access than other memory but pointers do not
/// work outside the workgroup where they were obtained.
/// Workgroup memory can be allocated statically or after compilation, when
/// launching a gpu-kernel. `gpu_launch_sized_workgroup_mem` returns the pointer to
/// the memory that is allocated at launch-time.
/// The size of this memory can differ between launches of a gpu-kernel, depending on
/// what is specified at launch-time.
/// However, the alignment is fixed by the kernel itself, at compile-time.
///
/// The returned pointer is the start of the workgroup memory region that is
/// allocated at launch-time.
/// All calls to `gpu_launch_sized_workgroup_mem` in a workgroup, independent of the
/// generic type, return the same address, so alias the same memory.
/// The returned pointer is aligned by at least the alignment of `T`.
///
/// If `gpu_launch_sized_workgroup_mem` is invoked multiple times with different
/// types that have different alignment, then you may only rely on the resulting
/// pointer having the alignment of `T` after a call to `gpu_launch_sized_workgroup_mem::<T>`
/// has occurred in the current program execution.
///
/// # Safety
///
/// The pointer is safe to dereference from the start (the returned pointer) up to the
/// size of workgroup memory that was specified when launching the current gpu-kernel.
/// This allocated size is not related in any way to `T`.
///
/// The user must take care of synchronizing access to workgroup memory between
/// threads in a workgroup. The usual data race requirements apply.
///
/// # Other APIs
///
/// CUDA and HIP call this dynamic shared memory, shared between threads in a block.
/// OpenCL and SYCL call this local memory, shared between threads in a work-group.
/// GLSL calls this shared memory, shared between invocations in a work group.
/// DirectX calls this groupshared memory, shared between threads in a thread-group.
#[must_use = "returns a pointer that does nothing unless used"]
#[rustc_intrinsic]
#[rustc_nounwind]
#[unstable(feature = "gpu_launch_sized_workgroup_mem", issue = "135513")]
#[cfg(any(target_arch = "amdgpu", target_arch = "nvptx64"))]
pub fn gpu_launch_sized_workgroup_mem<T>() -> *mut T;
/// Returns a pointer to the HSA kernel dispatch packet.
///
/// A `gpu-kernel` on amdgpu is always launched through a kernel dispatch packet.
+4
View File
@@ -222,6 +222,10 @@ fn should_ignore(line: &str) -> bool {
|| static_regex!(
"\\s*//@ \\!?(count|files|has|has-dir|hasraw|matches|matchesraw|snapshot)\\s.*"
).is_match(line)
// Matching for FileCheck checks
|| static_regex!(
"\\s*// [a-zA-Z0-9-_]*:\\s.*"
).is_match(line)
}
/// Returns `true` if `line` is allowed to be longer than the normal limit.
@@ -0,0 +1,41 @@
// Checks that the GPU intrinsic to get launch-sized workgroup memory works
// and correctly aligns the `external addrspace(...) global`s over multiple calls.
//@ revisions: amdgpu nvptx-pre-llvm-23 nvptx-post-llvm-23
//@ compile-flags: --crate-type=rlib -Copt-level=1
//
//@ [amdgpu] compile-flags: --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900
//@ [amdgpu] needs-llvm-components: amdgpu
//@ [nvptx-pre-llvm-23] compile-flags: --target nvptx64-nvidia-cuda
//@ [nvptx-pre-llvm-23] needs-llvm-components: nvptx
//@ [nvptx-pre-llvm-23] max-llvm-major-version: 22
//@ [nvptx-post-llvm-23] compile-flags: --target nvptx64-nvidia-cuda
//@ [nvptx-post-llvm-23] needs-llvm-components: nvptx
//@ [nvptx-post-llvm-23] min-llvm-version: 23
//@ add-minicore
#![feature(intrinsics, no_core, rustc_attrs)]
#![no_core]
extern crate minicore;
#[rustc_intrinsic]
#[rustc_nounwind]
fn gpu_launch_sized_workgroup_mem<T>() -> *mut T;
// amdgpu-DAG: @[[SMALL:[^ ]+]] = external addrspace(3) global [0 x i8], align 4
// amdgpu-DAG: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8
// amdgpu: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[SMALL]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) }
// nvptx-pre-llvm-23: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8
// nvptx-pre-llvm-23: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) }
// nvptx-post-llvm-23-DAG: @[[SMALL:[^ ]+]] = external addrspace(3) global [0 x i8], align 4
// nvptx-post-llvm-23-DAG: @[[BIG:[^ ]+]] = external addrspace(3) global [0 x i8], align 8
// nvptx-post-llvm-23: ret { ptr, ptr } { ptr addrspacecast (ptr addrspace(3) @[[SMALL]] to ptr), ptr addrspacecast (ptr addrspace(3) @[[BIG]] to ptr) }
#[unsafe(no_mangle)]
pub fn fun() -> (*mut i32, *mut f64) {
let small = gpu_launch_sized_workgroup_mem::<i32>();
let big = gpu_launch_sized_workgroup_mem::<f64>(); // Increase alignment to 8
(small, big)
}
+9 -9
View File
@@ -3,14 +3,14 @@
#![crate_type = "lib"]
// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section ".test_one"
// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section "__TEST,one"
#[no_mangle]
#[link_section = ".test_one"]
#[link_section = "__TEST,one"]
#[cfg(target_endian = "little")]
pub static VAR1: u32 = 1;
#[no_mangle]
#[link_section = ".test_one"]
#[link_section = "__TEST,one"]
#[cfg(target_endian = "big")]
pub static VAR1: u32 = 0x01000000;
@@ -19,17 +19,17 @@ pub enum E {
B(f32),
}
// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two"
// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section "__TEST,two"
#[no_mangle]
#[link_section = ".test_two"]
#[link_section = "__TEST,two"]
pub static VAR2: E = E::A(666);
// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section ".test_three"
// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section "__TEST,three"
#[no_mangle]
#[link_section = ".test_three"]
#[link_section = "__TEST,three"]
pub static VAR3: E = E::B(1.);
// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section ".test_four" {
// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" {
#[no_mangle]
#[link_section = ".test_four"]
#[link_section = "__TEST,four"]
pub fn fn1() {}
@@ -22,7 +22,7 @@
//@[thumb] needs-llvm-components: arm
#![crate_type = "lib"]
#![feature(no_core, lang_items, rustc_attrs)]
#![feature(no_core, lang_items, rustc_attrs, cfg_target_object_format)]
#![no_core]
extern crate minicore;
@@ -170,14 +170,17 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
}
// linux,linux_no_function_sections: .pushsection .text.some_different_name,\22ax\22, @progbits
// macos: .pushsection .text.some_different_name,regular,pure_instructions
// macos: .pushsection __TEXT,different,regular,pure_instructions
// win_x86_msvc,win_x86_gnu,win_i686_gnu: .section .text.some_different_name,\22xr\22
// win_x86_gnu_function_sections: .section .text.some_different_name,\22xr\22
// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits
// CHECK-LABEL: test_link_section:
#[no_mangle]
#[unsafe(naked)]
#[link_section = ".text.some_different_name"]
#[link_section = cfg_select!(
target_object_format = "mach-o" => "__TEXT,different",
_ => ".text.some_different_name"
)]
pub extern "C" fn test_link_section() {
cfg_select! {
all(target_arch = "arm", target_feature = "thumb-mode") => {
@@ -0,0 +1,51 @@
//! This test checks multiple edge-case of `--remap-path-prefix`.
//!
//! It tests:
//! - `=` sign in FROM path
//! - multiple path remappings
//! - multiple conflicting path remappings
//@ ignore-windows (does not support directories with = sign)
use std::path::Path;
use run_make_support::{
CompletedProcess, assert_contains, assert_not_contains, cwd, rfs, run_in_tmpdir, rustc, rustdoc,
};
fn main() {
run_in_tmpdir(|| {
let out_dir = cwd();
// Create a directory with an `=` sign
let eq_dir = out_dir.join("path=with=equal");
rfs::create_dir_all(&eq_dir);
let src_path = eq_dir.join("lib.rs");
rfs::write(&src_path, "pub fn broken_func() { ");
// Use multiple remap args and conflicting remappings
let remap_args = [
format!("--remap-path-prefix={}={}", eq_dir.display(), "REMAPPED_DIR"),
format!("--remap-path-prefix={}={}", eq_dir.display(), "REMAPPED_DIR2"),
];
fn run_test(cmd: impl FnOnce() -> CompletedProcess) {
let output = cmd();
let stderr = output.stderr_utf8();
// Checks the diagnostic output
assert_contains(&stderr, "REMAPPED_DIR2/lib.rs");
assert_not_contains(&stderr, "REMAPPED_DIR/");
assert_not_contains(&stderr, "path=with=equal");
};
// Test with rustc
run_test(|| rustc().input(&src_path).args(&remap_args).run_fail());
// Test with rustdoc
run_test(|| {
rustdoc().input(&src_path).arg("-Zunstable-options").args(&remap_args).run_fail()
});
});
}
@@ -9,6 +9,6 @@ pub extern "C" fn f() {}
#[export_name = "bar"]
pub extern "C" fn g() {}
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]'
#[link_section = ".text"]
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = "__TEXT,__text")]'
#[link_section = "__TEXT,__text"]
pub extern "C" fn example() {}
+1
View File
@@ -1,4 +1,5 @@
//@ edition: 2024
//@ only-linux
#![crate_name = "foo"]
//@ has foo/fn.f.html '//*[@class="code-attribute"]' '#[unsafe(no_mangle)]'
@@ -9,7 +9,7 @@
pub use attributes::no_mangle;
//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \
// '#[unsafe(link_section = ".here")]'
// '#[unsafe(link_section = "__TEXT,__here")]'
pub use attributes::link_section;
//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \
@@ -1,7 +1,7 @@
#[unsafe(no_mangle)]
pub fn no_mangle() {}
#[unsafe(link_section = ".here")]
#[unsafe(link_section = "__TEXT,__here")]
pub fn link_section() {}
#[unsafe(export_name = "exonym")]
@@ -2,6 +2,6 @@
#![no_std]
//@ count "$.index[?(@.name=='example')].attrs[*]" 1
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '".text"'
#[link_section = ".text"]
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '"__TEXT,__text"'
#[link_section = "__TEXT,__text"]
pub extern "C" fn example() {}
@@ -5,6 +5,6 @@
// However, the unsafe qualification is not shown by rustdoc.
//@ count "$.index[?(@.name=='example')].attrs[*]" 1
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '".text"'
#[unsafe(link_section = ".text")]
//@ is "$.index[?(@.name=='example')].attrs[*].link_section" '"__TEXT,__text"'
#[unsafe(link_section = "__TEXT,__text")]
pub extern "C" fn example() {}
-4
View File
@@ -412,10 +412,6 @@ Tests for quality of diagnostics involving suppression of cascading errors in so
Tests for built-in derive macros (`Debug`, `Clone`, etc.) when used in conjunction with built-in `#[derive(..)]` attributes.
## `tests/ui/deriving/`: Derive Macro
**FIXME**: Coalesce with `tests/ui/derives`.
## `tests/ui/dest-prop/` Destination Propagation
**FIXME**: Contains a single test for the `DestProp` mir-opt, should probably be rehomed.
+5 -2
View File
@@ -3,7 +3,7 @@
//@ ignore-spirv
//@ reference: attributes.codegen.naked.body
#![feature(asm_unwind, linkage, rustc_attrs)]
#![feature(asm_unwind, linkage, rustc_attrs, cfg_target_object_format)]
#![crate_type = "lib"]
use std::arch::{asm, naked_asm};
@@ -200,7 +200,10 @@ pub extern "C" fn compatible_must_use_attributes() -> u64 {
}
#[export_name = "exported_function_name"]
#[link_section = ".custom_section"]
#[link_section = cfg_select!(
target_object_format = "mach-o" => "__TEXT,__custom",
_ => ".custom",
)]
#[unsafe(naked)]
pub extern "C" fn compatible_ffi_attributes_1() {
naked_asm!("", options(raw));
+1 -1
View File
@@ -27,7 +27,7 @@ fn main() {
#[link_name = "x"]
//~^ WARN attribute cannot be used on macro calls
//~| WARN previously accepted
#[link_section = "x"]
#[link_section = "__TEXT,__text"]
//~^ WARN attribute cannot be used on macro calls
//~| WARN previously accepted
#[link_ordinal(42)]
+2 -2
View File
@@ -78,8 +78,8 @@ LL | #[link_name = "x"]
warning: `#[link_section]` attribute cannot be used on macro calls
--> $DIR/attr-on-mac-call.rs:30:5
|
LL | #[link_section = "x"]
| ^^^^^^^^^^^^^^^^^^^^^
LL | #[link_section = "__TEXT,__text"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= help: `#[link_section]` can be applied to functions and statics
@@ -7,7 +7,7 @@ trait Test {
//~^ ERROR cannot be used on required trait methods [unused_attributes]
//~| WARN previously accepted
fn method1(&self);
#[link_section = ".text"]
#[link_section = "__TEXT,__text"]
//~^ ERROR cannot be used on required trait methods [unused_attributes]
//~| WARN previously accepted
fn method2(&self);
@@ -15,8 +15,8 @@ LL | #![deny(unused_attributes)]
error: `#[link_section]` attribute cannot be used on required trait methods
--> $DIR/codegen_attr_on_required_trait_method.rs:10:5
|
LL | #[link_section = ".text"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[link_section = "__TEXT,__text"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= help: `#[link_section]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks
@@ -0,0 +1,11 @@
fn main() {
let _x = 30;
#[cfg_attr(, (cc))] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `,`
_x //~ ERROR mismatched types
}
fn inline_case() {
let _x = 30;
#[inline] //~ ERROR `#[inline]` attribute cannot be used on expressions
_x //~ ERROR mismatched types
}
@@ -0,0 +1,42 @@
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `,`
--> $DIR/cfg-attr-parsed-span-issue-154801.rs:3:16
|
LL | #[cfg_attr(, (cc))]
| ^
|
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
help: must be of the form
|
LL - #[cfg_attr(, (cc))]
LL + #[cfg_attr(predicate, attr1, attr2, ...)]
|
error: `#[inline]` attribute cannot be used on expressions
--> $DIR/cfg-attr-parsed-span-issue-154801.rs:9:5
|
LL | #[inline]
| ^^^^^^^^^
|
= help: `#[inline]` can only be applied to functions
error[E0308]: mismatched types
--> $DIR/cfg-attr-parsed-span-issue-154801.rs:4:5
|
LL | fn main() {
| - expected `()` because of default return type
...
LL | _x
| ^^ expected `()`, found integer
error[E0308]: mismatched types
--> $DIR/cfg-attr-parsed-span-issue-154801.rs:10:5
|
LL | fn inline_case() {
| - help: try adding a return type: `-> i32`
...
LL | _x
| ^^ expected `()`, found integer
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0308`.
@@ -0,0 +1,32 @@
#![feature(const_trait_impl)]
#![feature(const_destruct)]
use std::marker::Destruct;
struct NotConstDrop;
impl Drop for NotConstDrop {
fn drop(&mut self) {}
}
struct ConstDrop(NotConstDrop);
//~^ ERROR: `NotConstDrop` does not implement `[const] Destruct`
impl const Drop for ConstDrop {
fn drop(&mut self) {}
}
struct ConstDrop2<T>(T);
//~^ ERROR: `T` does not implement `[const] Destruct`
impl<T> const Drop for ConstDrop2<T> {
fn drop(&mut self) {}
}
struct ConstDrop3<T>(T);
impl<T: [const] Destruct> const Drop for ConstDrop3<T> {
fn drop(&mut self) {}
}
fn main() {}
@@ -0,0 +1,27 @@
error[E0367]: `NotConstDrop` does not implement `[const] Destruct`
--> $DIR/drop-impl-nonconst-drop-field.rs:12:18
|
LL | struct ConstDrop(NotConstDrop);
| ^^^^^^^^^^^^
|
note: required for this `Drop` impl
--> $DIR/drop-impl-nonconst-drop-field.rs:15:1
|
LL | impl const Drop for ConstDrop {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0367]: `T` does not implement `[const] Destruct`
--> $DIR/drop-impl-nonconst-drop-field.rs:19:22
|
LL | struct ConstDrop2<T>(T);
| ^
|
note: required for this `Drop` impl
--> $DIR/drop-impl-nonconst-drop-field.rs:22:1
|
LL | impl<T> const Drop for ConstDrop2<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0367`.
@@ -1,16 +0,0 @@
#![crate_type = "lib"]
pub trait Decoder {
type Error;
fn read_enum<T, F>(&mut self, name: &str, f: F) -> Result<T, Self::Error>
where F: FnOnce(&mut Self) -> Result<T, Self::Error>;
fn read_enum_variant<T, F>(&mut self, names: &[&str], f: F)
-> Result<T, Self::Error>
where F: FnMut(&mut Self, usize) -> Result<T, Self::Error>;
}
pub trait Decodable: Sized {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error>;
}

Some files were not shown because too many files have changed in this diff Show More