Skip to content

Commit

Permalink
Implement #[defines] attribute for consts and functions.
Browse files Browse the repository at this point in the history
statics still need work
  • Loading branch information
oli-obk committed Jul 31, 2024
1 parent ff0a065 commit d6e4776
Show file tree
Hide file tree
Showing 583 changed files with 2,048 additions and 1,915 deletions.
11 changes: 11 additions & 0 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::token::{self, CommentKind, Delimiter, Token};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenTree};
use crate::util::comments;
use crate::util::literal::escape_string_symbol;
use crate::NodeId;

pub struct MarkedAttrs(GrowableBitSet<AttrId>);

Expand Down Expand Up @@ -458,6 +459,16 @@ impl MetaItemKind {
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
}
}

pub fn defines(&self) -> Vec<NodeId> {
match self {
MetaItemKind::Word => vec![],
MetaItemKind::List(things) => {
things.iter().filter_map(|i| Some(i.meta_item()?.path.segments[0].id)).collect()
}
MetaItemKind::NameValue(_) => vec![],
}
}
}

impl NestedMetaItem {
Expand Down
21 changes: 19 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ struct LoweringContext<'a, 'hir> {

/// Bodies inside the owner being lowered.
bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
/// Bodies inside the owner being lowered.
defines: SortedMap<hir::ItemLocalId, &'hir [LocalDefId]>,
/// Attributes inside the owner being lowered.
attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>,
/// Collect items that were created by lowering the current owner.
Expand Down Expand Up @@ -173,6 +175,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

// HirId handling.
bodies: Vec::new(),
defines: SortedMap::default(),
attrs: SortedMap::default(),
children: Vec::default(),
current_hir_id_owner: hir::CRATE_OWNER_ID,
Expand Down Expand Up @@ -590,6 +593,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

let current_attrs = std::mem::take(&mut self.attrs);
let current_bodies = std::mem::take(&mut self.bodies);
let current_defines = std::mem::take(&mut self.defines);
let current_node_ids = std::mem::take(&mut self.node_id_to_local_id);
let current_trait_map = std::mem::take(&mut self.trait_map);
let current_owner =
Expand All @@ -615,6 +619,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let info = self.make_owner_info(item);

self.attrs = current_attrs;
self.defines = current_defines;
self.bodies = current_bodies;
self.node_id_to_local_id = current_node_ids;
self.trait_map = current_trait_map;
Expand Down Expand Up @@ -656,6 +661,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}

fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
let defines = std::mem::take(&mut self.defines);
let attrs = std::mem::take(&mut self.attrs);
let mut bodies = std::mem::take(&mut self.bodies);
let trait_map = std::mem::take(&mut self.trait_map);
Expand All @@ -673,11 +679,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

// Don't hash unless necessary, because it's expensive.
let (opt_hash_including_bodies, attrs_hash) =
self.tcx.hash_owner_nodes(node, &bodies, &attrs);
self.tcx.hash_owner_nodes(node, &bodies, &attrs, &defines);
let num_nodes = self.item_local_id_counter.as_usize();
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
let attrs = hir::AttributeMap { map: attrs, defines, opt_hash: attrs_hash };

self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
}
Expand Down Expand Up @@ -922,6 +928,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug_assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
debug_assert!(!ret.is_empty());

let defines = &*self.arena.alloc_from_iter(
ret.iter()
.filter(|attr| attr.has_name(sym::defines))
.filter_map(|attr| self.resolver.defines.get(&attr.id))
// TODO: error reporting for non-local items being mentioned and tests that go through these code paths

Check failure on line 936 in compiler/rustc_ast_lowering/src/lib.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
.flat_map(|defines| defines.into_iter().map(|did| did.expect_local())),
);
trace!(?id, ?ret, ?defines, "lower_attrs");

self.defines.insert(id.local_id, defines);
self.attrs.insert(id.local_id, ret);
ret
}
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,13 +934,18 @@ pub struct ParentedNode<'tcx> {
#[derive(Debug)]
pub struct AttributeMap<'tcx> {
pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>,
/// Preprocessed `#[defines]` attribute.
pub defines: SortedMap<ItemLocalId, &'tcx [LocalDefId]>,
// Only present when the crate hash is needed.
pub opt_hash: Option<Fingerprint>,
}

impl<'tcx> AttributeMap<'tcx> {
pub const EMPTY: &'static AttributeMap<'static> =
&AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) };
pub const EMPTY: &'static AttributeMap<'static> = &AttributeMap {
map: SortedMap::new(),
defines: SortedMap::new(),
opt_hash: Some(Fingerprint::ZERO),
};

#[inline]
pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir/src/stable_hash_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'

impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
// We ignore the `map` since it refers to information included in `opt_hash` which is
// We ignore the `map` and `defines` since they refer to information included in `opt_hash` which is
// hashed in the collector and used for the crate hash.
let AttributeMap { opt_hash, map: _ } = *self;
let AttributeMap { opt_hash, map: _, defines: _ } = *self;
opt_hash.unwrap().hash_stable(hcx, hasher);
}
}
Expand Down
89 changes: 29 additions & 60 deletions compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use rustc_errors::StashKey;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, def, Expr, ImplItem, Item, Node, TraitItem};
use rustc_middle::bug;
use rustc_hir::{self as hir, def, intravisit, Expr, ImplItem, Item, Node, TraitItem};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
use rustc_span::DUMMY_SP;

use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueType};
use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};

/// Checks "defining uses" of opaque `impl Trait` in associated types.
/// These can only be defined by associated items of the same trait.
Expand Down Expand Up @@ -82,38 +81,9 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
/// ```
#[instrument(skip(tcx), level = "debug")]
pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let scope = tcx.hir().get_defining_scope(hir_id);
let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };

debug!(?scope);

if scope == hir::CRATE_HIR_ID {
tcx.hir().walk_toplevel_module(&mut locator);
} else {
trace!("scope={:#?}", tcx.hir_node(scope));
match tcx.hir_node(scope) {
// We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods
// This allows our visitor to process the defining item itself, causing
// it to pick up any 'sibling' defining uses.
//
// For example, this code:
// ```
// fn foo() {
// type Blah = impl Debug;
// let my_closure = || -> Blah { true };
// }
// ```
//
// requires us to explicitly process `foo()` in order
// to notice the defining usage of `Blah`.
Node::Item(it) => locator.visit_item(it),
Node::ImplItem(it) => locator.visit_impl_item(it),
Node::TraitItem(it) => locator.visit_trait_item(it),
Node::ForeignItem(it) => locator.visit_foreign_item(it),
other => bug!("{:?} is not a valid scope for an opaque type item", other),
}
}
tcx.hir().walk_toplevel_module(&mut locator);

if let Some(hidden) = locator.found {
// Only check against typeck if we didn't already error
Expand All @@ -137,12 +107,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
span: tcx.def_span(def_id),
name: tcx.item_name(parent_def_id.to_def_id()),
what: match tcx.hir_node(scope) {
_ if scope == hir::CRATE_HIR_ID => "module",
Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module",
Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl",
_ => "item",
},
what: "crate",
});
Ty::new_error(tcx, reported)
}
Expand Down Expand Up @@ -176,6 +141,13 @@ impl TaitConstraintLocator<'_> {
return;
}

let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
// Don't try to check items that cannot possibly constrain the type.
if opaque_types_defined_by.is_empty() {
debug!("no constraint: no opaque types defined");
return;
}

// Function items with `_` in their return type already emit an error, skip any
// "non-defining use" errors for them.
// Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
Expand Down Expand Up @@ -215,8 +187,6 @@ impl TaitConstraintLocator<'_> {
return;
}

let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);

let mut constrained = false;
for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
if opaque_type_key.def_id != self.def_id {
Expand All @@ -225,13 +195,12 @@ impl TaitConstraintLocator<'_> {
constrained = true;

if !opaque_types_defined_by.contains(&self.def_id) {
self.tcx.dcx().emit_err(TaitForwardCompat {
span: hidden_type.span,
item_span: self
.tcx
.def_ident_span(item_def_id)
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
});
span_bug!(
hidden_type.span,
"item registered hidden type {} for {:?}, even though it does not define it",
hidden_type.ty,
opaque_type_key
);
}
let concrete_type =
self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
Expand All @@ -247,14 +216,20 @@ impl TaitConstraintLocator<'_> {
if !constrained {
debug!("no constraints in typeck results");
if opaque_types_defined_by.contains(&self.def_id) {
self.tcx.dcx().emit_err(TaitForwardCompat2 {
let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
span: self
.tcx
.def_ident_span(item_def_id)
.unwrap_or_else(|| self.tcx.def_span(item_def_id)),
opaque_type_span: self.tcx.def_span(self.def_id),
opaque_type: self.tcx.def_path_str(self.def_id),
});
// TODO: land this change as a separate PR on master, it works on its own.
// Avoid "opaque type not constrained" errors on the opaque itself.
self.found = Some(ty::OpaqueHiddenType {
span: DUMMY_SP,
ty: Ty::new_error(self.tcx, guar),
});
}
return;
};
Expand Down Expand Up @@ -300,19 +275,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
if it.owner_id.def_id != self.def_id {
self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
if it.owner_id.def_id != self.def_id {
self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
trace!(?it.owner_id);
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_metadata/src/rmeta/table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use rustc_hir::def::CtorOf;
use rustc_index::Idx;
use tracing::trace;

use crate::rmeta::*;

Expand Down Expand Up @@ -522,8 +521,6 @@ where
{
/// Given the metadata, extract out the value at a particular index (if any).
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
trace!("LazyTable::lookup: index={:?} len={:?}", i, self.len);

// Access past the end of the table returns a Default
if i.index() >= self.len {
return Default::default();
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,17 +664,6 @@ impl<'hir> Map<'hir> {
None
}

/// Returns the defining scope for an opaque type definition.
pub fn get_defining_scope(self, id: HirId) -> HirId {
let mut scope = id;
loop {
scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID);
if scope == CRATE_HIR_ID || !matches!(self.tcx.hir_node(scope), Node::Block(_)) {
return scope;
}
}
}

pub fn get_foreign_abi(self, hir_id: HirId) -> Abi {
let parent = self.get_parent_item(hir_id);
if let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) =
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl<'tcx> TyCtxt<'tcx> {
node: OwnerNode<'_>,
bodies: &SortedMap<ItemLocalId, &Body<'_>>,
attrs: &SortedMap<ItemLocalId, &[rustc_ast::Attribute]>,
defines: &SortedMap<ItemLocalId, &[LocalDefId]>,
) -> (Option<Fingerprint>, Option<Fingerprint>) {
if self.needs_crate_hash() {
self.with_stable_hashing_context(|mut hcx| {
Expand All @@ -151,6 +152,7 @@ impl<'tcx> TyCtxt<'tcx> {

let mut stable_hasher = StableHasher::new();
attrs.hash_stable(&mut hcx, &mut stable_hasher);
defines.hash_stable(&mut hcx, &mut stable_hasher);
let h2 = stable_hasher.finish();
(Some(h1), Some(h2))
})
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
let bodies = Default::default();
let attrs = hir::AttributeMap::EMPTY;

let (opt_hash_including_bodies, _) = self.tcx.hash_owner_nodes(node, &bodies, &attrs.map);
let (opt_hash_including_bodies, _) =
self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, &attrs.defines);
let node = node.into();
self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes {
opt_hash_including_bodies,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ pub struct ResolverAstLowering {

/// Information about functions signatures for delegation items expansion
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,

/// List of resolved `#[defines]` attribute arguments.
pub defines: FxHashMap<ast::AttrId, Vec<DefId>>,
}

#[derive(Debug)]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ passes_debug_visualizer_placement =
passes_debug_visualizer_unreadable =
couldn't read {$file}: {$error}
passes_defines_not_fn_or_const =
attribute should be applied to a function definition, a closure, a static, or a const
.label = cannot define hidden types
passes_deprecated =
attribute is ignored here
Expand Down
Loading

0 comments on commit d6e4776

Please sign in to comment.