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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -239,6 +242,7 @@ impl SpanLowerer {
struct ResolverDelayedAstLowering<'a, 'tcx> {
node_id_to_def_id: NodeMap<LocalDefId>,
partial_res_map: NodeMap<PartialRes>,
attr_res_map: FxIndexMap<rustc_span::AttrId, Vec<AttrResolution<ast::NodeId>>>,
next_node_id: NodeId,
base: &'a ResolverAstLowering<'tcx>,
}
Expand All @@ -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<rustc_span::AttrId, Vec<AttrResolution<ast::NodeId>>> {
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<Option<Res<NodeId>>> {
self.base.get_import_res(id)
}
Expand Down Expand Up @@ -345,6 +359,12 @@ impl<'tcx> ResolverAstLowering<'tcx> {
self.partial_res_map.get(&id).copied()
}

fn all_attr_resolutions(
&self,
) -> FxIndexMap<rustc_span::AttrId, Vec<AttrResolution<ast::NodeId>>> {
self.attr_res_map.clone()
}

/// Obtains per-namespace resolutions for `use` statement with the given `NodeId`.
fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>> {
self.import_res_map.get(&id).copied().unwrap_or_default()
Expand Down Expand Up @@ -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,
};
Expand Down
148 changes: 104 additions & 44 deletions compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand Down Expand Up @@ -100,7 +105,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> {
}
}

fn parse_repr<S: Stage>(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option<ReprAttr> {
fn parse_repr<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
param: &MetaItemParser,
) -> Option<ReprAttr> {
use ReprAttr::*;

// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
Expand All @@ -122,7 +130,7 @@ fn parse_repr<S: Stage>(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)
}
Expand Down Expand Up @@ -189,8 +197,13 @@ enum AlignKind {
Align,
}

enum AlignmentParseError {
Message(String),
AlreadyErrored,
}

fn parse_repr_align<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser,
param_span: Span,
align_kind: AlignKind,
Expand All @@ -214,31 +227,21 @@ fn parse_repr_align<S: Stage>(
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(),
Expand All @@ -247,6 +250,7 @@ fn parse_repr_align<S: Stage>(
});
None
}
Err(AlignmentParseError::AlreadyErrored) => None,
}
}

Expand Down Expand Up @@ -281,9 +285,74 @@ fn parse_alignment<S: Stage>(
Ok(align)
}

fn parse_alignment_or_const_path<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
arg: &MetaItemOrLitParser,
attr_name: &'static str,
) -> Result<AttrIntValue, AlignmentParseError> {
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));
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For who does end up reviewing this PR, I'm taking another look at things, and I want to note that in fn parse_alignment_or_const_path<S: Stage>, I'm returning 1 on ShouldEmit::Nothing. This was a deliberate decision because repr(packed) itself returns 1 by default elsewhere, and is effectively no-op otherwise, but part of not being obtrusive to the codebase I shimmed it in there in order to avoid a heavier refactor of repr.rs to make it typed. Categorically it is fake data, but I think it's defensible from this stance. If the lang and compiler teams want to go this direction of resolutions within attributes, this should be pulled out for a better solution so future work doesn't trip over it. Deferred state should be represented properly, so I wanted to note this now.

}
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];
Expand All @@ -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) => {}
}
}
}
Expand All @@ -335,8 +397,7 @@ impl<S: Stage> AttributeParser<S> for RustcAlignParser {
]);

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (align, span) = self.0?;
Some(AttributeKind::RustcAlign { align, span })
(!self.0.is_empty()).then_some(AttributeKind::RustcAlign { aligns: self.0 })
}
}

Expand All @@ -358,7 +419,6 @@ impl<S: Stage> AttributeParser<S> for RustcAlignStaticParser {
AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (align, span) = self.0.0?;
Some(AttributeKind::RustcAlign { align, span })
(!self.0.0.is_empty()).then_some(AttributeKind::RustcAlign { aligns: self.0.0 })
}
}
21 changes: 20 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<rustc_hir::attrs::AttrResolved<NodeId>> {
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> {
Expand Down
Loading
Loading