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
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub(crate) mod link_attrs;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;
pub(crate) mod macro_attrs;
pub(crate) mod modules;
pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/modules.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use super::prelude::*;

pub(crate) struct TransparentParser;
impl<S: Stage> NoArgsAttributeParser<S> for TransparentParser {
const PATH: &[Symbol] = &[sym::transparent];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Transparent;
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::macro_attrs::{
AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser,
};
use crate::attributes::modules::TransparentParser;
use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
Expand Down Expand Up @@ -241,6 +242,7 @@ attribute_parsers!(
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Single<WithoutArgs<TransparentParser>>,
Single<WithoutArgs<TypeConstParser>>,
Single<WithoutArgs<UnsafeSpecializationMarkerParser>>,
// tidy-alphabetical-end
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
),

gated!(
transparent, Normal,
template!(Word, "https://github.com/rust-lang/rust/issues/79260#issuecomment-731773625"),
Copy link
Member Author

@yaahc yaahc Oct 15, 2025

Choose a reason for hiding this comment

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

probably also want to update this to point to actual documentation (I don't intend to add that, if its a blocker then leave this open or close it until someone comes along who wants to push this over the finish line)

ErrorFollowing, EncodeCrossCrate::No, transparent_modules, experimental!(transparent)
),

// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ declare_features! (
(unstable, trait_alias, "1.24.0", Some(41517)),
/// Allows for transmuting between arrays with sizes that contain generic consts.
(unstable, transmute_generic_consts, "1.70.0", Some(109929)),
/// Allows #[transparent] on modules to inherit lexical scopes.
(unstable, transparent_modules, "1.91.0", Some(79260)),
Copy link
Member Author

Choose a reason for hiding this comment

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

not a real tracking issue

/// Allows #[repr(transparent)] on unions (RFC 2645).
(unstable, transparent_unions, "1.37.0", Some(60405)),
/// Allows inconsistent bounds in where clauses.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,9 @@ pub enum AttributeKind {
/// Represents `#[track_caller]`
TrackCaller(Span),

/// Represents `#[transparent]` mod attribute
Transparent(Span),

/// Represents `#[type_const]`.
TypeConst(Span),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ impl AttributeKind {
StdInternalSymbol(..) => No,
TargetFeature { .. } => No,
TrackCaller(..) => Yes,
Transparent(..) => No,
TypeConst(..) => Yes,
TypeLengthLimit { .. } => No,
UnsafeSpecializationMarker(..) => No,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
| AttributeKind::MacroTransparency(_)
| AttributeKind::Transparent(_)
| AttributeKind::Pointee(..)
| AttributeKind::Dummy
| AttributeKind::RustcBuiltinMacro { .. }
Expand Down
25 changes: 19 additions & 6 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let expn_id = self.cstore().expn_that_defined_untracked(def_id, self.tcx.sess);
return Some(self.new_extern_module(
parent,
ModuleKind::Def(def_kind, def_id, Some(self.tcx.item_name(def_id))),
ModuleKind::Def(
def_kind,
def_id,
Some((self.tcx.item_name(def_id), false)),
),
expn_id,
self.def_span(def_id),
// FIXME: Account for `#[no_implicit_prelude]` attributes.
Expand Down Expand Up @@ -629,14 +633,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// Disallow `use $crate;`
if source.ident.name == kw::DollarCrate && module_path.is_empty() {
let crate_root = self.r.resolve_crate_root(source.ident);
let crate_name = match crate_root.kind {
ModuleKind::Def(.., name) => name,
let crate_name_and_transparent = match crate_root.kind {
ModuleKind::Def(.., name_and_transparent) => name_and_transparent,
ModuleKind::Block => unreachable!(),
};
// HACK(eddyb) unclear how good this is, but keeping `$crate`
// in `source` breaks `tests/ui/imports/import-crate-var.rs`,
// while the current crate doesn't have a valid `crate_name`.
if let Some(crate_name) = crate_name {
if let Some((crate_name, _transparent)) = crate_name_and_transparent {
// `crate_name` should not be interpreted as relative.
module_path.push(Segment::from_ident_and_id(
Ident::new(kw::PathRoot, source.ident.span),
Expand Down Expand Up @@ -818,9 +822,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
{
self.r.mods_with_parse_errors.insert(def_id);
}
let transparent = AttributeParser::parse_limited(
self.r.tcx.sess,
&item.attrs,
sym::transparent,
item.span,
item.id,
None,
)
.is_some();
self.parent_scope.module = self.r.new_local_module(
Some(parent),
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
ModuleKind::Def(def_kind, def_id, Some((ident.name, transparent))),
expansion.to_expn_id(),
item.span,
parent.no_implicit_prelude
Expand Down Expand Up @@ -852,7 +865,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {

self.parent_scope.module = self.r.new_local_module(
Some(parent),
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
ModuleKind::Def(def_kind, def_id, Some((ident.name, false))),
expansion.to_expn_id(),
item.span,
parent.no_implicit_prelude,
Expand Down
19 changes: 16 additions & 3 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return Some((module.parent.unwrap().nearest_item_scope(), None));
}

if module.is_transparent() {
return Some((module.parent.unwrap().nearest_item_scope(), None));
}

// We need to support the next case under a deprecation warning
// ```
// struct MyStruct;
Expand Down Expand Up @@ -345,7 +349,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Encountered a module item, abandon ribs and look into that module and preludes.
let parent_scope = &ParentScope { module, ..*parent_scope };
let finalize = finalize.map(|f| Finalize { stage: Stage::Late, ..f });
return self
let binding = self
.cm()
.resolve_ident_in_scope_set(
orig_ident,
Expand All @@ -356,8 +360,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ignore_binding,
None,
)
.ok()
.map(LexicalScopeBinding::Item);
.ok();
match binding {
Some(binding) => {
return Some(LexicalScopeBinding::Item(binding));
}
None => {
if !module.is_transparent() {
return None;
}
}
}
}

if let RibKind::MacroDefinition(def) = rib.kind
Expand Down
15 changes: 12 additions & 3 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,17 +524,19 @@ enum ModuleKind {
/// * A normal module – either `mod from_file;` or `mod from_block { }` –
/// or the crate root (which is conceptually a top-level module).
/// The crate root will have `None` for the symbol.
/// * the bool in the tuple next to symbol represents whether the "transparent"
/// attribute is present on the module, only applies to named `mod` items.
/// * A trait or an enum (it implicitly contains associated types, methods and variant
/// constructors).
Def(DefKind, DefId, Option<Symbol>),
Def(DefKind, DefId, Option<(Symbol, bool)>),
Copy link
Member Author

Choose a reason for hiding this comment

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

This was the most obvious place to put this but it doesn't feel great, don't love all the places where I changed shit to name_and_transparent, open to suggestions for how to better thread this information through

}

impl ModuleKind {
/// Get name of the module.
fn name(&self) -> Option<Symbol> {
match *self {
ModuleKind::Block => None,
ModuleKind::Def(.., name) => name,
ModuleKind::Def(.., name_and_transparent) => name_and_transparent.map(|(name, _)| name),
}
}
}
Expand Down Expand Up @@ -759,6 +761,13 @@ impl<'ra> Module<'ra> {
}
}

fn is_transparent(self) -> bool {
match self.kind {
ModuleKind::Def(DefKind::Mod, _, Some((_, transparent))) => transparent,
_ => false,
}
}

fn is_ancestor_of(self, mut other: Self) -> bool {
while self != other {
if let Some(parent) = other.parent {
Expand Down Expand Up @@ -2449,7 +2458,7 @@ fn module_to_string(mut module: Module<'_>) -> Option<String> {
if let ModuleKind::Def(.., name) = module.kind {
if let Some(parent) = module.parent {
// `unwrap` is safe: the presence of a parent means it's not the crate root.
names.push(name.unwrap());
names.push(name.unwrap().0);
module = parent
} else {
break;
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,11 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
hygiene::update_dollar_crate_names(|ctxt| {
let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
match self.resolve_crate_root(ident).kind {
ModuleKind::Def(.., name) if let Some(name) = name => name,
ModuleKind::Def(.., name_and_transparent)
if let Some((name, _transparent)) = name_and_transparent =>
{
name
}
_ => kw::Crate,
}
});
Expand Down Expand Up @@ -1123,8 +1127,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
);
if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) {
let location = match parent_scope.module.kind {
ModuleKind::Def(kind, def_id, name) => {
if let Some(name) = name {
ModuleKind::Def(kind, def_id, name_and_transparent) => {
if let Some((name, _transparent)) = name_and_transparent {
format!("{} `{name}`", kind.descr(def_id))
} else {
"the crate root".to_string()
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2230,6 +2230,7 @@ symbols! {
transmute_unchecked,
transparent,
Copy link
Member Author

Choose a reason for hiding this comment

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

I feel like I'm probably doing this part subtly wrong.

The transparent symbol already exists and I'm using it, I tried seeing if I could stay away from it and find a way to have all the compiler internal stuff talk about it with a unique symbol (transparent_modules) but have the actual ident of the attribute be transparent but it seemed like this was controlling that. If I got that wrong lmk.

transparent_enums,
transparent_modules,
transparent_unions,
trivial_bounds,
truncf16,
Expand Down
47 changes: 47 additions & 0 deletions tests/ui/modules/no-transparent-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//@ check-fail
//@ edition:2018
// gate-test-transparent_modules
mod y {
macro_rules! s {
() => {};
}

pub(crate) use s;
}

trait IWantAMethod {
Copy link
Contributor

Choose a reason for hiding this comment

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

you'll also want a test in which you use #[transparent] without the feature gate to make sure it's properly rejected without the feature

Copy link
Contributor

Choose a reason for hiding this comment

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

note: this does work correctly, I just tested it, but you still want to have a test for it

fn method(&self) {}
}

impl IWantAMethod for () {}

fn foo() {
struct S;
impl S {
const NAME: &'static str = "S";
}
enum C {
B,
}

use y::s;
mod x {
// early resolution
s!(); //~ ERROR cannot find macro `s` in this scope
// late resolution
struct Y(S); //~ ERROR cannot find type `S` in this scope
impl Y {
// hir_typeck type dependent name resolution of associated const
const SNAME: &'static str = S::NAME; //~ ERROR failed to resolve: use of undeclared type `S`
}
fn bar() -> C {
//~^ ERROR cannot find type `C` in this scope
// method lookup, resolving appropriate trait in scope
().method(); //~ ERROR no method named `method` found for unit type `()` in the current scope
// hir ty lowering type dependent name resolution of associated enum variant
C::B //~ ERROR failed to resolve: use of undeclared type `C`
}
}
}

fn main() {}
79 changes: 79 additions & 0 deletions tests/ui/modules/no-transparent-fail.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
error: cannot find macro `s` in this scope
--> $DIR/no-transparent-fail.rs:30:9
|
LL | s!();
| ^
|
= help: have you added the `#[macro_use]` on the module/import?
help: consider importing this macro through its public re-export
|
LL + use crate::y::s;
|

error[E0412]: cannot find type `S` in this scope
--> $DIR/no-transparent-fail.rs:32:18
|
LL | struct Y(S);
| ^ not found in this scope
|
help: you might be missing a type parameter
|
LL | struct Y<S>(S);
| +++

error[E0412]: cannot find type `C` in this scope
--> $DIR/no-transparent-fail.rs:37:21
|
LL | struct Y(S);
| ------------ similarly named struct `Y` defined here
...
LL | fn bar() -> C {
| ^
|
help: a struct with a similar name exists
|
LL - fn bar() -> C {
LL + fn bar() -> Y {
|
help: you might be missing a type parameter
|
LL | fn bar<C>() -> C {
| +++

error[E0433]: failed to resolve: use of undeclared type `S`
--> $DIR/no-transparent-fail.rs:35:41
|
LL | const SNAME: &'static str = S::NAME;
| ^
| |
| use of undeclared type `S`
| help: a struct with a similar name exists: `Y`

error[E0599]: no method named `method` found for unit type `()` in the current scope
--> $DIR/no-transparent-fail.rs:40:16
|
LL | fn method(&self) {}
| ------ the method is available for `()` here
...
LL | ().method();
| ^^^^^^ method not found in `()`
|
= help: items from traits can only be used if the trait is in scope
help: trait `IWantAMethod` which provides `method` is implemented but not in scope; perhaps you want to import it
|
LL + use crate::IWantAMethod;
|

error[E0433]: failed to resolve: use of undeclared type `C`
--> $DIR/no-transparent-fail.rs:42:13
|
LL | C::B
| ^
| |
| use of undeclared type `C`
| help: a struct with a similar name exists: `Y`

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0412, E0433, E0599.
For more information about an error, try `rustc --explain E0412`.
Loading
Loading