diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0561490344d21..4def804cc1d4c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -42,12 +42,13 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; use rustc_attr_parsing::{AttributeParser, Late, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle}; +use rustc_hir::attrs::AttrResolution; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; @@ -155,6 +156,7 @@ struct LoweringContext<'a, 'hir, R> { impl<'a, 'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'a, 'hir, R> { fn new(tcx: TyCtxt<'hir>, resolver: &'a mut R) -> Self { let registered_tools = tcx.registered_tools(()); + let attr_res_map = resolver.all_attr_resolutions(); Self { tcx, resolver, @@ -209,6 +211,7 @@ impl<'a, 'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'a, 'hir, R> { tcx.sess, tcx.features(), registered_tools, + attr_res_map, Late, ), delayed_lints: Vec::new(), @@ -239,6 +242,7 @@ impl SpanLowerer { struct ResolverDelayedAstLowering<'a, 'tcx> { node_id_to_def_id: NodeMap, partial_res_map: NodeMap, + attr_res_map: FxIndexMap>>, next_node_id: NodeId, base: &'a ResolverAstLowering<'tcx>, } @@ -253,6 +257,16 @@ impl<'a, 'tcx> ResolverAstLoweringExt<'tcx> for ResolverDelayedAstLowering<'a, ' self.partial_res_map.get(&id).copied().or_else(|| self.base.get_partial_res(id)) } + fn all_attr_resolutions( + &self, + ) -> FxIndexMap>> { + let mut map = self.base.all_attr_resolutions(); + for (attr_id, resolutions) in &self.attr_res_map { + map.entry(*attr_id).or_default().extend(resolutions.iter().copied()); + } + map + } + fn get_import_res(&self, id: NodeId) -> PerNS>> { self.base.get_import_res(id) } @@ -345,6 +359,12 @@ impl<'tcx> ResolverAstLowering<'tcx> { self.partial_res_map.get(&id).copied() } + fn all_attr_resolutions( + &self, + ) -> FxIndexMap>> { + self.attr_res_map.clone() + } + /// Obtains per-namespace resolutions for `use` statement with the given `NodeId`. fn get_import_res(&self, id: NodeId) -> PerNS>> { self.import_res_map.get(&id).copied().unwrap_or_default() @@ -663,6 +683,7 @@ pub fn lower_delayed_owner(tcx: TyCtxt<'_>, def_id: LocalDefId) { let mut resolver = ResolverDelayedAstLowering { next_node_id: resolver.next_node_id, partial_res_map: Default::default(), + attr_res_map: Default::default(), node_id_to_def_id: Default::default(), base: resolver, }; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index ebb9d90eb152c..2c6edf807e7ec 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,9 +1,14 @@ use rustc_abi::{Align, Size}; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; -use rustc_hir::attrs::{IntType, ReprAttr}; +use rustc_hir::attrs::{AttrIntValue, AttrResolutionKind, AttrResolved, IntType, ReprAttr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_session::parse::feature_err; use super::prelude::*; -use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; +use crate::ShouldEmit; +use crate::session_diagnostics::{ + self, AttrConstGenericNotSupported, AttrConstPathNotConst, IncorrectReprFormatGenericCause, +}; /// Parse #[repr(...)] forms. /// @@ -100,7 +105,10 @@ fn int_type_of_word(s: Symbol) -> Option { } } -fn parse_repr(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option { +fn parse_repr( + cx: &mut AcceptContext<'_, '_, S>, + param: &MetaItemParser, +) -> Option { use ReprAttr::*; // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the @@ -122,7 +130,7 @@ fn parse_repr(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) - parse_repr_align(cx, l, param.span(), AlignKind::Align) } - (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)), + (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(AttrIntValue::Lit(1))), (Some(sym::packed), ArgParser::List(l)) => { parse_repr_align(cx, l, param.span(), AlignKind::Packed) } @@ -189,8 +197,13 @@ enum AlignKind { Align, } +enum AlignmentParseError { + Message(String), + AlreadyErrored, +} + fn parse_repr_align( - cx: &AcceptContext<'_, '_, S>, + cx: &mut AcceptContext<'_, '_, S>, list: &MetaItemListParser, param_span: Span, align_kind: AlignKind, @@ -214,31 +227,21 @@ fn parse_repr_align( return None; }; - let Some(lit) = align.lit() else { + match parse_alignment_or_const_path( + cx, + align, match align_kind { - Packed => { - cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger { - span: align.span(), - }); - } - Align => { - cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger { - span: align.span(), - }); - } - } - - return None; - }; - - match parse_alignment(&lit.kind, cx) { - Ok(literal) => Some(match align_kind { - AlignKind::Packed => ReprAttr::ReprPacked(literal), - AlignKind::Align => ReprAttr::ReprAlign(literal), + Packed => "repr(packed)", + Align => "repr(align)", + }, + ) { + Ok(value) => Some(match align_kind { + AlignKind::Packed => ReprAttr::ReprPacked(value), + AlignKind::Align => ReprAttr::ReprAlign(value), }), - Err(message) => { + Err(AlignmentParseError::Message(message)) => { cx.emit_err(session_diagnostics::InvalidReprGeneric { - span: lit.span, + span: align.span(), repr_arg: match align_kind { Packed => "packed".to_string(), Align => "align".to_string(), @@ -247,6 +250,7 @@ fn parse_repr_align( }); None } + Err(AlignmentParseError::AlreadyErrored) => None, } } @@ -281,9 +285,74 @@ fn parse_alignment( Ok(align) } +fn parse_alignment_or_const_path( + cx: &mut AcceptContext<'_, '_, S>, + arg: &MetaItemOrLitParser, + attr_name: &'static str, +) -> Result { + if let Some(lit) = arg.lit() { + return parse_alignment(&lit.kind, cx) + .map(|align| AttrIntValue::Lit(u128::from(align.bytes()))) + .map_err(AlignmentParseError::Message); + } + + let Some(meta) = arg.meta_item() else { + return Err(AlignmentParseError::Message("not an unsuffixed integer".to_string())); + }; + + if !matches!(meta.args(), ArgParser::NoArgs) { + return Err(AlignmentParseError::Message("not an unsuffixed integer".to_string())); + } + + let path_span = meta.path().span(); + let feature_enabled = cx.features_option().is_some_and(|features| features.const_attr_paths()) + || path_span.allows_unstable(sym::const_attr_paths); + + if !feature_enabled { + if matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { + return Ok(AttrIntValue::Lit(1)); + } + + feature_err( + cx.sess(), + sym::const_attr_paths, + meta.span(), + "const item paths in builtin attributes are experimental", + ) + .emit(); + return Err(AlignmentParseError::AlreadyErrored); + } + + cx.record_attr_resolution_request(AttrResolutionKind::Const, meta.path().0.clone()); + + let Some(resolution) = cx.attr_resolution(AttrResolutionKind::Const, path_span) else { + // `parse_limited(sym::repr)` runs before lowering for callers that only care whether + // `repr(packed(...))` exists at all. + if matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { + return Ok(AttrIntValue::Lit(1)); + } + return Err(AlignmentParseError::Message("not an unsuffixed integer".to_string())); + }; + + match resolution { + AttrResolved::Resolved(Res::Def(DefKind::Const { .. }, def_id)) => { + Ok(AttrIntValue::Const { def_id, span: path_span }) + } + AttrResolved::Resolved(Res::Def(DefKind::ConstParam, _)) => { + cx.emit_err(AttrConstGenericNotSupported { span: path_span, attr_name }); + Err(AlignmentParseError::AlreadyErrored) + } + AttrResolved::Resolved(res) => { + cx.emit_err(AttrConstPathNotConst { span: path_span, attr_name, thing: res.descr() }); + Err(AlignmentParseError::AlreadyErrored) + } + AttrResolved::Error => Err(AlignmentParseError::AlreadyErrored), + } +} + /// Parse #[align(N)]. #[derive(Default)] -pub(crate) struct RustcAlignParser(Option<(Align, Span)>); +pub(crate) struct RustcAlignParser(ThinVec<(AttrIntValue, Span)>); impl RustcAlignParser { const PATH: &[Symbol] = &[sym::rustc_align]; @@ -301,22 +370,15 @@ impl RustcAlignParser { return; }; - let Some(lit) = align.lit() else { - cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger { - span: align.span(), - }); - - return; - }; - - match parse_alignment(&lit.kind, cx) { - Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))), - Err(message) => { + match parse_alignment_or_const_path(cx, align, "rustc_align") { + Ok(literal) => self.0.push((literal, cx.attr_span)), + Err(AlignmentParseError::Message(message)) => { cx.emit_err(session_diagnostics::InvalidAlignmentValue { - span: lit.span, + span: align.span(), error_part: message, }); } + Err(AlignmentParseError::AlreadyErrored) => {} } } } @@ -335,8 +397,7 @@ impl AttributeParser for RustcAlignParser { ]); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { - let (align, span) = self.0?; - Some(AttributeKind::RustcAlign { align, span }) + (!self.0.is_empty()).then_some(AttributeKind::RustcAlign { aligns: self.0 }) } } @@ -358,7 +419,6 @@ impl AttributeParser for RustcAlignStaticParser { AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { - let (align, span) = self.0.0?; - Some(AttributeKind::RustcAlign { align, span }) + (!self.0.0.is_empty()).then_some(AttributeKind::RustcAlign { aligns: self.0.0 }) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 74eb1222078d0..031f92a735704 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -9,7 +9,7 @@ use private::Sealed; use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttrResolutionKind, AttributeKind}; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, HirId}; use rustc_parse::parser::Recovery; @@ -502,6 +502,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { pub(crate) fn adcx(&mut self) -> AttributeDiagnosticContext<'_, 'f, 'sess, S> { AttributeDiagnosticContext { ctx: self, custom_suggestions: Vec::new() } } + + pub(crate) fn attr_resolution( + &self, + kind: AttrResolutionKind, + path_span: Span, + ) -> Option> { + self.shared.cx.attr_resolution(self.attr_id, kind, path_span) + } + + pub(crate) fn record_attr_resolution_request( + &mut self, + kind: AttrResolutionKind, + path: rustc_ast::Path, + ) { + self.shared.cx.record_attr_resolution_request( + self.attr_id, + crate::interface::AttrResolutionRequest { kind, path }, + ); + } } impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index e9d868039380b..35d4ff0d43b22 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -2,16 +2,16 @@ use std::convert::identity; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; -use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety}; -use rustc_data_structures::fx::FxIndexSet; +use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Path, Safety}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::DiagCtxtHandle; use rustc_feature::{AttributeTemplate, Features}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttrResolution, AttrResolutionKind, AttrResolved, AttributeKind}; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; use rustc_session::Session; use rustc_session::lint::{BuiltinLintDiag, LintId}; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage}; use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState}; @@ -19,6 +19,18 @@ use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; +/// A parser-discovered attribute argument path that should be resolved later. +#[derive(Clone, Debug)] +pub struct AttrResolutionRequest { + pub kind: AttrResolutionKind, + pub path: Path, +} + +struct LimitedParseResult { + attributes: Vec, + attr_resolution_requests: FxIndexMap>, +} + /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess, S: Stage = Late> { @@ -26,6 +38,8 @@ pub struct AttributeParser<'sess, S: Stage = Late> { pub(crate) features: Option<&'sess Features>, pub(crate) sess: &'sess Session, pub(crate) stage: S, + pub(crate) attr_res_map: FxIndexMap>>, + pub(crate) attr_resolution_requests: FxIndexMap>, /// *Only* parse attributes with this symbol. /// @@ -48,8 +62,6 @@ impl<'sess> AttributeParser<'sess, Early> { /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors - /// - /// Due to this function not taking in RegisteredTools (`FxIndexSet`), *do not* use this for parsing any lint attributes pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], @@ -71,8 +83,6 @@ impl<'sess> AttributeParser<'sess, Early> { /// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors. /// Usually you want `parse_limited`, which emits no errors. - /// - /// Due to this function not taking in RegisteredTools (`FxIndexSet`), *do not* use this for parsing any lint attributes pub fn parse_limited_should_emit( sess: &'sess Session, attrs: &[ast::Attribute], @@ -82,7 +92,7 @@ impl<'sess> AttributeParser<'sess, Early> { features: Option<&'sess Features>, should_emit: ShouldEmit, ) -> Option { - let mut parsed = Self::parse_limited_all( + let mut parsed = Self::parse_limited_inner( sess, attrs, Some(sym), @@ -92,11 +102,56 @@ impl<'sess> AttributeParser<'sess, Early> { features, should_emit, None, - ); + ) + .attributes; assert!(parsed.len() <= 1); parsed.pop() } + /// This does the same parse as `parse_limited_should_emit`, but returns any late resolution + /// requests discovered while parsing the selected attribute. + pub fn parse_limited_attr_resolution_requests_should_emit( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + should_emit: ShouldEmit, + ) -> FxIndexMap> { + Self::parse_limited_inner( + sess, + attrs, + Some(sym), + Target::Crate, // Does not matter, we're not going to emit errors anyways + target_span, + target_node_id, + features, + should_emit, + None, + ) + .attr_resolution_requests + } + + pub fn parse_limited_attr_resolution_requests( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + ) -> FxIndexMap> { + Self::parse_limited_attr_resolution_requests_should_emit( + sess, + attrs, + sym, + target_span, + target_node_id, + features, + ShouldEmit::Nothing, + ) + } + /// This method allows you to parse a list of attributes *before* `rustc_ast_lowering`. /// This can be used for attributes that would be removed before `rustc_ast_lowering`, such as attributes on macro calls. /// @@ -104,9 +159,9 @@ impl<'sess> AttributeParser<'sess, Early> { /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would /// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all). /// Therefore, if `parse_only` is None, then features *must* be provided. - pub fn parse_limited_all<'a>( + pub fn parse_limited_all( sess: &'sess Session, - attrs: impl IntoIterator, + attrs: &[ast::Attribute], parse_only: Option, target: Target, target_span: Span, @@ -115,29 +170,25 @@ impl<'sess> AttributeParser<'sess, Early> { emit_errors: ShouldEmit, tools: Option<&'sess FxIndexSet>, ) -> Vec { - let mut p = Self { features, tools, parse_only, sess, stage: Early { emit_errors } }; - p.parse_attribute_list( + Self::parse_limited_inner( + sess, attrs, - target_span, + parse_only, target, - OmitDoc::Skip, - std::convert::identity, - |lint_id, span, kind| { - sess.psess.buffer_lint( - lint_id.lint, - span, - target_node_id, - BuiltinLintDiag::AttributeLint(kind), - ) - }, + target_span, + target_node_id, + features, + emit_errors, + tools, ) + .attributes } - /// This method provides the same functionality as [`parse_limited_all`](Self::parse_limited_all) except filtered, - /// making sure that only allow-listed symbols are parsed - pub fn parse_limited_all_filtered<'a>( + /// This method provides the same functionality as [`parse_limited_all`](Self::parse_limited_all) + /// except filtered, making sure that only allow-listed symbols are parsed. + pub fn parse_limited_all_filtered( sess: &'sess Session, - attrs: impl IntoIterator, + attrs: &[ast::Attribute], filter: &[Symbol], target: Target, target_span: Span, @@ -146,9 +197,11 @@ impl<'sess> AttributeParser<'sess, Early> { emit_errors: ShouldEmit, tools: &'sess FxIndexSet, ) -> Vec { + let filtered = + attrs.iter().filter(|attr| attr.has_any_name(filter)).cloned().collect::>(); Self::parse_limited_all( sess, - attrs.into_iter().filter(|attr| attr.has_any_name(filter)), + &filtered, None, target, target_span, @@ -159,6 +212,44 @@ impl<'sess> AttributeParser<'sess, Early> { ) } + fn parse_limited_inner( + sess: &'sess Session, + attrs: &[ast::Attribute], + parse_only: Option, + target: Target, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: ShouldEmit, + tools: Option<&'sess FxIndexSet>, + ) -> LimitedParseResult { + let mut p = Self { + features, + tools, + parse_only, + sess, + stage: Early { emit_errors }, + attr_res_map: Default::default(), + attr_resolution_requests: Default::default(), + }; + let attributes = p.parse_attribute_list( + attrs, + target_span, + target, + OmitDoc::Skip, + std::convert::identity, + |lint_id, span, kind| { + sess.psess.buffer_lint( + lint_id.lint, + span, + target_node_id, + BuiltinLintDiag::AttributeLint(kind), + ) + }, + ); + LimitedParseResult { attributes, attr_resolution_requests: p.attr_resolution_requests } + } + /// This method parses a single attribute, using `parse_fn`. /// This is useful if you already know what exact attribute this is, and want to parse it. pub fn parse_single( @@ -225,8 +316,15 @@ impl<'sess> AttributeParser<'sess, Early> { parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> T, template: &AttributeTemplate, ) -> T { - let mut parser = - Self { features, tools: None, parse_only: None, sess, stage: Early { emit_errors } }; + let mut parser = Self { + features, + tools: None, + parse_only: None, + sess, + stage: Early { emit_errors }, + attr_res_map: Default::default(), + attr_resolution_requests: Default::default(), + }; let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| { sess.psess.buffer_lint( lint_id.lint, @@ -238,7 +336,6 @@ impl<'sess> AttributeParser<'sess, Early> { if let Some(safety) = attr_safety { parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint) } - let attr_id = sess.psess.attr_id_generator.mk_attr_id(); let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { shared: SharedContext { cx: &mut parser, @@ -246,13 +343,13 @@ impl<'sess> AttributeParser<'sess, Early> { target, emit_lint: &mut emit_lint, }, + attr_id: sess.psess.attr_id_generator.mk_attr_id(), attr_span, inner_span, attr_style, parsed_description, template, attr_path, - attr_id, }; parse_fn(&mut cx, args) } @@ -263,9 +360,18 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { sess: &'sess Session, features: &'sess Features, tools: &'sess FxIndexSet, + attr_res_map: FxIndexMap>>, stage: S, ) -> Self { - Self { features: Some(features), tools: Some(tools), parse_only: None, sess, stage } + Self { + features: Some(features), + tools: Some(tools), + parse_only: None, + sess, + stage, + attr_res_map, + attr_resolution_requests: Default::default(), + } } pub(crate) fn sess(&self) -> &'sess Session { @@ -284,13 +390,49 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { self.sess().dcx() } + pub(crate) fn attr_resolution( + &self, + attr_id: AttrId, + kind: AttrResolutionKind, + path_span: Span, + ) -> Option> { + self.attr_res_map.get(&attr_id).and_then(|resolutions| { + let mut fallback = None; + let mut saw_multiple = false; + + for resolution in resolutions { + if resolution.kind != kind { + continue; + } + if resolution.path_span == path_span { + return Some(resolution.resolved); + } + if fallback.is_some() { + saw_multiple = true; + } else { + fallback = Some(resolution.resolved); + } + } + + if saw_multiple { None } else { fallback } + }) + } + + pub(crate) fn record_attr_resolution_request( + &mut self, + attr_id: AttrId, + request: AttrResolutionRequest, + ) { + self.attr_resolution_requests.entry(attr_id).or_default().push(request); + } + /// Parse a list of attributes. /// /// `target_span` is the span of the thing this list of attributes is applied to, /// and when `omit_doc` is set, doc attributes are filtered out. - pub fn parse_attribute_list<'a>( + pub fn parse_attribute_list( &mut self, - attrs: impl IntoIterator, + attrs: &[ast::Attribute], target_span: Span, target: Target, omit_doc: OmitDoc, @@ -306,9 +448,9 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); - let mut finalizers: Vec<&FinalizeFn> = Vec::new(); + let mut finalizers: Vec<&FinalizeFn> = Vec::with_capacity(attrs.len()); - for attr in attrs.into_iter() { + for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { if !attr.has_name(expected) { @@ -412,13 +554,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target, emit_lint: &mut emit_lint, }, + attr_id: attr.id, attr_span, inner_span: lower_span(n.item.span()), attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, attr_path: attr_path.clone(), - attr_id: attr.id, }; (accept.accept_fn)(&mut cx, &args); diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 93eb5a0c3ab73..125f27a2dbef0 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -112,5 +112,5 @@ pub use attributes::cfg::{ pub use attributes::cfg_select::*; pub use attributes::util::{is_builtin_attr, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; -pub use interface::AttributeParser; +pub use interface::{AttrResolutionRequest, AttributeParser}; pub use session_diagnostics::ParsedDescription; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index ebb217a3a1251..9740142307f4b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -191,13 +191,6 @@ pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } -#[derive(Diagnostic)] -#[diag("incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument", code = E0552)] -pub(crate) struct IncorrectReprFormatPackedExpectInteger { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("invalid representation hint: `{$name}` does not take a parenthesized argument list", code = E0552)] pub(crate) struct InvalidReprHintNoParen { @@ -246,10 +239,20 @@ pub(crate) struct IncorrectReprFormatAlignOneArg { } #[derive(Diagnostic)] -#[diag("incorrect `repr(align)` attribute format: `align` expects a literal integer as argument", code = E0693)] -pub(crate) struct IncorrectReprFormatExpectInteger { +#[diag("paths in `{$attr_name}` must refer to `const` items, not {$thing}")] +pub(crate) struct AttrConstPathNotConst { + #[primary_span] + pub span: Span, + pub attr_name: &'static str, + pub thing: &'static str, +} + +#[derive(Diagnostic)] +#[diag("generic const parameters are not supported in `{$attr_name}`")] +pub(crate) struct AttrConstGenericNotSupported { #[primary_span] pub span: Span, + pub attr_name: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 6dd4f2d6b5925..64384a524d529 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -76,7 +76,14 @@ fn process_builtin_attrs( interesting_spans.inline = Some(*span); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, - AttributeKind::RustcAlign { align, .. } => codegen_fn_attrs.alignment = Some(*align), + AttributeKind::RustcAlign { aligns } => { + for (align, _) in aligns { + if let Some(align) = tcx.eval_attr_alignment(*align, "rustc_align") { + codegen_fn_attrs.alignment = + Some(codegen_fn_attrs.alignment.map_or(align, |cur| cur.max(align))); + } + } + } AttributeKind::LinkName { name, .. } => { // FIXME Remove check for foreign functions once #[link_name] on non-foreign // functions is a hard error diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a9ad07be319c3..1a5083de5f29b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -426,6 +426,8 @@ declare_features! ( (unstable, cmse_nonsecure_entry, "1.48.0", Some(75835)), /// Allows `async {}` expressions in const contexts. (unstable, const_async_blocks, "1.53.0", Some(85368)), + /// Allows paths to const items in selected builtin attribute arguments. + (unstable, const_attr_paths, "CURRENT_RUSTC_VERSION", Some(52840)), /// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items. (unstable, const_block_items, "1.95.0", Some(149226)), /// Allows defining and calling c-variadic functions in const contexts. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index f18d5a1f190a2..d80e4d3ff7a37 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -3,7 +3,6 @@ use std::fmt; use std::path::PathBuf; pub use ReprAttr::*; -use rustc_abi::Align; pub use rustc_ast::attr::data_structures::*; use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::TypeTree; @@ -21,6 +20,7 @@ use thin_vec::ThinVec; use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; +use crate::def::Res; use crate::limit::Limit; use crate::{ DefaultBodyStability, HashIgnoredAttrId, PartialConstStability, RustcVersion, Stability, @@ -172,10 +172,47 @@ pub enum ReprAttr { ReprInt(IntType), ReprRust, ReprC, - ReprPacked(Align), + ReprPacked(AttrIntValue), ReprSimd, ReprTransparent, - ReprAlign(Align), + ReprAlign(AttrIntValue), +} + +#[derive(PartialEq, Eq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic)] +pub enum AttrIntValue { + Lit(u128), + Const { def_id: DefId, span: Span }, +} + +/// The resolution strategy a parsed builtin attribute argument expects. +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + HashStable_Generic, + Encodable, + Decodable, + PrintAttribute +)] +pub enum AttrResolutionKind { + Const, +} + +/// A resolved attribute argument path, or an error placeholder if resolution failed. +#[derive(Debug, Copy, Clone, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum AttrResolved { + Resolved(Res), + Error, +} + +/// A resolved attribute argument path produced by late resolution for a builtin attribute. +#[derive(Debug, Copy, Clone, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct AttrResolution { + pub kind: AttrResolutionKind, + pub path_span: Span, + pub resolved: AttrResolved, } pub enum TransparencyError { @@ -1409,8 +1446,7 @@ pub enum AttributeKind { /// Represents `#[align(N)]`. // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity RustcAlign { - align: Align, - span: Span, + aligns: ThinVec<(AttrIntValue, Span)>, }, /// Represents `#[rustc_allocator]` diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 811b250b6fcd6..5866ebd24d90c 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -18,7 +18,7 @@ use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; use crate::HashIgnoredAttrId; -use crate::attrs::LintInstance; +use crate::attrs::{AttrIntValue, LintInstance}; use crate::limit::Limit; /// This trait is used to print attributes in `rustc_hir_pretty`. @@ -120,6 +120,19 @@ impl PrintAttribute for Path { } } +impl PrintAttribute for AttrIntValue { + fn should_render(&self) -> bool { + true + } + + fn print_attribute(&self, p: &mut Printer) { + match self { + AttrIntValue::Lit(value) => p.word(format!("Lit({value})")), + AttrIntValue::Const { .. } => p.word("Const"), + } + } +} + macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 06ccf61a16203..90b79edc7a695 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1599,7 +1599,8 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { for (r, _) in reprs { if let ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack != &repr_pack + && let Some(pack) = tcx.eval_attr_alignment(*pack, "repr(packed)") + && pack != repr_pack { struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 44ac747726a61..860e3febcfc25 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -84,6 +84,40 @@ pub(crate) struct ConstEvalNonIntError { pub span: Span, } +#[derive(Diagnostic)] +#[diag("const item used in `{$attr_name}` must have an integer type, found `{$ty}`")] +pub(crate) struct AttrConstNonInt<'tcx> { + #[primary_span] + pub span: Span, + pub attr_name: &'static str, + pub ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag("const item used in `{$attr_name}` must evaluate to a non-negative integer")] +pub(crate) struct AttrConstNegative { + #[primary_span] + pub span: Span, + pub attr_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag("const item used in `{$attr_name}` cannot depend on generic parameters")] +pub(crate) struct AttrConstTooGeneric { + #[primary_span] + pub span: Span, + pub attr_name: &'static str, +} + +#[derive(Diagnostic)] +#[diag("invalid value for `{$attr_name}`: {$reason}")] +pub(crate) struct InvalidAttrValue { + #[primary_span] + pub span: Span, + pub attr_name: &'static str, + pub reason: String, +} + #[derive(Diagnostic)] #[diag( "to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled" diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 822bbe079327f..e2f2ca820a4be 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -24,7 +24,7 @@ pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_abi::{ - Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, + Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, Size, VariantIdx, }; use rustc_ast as ast; use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; @@ -37,7 +37,7 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir as hir; -use rustc_hir::attrs::StrippedCfgItem; +use rustc_hir::attrs::{AttrResolution, StrippedCfgItem}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_hir::{LangItem, attrs as attr, find_attr}; @@ -51,7 +51,7 @@ use rustc_serialize::{Decodable, Encodable}; use rustc_session::config::OptLevel; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; -use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol}; +use rustc_span::{AttrId, DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol}; use rustc_target::callconv::FnAbi; pub use rustc_type_ir::data_structures::{DelayedMap, DelayedSet}; pub use rustc_type_ir::fast_reject::DeepRejectCtxt; @@ -116,6 +116,7 @@ use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::ich::StableHashingContext; use crate::metadata::{AmbigModChild, ModChild}; use crate::middle::privacy::EffectiveVisibilities; +use crate::mir::interpret::ErrorHandled; use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; use crate::query::{IntoQueryKey, Providers}; use crate::ty; @@ -209,6 +210,9 @@ pub struct ResolverAstLowering<'tcx> { pub lifetimes_res_map: NodeMap, /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap>, + /// Resolutions for builtin attribute arguments that need late name resolution, keyed by + /// attribute ID. + pub attr_res_map: FxIndexMap>>, pub next_node_id: ast::NodeId, @@ -1391,6 +1395,107 @@ pub enum ImplTraitInTraitData { } impl<'tcx> TyCtxt<'tcx> { + pub fn eval_attr_int_value( + self, + value: attr::AttrIntValue, + attr_name: &'static str, + ) -> Option { + match value { + attr::AttrIntValue::Lit(value) => Some(value), + attr::AttrIntValue::Const { def_id, span } => { + let ty = self.type_of(def_id).instantiate_identity(); + if !ty.is_integral() { + self.dcx().emit_err(crate::error::AttrConstNonInt { span, attr_name, ty }); + return None; + } + + let typing_env = ty::TypingEnv::post_analysis(self, def_id); + let size = match self + .layout_of(typing_env.with_post_analysis_normalized(self).as_query_input(ty)) + { + Ok(layout) => layout.size, + Err(_) => return None, + }; + + match self.const_eval_poly(def_id) { + Ok(val) => { + let Some(int) = val.try_to_scalar_int() else { + self.dcx().emit_err(crate::error::AttrConstNonInt { + span, + attr_name, + ty, + }); + return None; + }; + + if ty.is_signed() { + let value = int.to_int(size); + if value < 0 { + self.dcx() + .emit_err(crate::error::AttrConstNegative { span, attr_name }); + None + } else { + Some(value as u128) + } + } else { + Some(int.to_uint(size)) + } + } + Err(ErrorHandled::Reported(..)) => None, + Err(ErrorHandled::TooGeneric(_)) => { + self.dcx().emit_err(crate::error::AttrConstTooGeneric { span, attr_name }); + None + } + } + } + } + } + + pub fn eval_attr_alignment( + self, + value: attr::AttrIntValue, + attr_name: &'static str, + ) -> Option { + let span = match value { + attr::AttrIntValue::Lit(_) => DUMMY_SP, + attr::AttrIntValue::Const { span, .. } => span, + }; + let value = self.eval_attr_int_value(value, attr_name)?; + + if !value.is_power_of_two() { + self.dcx().emit_err(crate::error::InvalidAttrValue { + span, + attr_name, + reason: "not a power of two".to_string(), + }); + return None; + } + + let align = + value.try_into().ok().and_then(|a| Align::from_bytes(a).ok()).or_else(|| { + self.dcx().emit_err(crate::error::InvalidAttrValue { + span, + attr_name, + reason: "larger than 2^29".to_string(), + }); + None + })?; + + let max = Size::from_bits(self.sess.target.pointer_width).signed_int_max() as u64; + if align.bytes() > max { + self.dcx().emit_err(crate::error::InvalidAttrValue { + span, + attr_name, + reason: format!( + "alignment larger than `isize::MAX` bytes ({max} for the current target)" + ), + }); + return None; + } + + Some(align) + } + pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> { self.typeck(self.hir_body_owner_def_id(body)) } @@ -1433,11 +1538,13 @@ impl<'tcx> TyCtxt<'tcx> { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { - min_pack = Some(if let Some(min_pack) = min_pack { - min_pack.min(pack) - } else { - pack - }); + if let Some(pack) = self.eval_attr_alignment(pack, "repr(packed)") { + min_pack = Some(if let Some(min_pack) = min_pack { + min_pack.min(pack) + } else { + pack + }); + } ReprFlags::empty() } attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, @@ -1464,7 +1571,7 @@ impl<'tcx> TyCtxt<'tcx> { ReprFlags::empty() } attr::ReprAlign(align) => { - max_align = max_align.max(Some(align)); + max_align = max_align.max(self.eval_attr_alignment(align, "repr(align)")); ReprFlags::empty() } }); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 23c209c44745b..c003192174c32 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -147,6 +147,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { &self.resolver.tcx.sess, self.resolver.tcx.features(), self.resolver.tcx().registered_tools(()), + Default::default(), Early { emit_errors: ShouldEmit::Nothing }, ); let attrs = parser.parse_attribute_list( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b3a6fe95e5fa6..8d418a570ada5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -8,14 +8,15 @@ use std::borrow::Cow; use std::collections::hash_map::Entry; -use std::debug_assert_matches; use std::mem::{replace, swap, take}; use std::ops::{ControlFlow, Range}; +use std::{debug_assert_matches, slice}; use rustc_ast::visit::{ AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, }; use rustc_ast::*; +use rustc_attr_parsing::{AttrResolutionRequest, AttributeParser}; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -24,6 +25,7 @@ use rustc_errors::{ Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan, StashKey, Suggestions, pluralize, }; +use rustc_hir::attrs::AttrResolved; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; @@ -806,9 +808,8 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { - fn visit_attribute(&mut self, _: &'ast Attribute) { - // We do not want to resolve expressions that appear in attributes, - // as they do not correspond to actual code. + fn visit_attribute(&mut self, attr: &'ast Attribute) { + self.resolve_attr_paths(attr); } fn visit_item(&mut self, item: &'ast Item) { let prev = replace(&mut self.diag_metadata.current_item, Some(item)); @@ -1466,6 +1467,57 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } } +impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { + fn resolve_attr_paths(&mut self, attr: &'ast Attribute) { + let Some(name) = attr.name() else { + return; + }; + if !matches!(name, sym::repr | sym::rustc_align | sym::rustc_align_static) { + return; + } + + let mut requests = AttributeParser::parse_limited_attr_resolution_requests( + self.r.tcx.sess, + slice::from_ref(attr), + name, + attr.span, + DUMMY_NODE_ID, + Some(self.r.tcx.features()), + ); + for request in requests.shift_remove(&attr.id).into_iter().flatten() { + self.resolve_attr_path(attr.id, request); + } + } + + fn resolve_attr_path(&mut self, attr_id: AttrId, request: AttrResolutionRequest) { + let path_segments = request + .path + .segments + .iter() + .map(|segment| Segment::from_ident(segment.ident)) + .collect::>(); + let partial_res = self.smart_resolve_path_fragment( + &None, + &path_segments, + PathSource::Expr(None), + Finalize::new(DUMMY_NODE_ID, request.path.span), + RecordPartialRes::No, + None, + ); + let resolved = match partial_res.full_res() { + Some(Res::Err) | None => AttrResolved::Error, + Some(res) => AttrResolved::Resolved(res), + }; + self.r.attr_resolutions.entry(attr_id).or_default().push( + rustc_hir::attrs::AttrResolution { + kind: request.kind, + path_span: request.path.span, + resolved, + }, + ); + } +} + impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn new(resolver: &'a mut Resolver<'ra, 'tcx>) -> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // During late resolution we only track the module component of the parent scope, @@ -2773,6 +2825,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind); + walk_list!(self, visit_attribute, &item.attrs); let def_kind = self.r.local_def_kind(item.id); match &item.kind { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d75f2981a7724..d80716c8a21da 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -50,7 +50,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::StrippedCfgItem; +use rustc_hir::attrs::{AttrResolution, StrippedCfgItem}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, @@ -72,7 +72,7 @@ use rustc_middle::ty::{ use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; +use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -1387,6 +1387,7 @@ pub struct Resolver<'ra, 'tcx> { effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxIndexMap, doc_link_traits_in_scope: FxIndexMap>, + attr_resolutions: FxIndexMap>>, all_macro_rules: UnordSet = Default::default(), /// Invocation ids of all glob delegations. @@ -1741,6 +1742,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { effective_visibilities: Default::default(), doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), + attr_resolutions: Default::default(), current_crate_outer_attr_insert_span, .. }; @@ -1863,6 +1865,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { label_res_map: self.label_res_map, lifetimes_res_map: self.lifetimes_res_map, extra_lifetime_params_map: self.extra_lifetime_params_map, + attr_res_map: self.attr_resolutions, next_node_id: self.next_node_id, node_id_to_def_id: self .node_id_to_def_id diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 53e2527057bc2..90f84c701e85b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -643,6 +643,7 @@ symbols! { console, const_allocate, const_async_blocks, + const_attr_paths, const_block_items, const_c_variadic, const_closures, diff --git a/tests/codegen-llvm/align-fn-const-path.rs b/tests/codegen-llvm/align-fn-const-path.rs new file mode 100644 index 0000000000000..96a283d2e0331 --- /dev/null +++ b/tests/codegen-llvm/align-fn-const-path.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -Clink-dead-code +//@ edition: 2024 +//@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) + +#![crate_type = "lib"] +#![feature(rustc_attrs, fn_align, const_attr_paths)] + +const LOW: usize = 32; + +mod alignments { + pub const HIGH: usize = 64; +} + +// CHECK-LABEL: @const_align +// CHECK-SAME: align 64 +#[unsafe(no_mangle)] +#[rustc_align(LOW)] +#[rustc_align(alignments::HIGH)] +pub fn const_align() {} diff --git a/tests/codegen-llvm/align-static-const-path.rs b/tests/codegen-llvm/align-static-const-path.rs new file mode 100644 index 0000000000000..973593392fcb8 --- /dev/null +++ b/tests/codegen-llvm/align-static-const-path.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 + +#![crate_type = "lib"] +#![feature(static_align, const_attr_paths)] + +const LOW: usize = 32; + +mod alignments { + pub const HIGH: usize = 64; +} + +// CHECK: @CONST_ALIGN = +// CHECK-SAME: align 64 +#[no_mangle] +#[rustc_align_static(LOW)] +#[rustc_align_static(alignments::HIGH)] +pub static CONST_ALIGN: u64 = 0; diff --git a/tests/codegen-llvm/align-struct-const-path.rs b/tests/codegen-llvm/align-struct-const-path.rs new file mode 100644 index 0000000000000..1b3db79757b67 --- /dev/null +++ b/tests/codegen-llvm/align-struct-const-path.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 +// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480) +//@ ignore-i686-pc-windows-msvc +//@ ignore-i686-pc-windows-gnu + +#![crate_type = "lib"] +#![feature(const_attr_paths)] + +mod alignments { + pub const STRUCT: usize = 64; +} + +#[repr(align(alignments::STRUCT))] +pub struct Align64(i32); + +// CHECK-LABEL: @align64_const +#[no_mangle] +pub fn align64_const(i: i32) -> Align64 { + // CHECK: %a64 = alloca [64 x i8], align 64 + let a64 = Align64(i); + a64 +} diff --git a/tests/incremental/attr-const-path-repr-align.rs b/tests/incremental/attr-const-path-repr-align.rs new file mode 100644 index 0000000000000..4eef3670d175f --- /dev/null +++ b/tests/incremental/attr-const-path-repr-align.rs @@ -0,0 +1,24 @@ +//@ revisions: cfail1 cfail2 +//@ build-pass + +#![feature(const_attr_paths)] +#![allow(dead_code)] + +#[cfg(cfail1)] +const ALIGN: usize = 8; +#[cfg(cfail2)] +const ALIGN: usize = 16; + +#[cfg(cfail1)] +const EXPECT: usize = 8; +#[cfg(cfail2)] +const EXPECT: usize = 16; + +#[repr(align(ALIGN))] +struct Aligned(u8); + +const _: [(); EXPECT] = [(); core::mem::align_of::()]; + +fn main() { + let _ = Aligned(0); +} diff --git a/tests/pretty/hir-pretty-attr.pp b/tests/pretty/hir-pretty-attr.pp index be23294e8f7eb..f13f77f47b3f1 100644 --- a/tests/pretty/hir-pretty-attr.pp +++ b/tests/pretty/hir-pretty-attr.pp @@ -5,6 +5,6 @@ //@ pretty-mode:hir //@ pp-exact:hir-pretty-attr.pp -#[attr = Repr {reprs: [ReprC, ReprPacked(Align(4 bytes)), ReprTransparent]}] +#[attr = Repr {reprs: [ReprC, ReprPacked(Lit(4)), ReprTransparent]}] struct Example { } diff --git a/tests/ui/attributes/arg-error-issue-121425.rs b/tests/ui/attributes/arg-error-issue-121425.rs index c15b276ad4eb2..486bc8ca3658f 100644 --- a/tests/ui/attributes/arg-error-issue-121425.rs +++ b/tests/ui/attributes/arg-error-issue-121425.rs @@ -1,8 +1,9 @@ +#![feature(const_attr_paths)] + //@ compile-flags: -Zdeduplicate-diagnostics=yes const N: usize = 8; #[repr(align(N))] -//~^ ERROR: incorrect `repr(align)` attribute format struct T; #[repr(align('a'))] @@ -19,7 +20,6 @@ struct X; const P: usize = 8; #[repr(packed(P))] -//~^ ERROR: attribute format: `packed` expects a literal integer as argument struct A; #[repr(packed())] diff --git a/tests/ui/attributes/arg-error-issue-121425.stderr b/tests/ui/attributes/arg-error-issue-121425.stderr index 1beb99b1703cf..89f687744a448 100644 --- a/tests/ui/attributes/arg-error-issue-121425.stderr +++ b/tests/ui/attributes/arg-error-issue-121425.stderr @@ -1,40 +1,28 @@ -error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - --> $DIR/arg-error-issue-121425.rs:4:14 - | -LL | #[repr(align(N))] - | ^ - error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer - --> $DIR/arg-error-issue-121425.rs:8:14 + --> $DIR/arg-error-issue-121425.rs:9:14 | LL | #[repr(align('a'))] | ^^^ error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer - --> $DIR/arg-error-issue-121425.rs:12:14 + --> $DIR/arg-error-issue-121425.rs:13:14 | LL | #[repr(align("str"))] | ^^^^^ error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - --> $DIR/arg-error-issue-121425.rs:16:8 + --> $DIR/arg-error-issue-121425.rs:17:8 | LL | #[repr(align())] | ^^^^^^^ -error[E0552]: incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument - --> $DIR/arg-error-issue-121425.rs:21:15 - | -LL | #[repr(packed(P))] - | ^ - error[E0552]: incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all --> $DIR/arg-error-issue-121425.rs:25:8 | LL | #[repr(packed())] | ^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0552, E0589, E0693. For more information about an error, try `rustc --explain E0552`. diff --git a/tests/ui/attributes/fn-align-const-path.rs b/tests/ui/attributes/fn-align-const-path.rs new file mode 100644 index 0000000000000..69fd4004a6fc5 --- /dev/null +++ b/tests/ui/attributes/fn-align-const-path.rs @@ -0,0 +1,14 @@ +//@ run-pass +//@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) +//@ ignore-backends: gcc + +#![feature(rustc_attrs, fn_align, const_attr_paths)] + +const ALIGN: usize = 4096; + +#[rustc_align(ALIGN)] +fn aligned() {} + +fn main() { + assert_eq!((aligned as fn() as usize & !1) % ALIGN, 0); +} diff --git a/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.rs b/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.rs new file mode 100644 index 0000000000000..168187f10172e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.rs @@ -0,0 +1,21 @@ +#![feature(rustc_attrs, fn_align, static_align)] + +#[repr(align(UNKNOWN_ALIGN))] +//~^ ERROR const item paths in builtin attributes are experimental +struct ReprAlign; + +#[repr(packed(UNKNOWN_PACK))] +//~^ ERROR const item paths in builtin attributes are experimental +struct ReprPacked(u32); + +#[rustc_align(UNKNOWN_FN_ALIGN)] +//~^ ERROR const item paths in builtin attributes are experimental +fn aligned_fn() {} + +#[rustc_align_static(UNKNOWN_STATIC_ALIGN)] +//~^ ERROR const item paths in builtin attributes are experimental +static ALIGNED_STATIC: u64 = 0; + +fn main() { + let _ = ALIGNED_STATIC; +} diff --git a/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.stderr b/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.stderr new file mode 100644 index 0000000000000..362e1c7f9133f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-attr-paths-unresolved.stderr @@ -0,0 +1,43 @@ +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths-unresolved.rs:3:14 + | +LL | #[repr(align(UNKNOWN_ALIGN))] + | ^^^^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths-unresolved.rs:7:15 + | +LL | #[repr(packed(UNKNOWN_PACK))] + | ^^^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths-unresolved.rs:11:15 + | +LL | #[rustc_align(UNKNOWN_FN_ALIGN)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths-unresolved.rs:15:22 + | +LL | #[rustc_align_static(UNKNOWN_STATIC_ALIGN)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-const-attr-paths.rs b/tests/ui/feature-gates/feature-gate-const-attr-paths.rs new file mode 100644 index 0000000000000..d1e961b95371b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-attr-paths.rs @@ -0,0 +1,25 @@ +#![feature(rustc_attrs, fn_align, static_align)] + +const REPR_ALIGN: usize = 8; +#[repr(align(REPR_ALIGN))] +//~^ ERROR const item paths in builtin attributes are experimental +struct ReprAlign; + +const REPR_PACK: usize = 2; +#[repr(packed(REPR_PACK))] +//~^ ERROR const item paths in builtin attributes are experimental +struct ReprPacked(u32); + +const FN_ALIGN: usize = 16; +#[rustc_align(FN_ALIGN)] +//~^ ERROR const item paths in builtin attributes are experimental +fn aligned_fn() {} + +const STATIC_ALIGN: usize = 16; +#[rustc_align_static(STATIC_ALIGN)] +//~^ ERROR const item paths in builtin attributes are experimental +static ALIGNED_STATIC: u64 = 0; + +fn main() { + let _ = ALIGNED_STATIC; +} diff --git a/tests/ui/feature-gates/feature-gate-const-attr-paths.stderr b/tests/ui/feature-gates/feature-gate-const-attr-paths.stderr new file mode 100644 index 0000000000000..d7cc001982d51 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-attr-paths.stderr @@ -0,0 +1,43 @@ +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths.rs:4:14 + | +LL | #[repr(align(REPR_ALIGN))] + | ^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths.rs:9:15 + | +LL | #[repr(packed(REPR_PACK))] + | ^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths.rs:14:15 + | +LL | #[rustc_align(FN_ALIGN)] + | ^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const item paths in builtin attributes are experimental + --> $DIR/feature-gate-const-attr-paths.rs:19:22 + | +LL | #[rustc_align_static(STATIC_ALIGN)] + | ^^^^^^^^^^^^ + | + = note: see issue #52840 for more information + = help: add `#![feature(const_attr_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/repr/auxiliary/repr_const_paths_aux.rs b/tests/ui/repr/auxiliary/repr_const_paths_aux.rs new file mode 100644 index 0000000000000..bc9dfd0cc101e --- /dev/null +++ b/tests/ui/repr/auxiliary/repr_const_paths_aux.rs @@ -0,0 +1 @@ +pub const ALIGN: usize = 16; diff --git a/tests/ui/repr/repr-const-paths-cross-crate.rs b/tests/ui/repr/repr-const-paths-cross-crate.rs new file mode 100644 index 0000000000000..a6aca3b102c25 --- /dev/null +++ b/tests/ui/repr/repr-const-paths-cross-crate.rs @@ -0,0 +1,13 @@ +//@ check-pass +//@ aux-build:repr_const_paths_aux.rs + +#![feature(const_attr_paths)] + +extern crate repr_const_paths_aux; + +#[repr(align(repr_const_paths_aux::ALIGN))] +struct CrossCrate(u8); + +fn main() { + let _ = CrossCrate(0); +} diff --git a/tests/ui/repr/repr-const-paths-invalid.rs b/tests/ui/repr/repr-const-paths-invalid.rs new file mode 100644 index 0000000000000..e7b252b157cb1 --- /dev/null +++ b/tests/ui/repr/repr-const-paths-invalid.rs @@ -0,0 +1,29 @@ +#![feature(const_attr_paths)] + +fn not_const() -> usize { + 8 +} + +#[repr(align(not_const))] +//~^ ERROR paths in `repr(align)` must refer to `const` items, not function +struct WrongItem; + +const BOOL_ALIGN: bool = true; +#[repr(align(BOOL_ALIGN))] +//~^ ERROR const item used in `repr(align)` must have an integer type +struct WrongType; + +const NEG_ALIGN: isize = -8; +#[repr(align(NEG_ALIGN))] +//~^ ERROR const item used in `repr(align)` must evaluate to a non-negative integer +struct Negative; + +#[repr(align(N))] +//~^ ERROR cannot find value `N` in this scope +struct Generic; + +const CYCLE: usize = core::mem::align_of::(); +#[repr(align(CYCLE))] +struct Cycle; //~ ERROR cycle detected when computing ADT definition for `Cycle` + +fn main() {} diff --git a/tests/ui/repr/repr-const-paths-invalid.stderr b/tests/ui/repr/repr-const-paths-invalid.stderr new file mode 100644 index 0000000000000..b0d876a68ed63 --- /dev/null +++ b/tests/ui/repr/repr-const-paths-invalid.stderr @@ -0,0 +1,73 @@ +error[E0425]: cannot find value `N` in this scope + --> $DIR/repr-const-paths-invalid.rs:21:14 + | +LL | #[repr(align(N))] + | ^ not found in this scope + | +help: you might be missing a const parameter + | +LL | struct Generic; + | +++++++++++++++++++++ + +error: paths in `repr(align)` must refer to `const` items, not function + --> $DIR/repr-const-paths-invalid.rs:7:14 + | +LL | #[repr(align(not_const))] + | ^^^^^^^^^ + +error: const item used in `repr(align)` must have an integer type, found `bool` + --> $DIR/repr-const-paths-invalid.rs:12:14 + | +LL | #[repr(align(BOOL_ALIGN))] + | ^^^^^^^^^^ + +error: const item used in `repr(align)` must evaluate to a non-negative integer + --> $DIR/repr-const-paths-invalid.rs:17:14 + | +LL | #[repr(align(NEG_ALIGN))] + | ^^^^^^^^^ + +error[E0391]: cycle detected when computing ADT definition for `Cycle` + --> $DIR/repr-const-paths-invalid.rs:27:1 + | +LL | struct Cycle; + | ^^^^^^^^^^^^ + | +note: ...which requires simplifying constant for the type system `CYCLE`... + --> $DIR/repr-const-paths-invalid.rs:25:1 + | +LL | const CYCLE: usize = core::mem::align_of::(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires checking if `CYCLE` is a trivial const... + --> $DIR/repr-const-paths-invalid.rs:25:1 + | +LL | const CYCLE: usize = core::mem::align_of::(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires building MIR for `CYCLE`... + --> $DIR/repr-const-paths-invalid.rs:25:1 + | +LL | const CYCLE: usize = core::mem::align_of::(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires match-checking `CYCLE`... + --> $DIR/repr-const-paths-invalid.rs:25:1 + | +LL | const CYCLE: usize = core::mem::align_of::(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires type-checking `CYCLE`... + --> $DIR/repr-const-paths-invalid.rs:25:44 + | +LL | const CYCLE: usize = core::mem::align_of::(); + | ^^^^^ +note: ...which requires computing type of `Cycle`... + --> $DIR/repr-const-paths-invalid.rs:27:1 + | +LL | struct Cycle; + | ^^^^^^^^^^^^ + = note: ...which again requires computing ADT definition for `Cycle`, completing the cycle + = note: cycle used when computing the inferred outlives-predicates for items in this crate + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0391, E0425. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/repr/repr-const-paths.rs b/tests/ui/repr/repr-const-paths.rs new file mode 100644 index 0000000000000..dc6ac866d3425 --- /dev/null +++ b/tests/ui/repr/repr-const-paths.rs @@ -0,0 +1,34 @@ +//@ check-pass + +#![feature(const_attr_paths)] + +mod foo { + pub const fn align() -> usize { + 8 + } + + pub const ALIGN: usize = align(); +} + +const COMPUTED_ALIGN: usize = core::mem::align_of::(); +const PACK: usize = 2; +use foo::ALIGN as IMPORTED_ALIGN; + +#[repr(align(foo::ALIGN))] +struct Aligned(u8); + +#[repr(align(IMPORTED_ALIGN))] +struct Imported(u8); + +#[repr(align(COMPUTED_ALIGN))] +struct Computed(u8); + +#[repr(packed(PACK))] +struct Packed(u32); + +fn main() { + let _ = Aligned(0); + let _ = Imported(0); + let _ = Computed(0); + let _ = Packed(0); +} diff --git a/tests/ui/static/static-align-const-path.rs b/tests/ui/static/static-align-const-path.rs new file mode 100644 index 0000000000000..bea83d0db5ac4 --- /dev/null +++ b/tests/ui/static/static-align-const-path.rs @@ -0,0 +1,12 @@ +//@ run-pass + +#![feature(rustc_attrs, static_align, const_attr_paths)] + +const ALIGN: usize = 128; + +#[rustc_align_static(ALIGN)] +static ALIGNED: u64 = 0; + +fn main() { + assert_eq!((&raw const ALIGNED as usize) % ALIGN, 0); +} diff --git a/tests/ui/unpretty/attr-const-paths.rs b/tests/ui/unpretty/attr-const-paths.rs new file mode 100644 index 0000000000000..0cfa40c710162 --- /dev/null +++ b/tests/ui/unpretty/attr-const-paths.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Zunpretty=hir +//@ check-pass + +#![feature(const_attr_paths)] + +const ALIGN: usize = 8; + +#[repr(align(ALIGN))] +struct Aligned; + +#[repr(packed(2))] +struct Packed(u32); + +fn main() {} diff --git a/tests/ui/unpretty/attr-const-paths.stdout b/tests/ui/unpretty/attr-const-paths.stdout new file mode 100644 index 0000000000000..22ae78065ae24 --- /dev/null +++ b/tests/ui/unpretty/attr-const-paths.stdout @@ -0,0 +1,17 @@ +#![attr = Feature([const_attr_paths#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; +//@ compile-flags: -Zunpretty=hir +//@ check-pass + + +const ALIGN: usize = 8; + +#[attr = Repr {reprs: [ReprAlign(Const)]}] +struct Aligned; + +#[attr = Repr {reprs: [ReprPacked(Lit(2))]}] +struct Packed(u32); + +fn main() { }