mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
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:
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 there’s 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> {
|
||||
|
||||
@@ -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]`.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1033,6 +1033,7 @@
|
||||
global_asm,
|
||||
global_registration,
|
||||
globs,
|
||||
gpu_launch_sized_workgroup_mem,
|
||||
gt,
|
||||
guard,
|
||||
guard_patterns,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,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() {}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user