diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 8dbf4c0ef32ef..8dd5bc6bf17e2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -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; diff --git a/compiler/rustc_attr_parsing/src/attributes/modules.rs b/compiler/rustc_attr_parsing/src/attributes/modules.rs new file mode 100644 index 0000000000000..ab3e7af865a93 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/modules.rs @@ -0,0 +1,9 @@ +use super::prelude::*; + +pub(crate) struct TransparentParser; +impl NoArgsAttributeParser for TransparentParser { + const PATH: &[Symbol] = &[sym::transparent]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Transparent; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e8bb4caa41664..08981764d310c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -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; @@ -241,6 +242,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, // tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 220388ffe4353..186ac59c4dfb1 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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"), + ErrorFollowing, EncodeCrossCrate::No, transparent_modules, experimental!(transparent) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8397cd294e0a9..24ac528c0212f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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)), /// Allows #[repr(transparent)] on unions (RFC 2645). (unstable, transparent_unions, "1.37.0", Some(60405)), /// Allows inconsistent bounds in where clauses. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 2435363ef0eb8..2c689dc919011 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -708,6 +708,9 @@ pub enum AttributeKind { /// Represents `#[track_caller]` TrackCaller(Span), + /// Represents `#[transparent]` mod attribute + Transparent(Span), + /// Represents `#[type_const]`. TypeConst(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 1611b865c7726..3ed47c31a5673 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -98,6 +98,7 @@ impl AttributeKind { StdInternalSymbol(..) => No, TargetFeature { .. } => No, TrackCaller(..) => Yes, + Transparent(..) => No, TypeConst(..) => Yes, TypeLengthLimit { .. } => No, UnsafeSpecializationMarker(..) => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c4ceba0840f13..37d12b4eeda34 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -225,6 +225,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_) + | AttributeKind::Transparent(_) | AttributeKind::Pointee(..) | AttributeKind::Dummy | AttributeKind::RustcBuiltinMacro { .. } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c10b6ca7e71b5..1b8139d60bbe6 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -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. @@ -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), @@ -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 @@ -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, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 3931c3c75f9fb..bbe0418b11351 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -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; @@ -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, @@ -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 diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index d9c3f4089a0fb..1bd0d497e3270 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -524,9 +524,11 @@ 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), + Def(DefKind, DefId, Option<(Symbol, bool)>), } impl ModuleKind { @@ -534,7 +536,7 @@ impl ModuleKind { fn name(&self) -> Option { match *self { ModuleKind::Block => None, - ModuleKind::Def(.., name) => name, + ModuleKind::Def(.., name_and_transparent) => name_and_transparent.map(|(name, _)| name), } } } @@ -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 { @@ -2449,7 +2458,7 @@ fn module_to_string(mut module: Module<'_>) -> Option { 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; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c50dfd41b51c8..1f5e31c4b7ded 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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, } }); @@ -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() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4f21189f9a7d5..a77a68d4eded5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2230,6 +2230,7 @@ symbols! { transmute_unchecked, transparent, transparent_enums, + transparent_modules, transparent_unions, trivial_bounds, truncf16, diff --git a/tests/ui/modules/no-transparent-fail.rs b/tests/ui/modules/no-transparent-fail.rs new file mode 100644 index 0000000000000..84426b2241da1 --- /dev/null +++ b/tests/ui/modules/no-transparent-fail.rs @@ -0,0 +1,47 @@ +//@ check-fail +//@ edition:2018 +// gate-test-transparent_modules +mod y { + macro_rules! s { + () => {}; + } + + pub(crate) use s; +} + +trait IWantAMethod { + 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() {} diff --git a/tests/ui/modules/no-transparent-fail.stderr b/tests/ui/modules/no-transparent-fail.stderr new file mode 100644 index 0000000000000..9e7510542483b --- /dev/null +++ b/tests/ui/modules/no-transparent-fail.stderr @@ -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); + | +++ + +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 { + | +++ + +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`. diff --git a/tests/ui/modules/transparent-fail.rs b/tests/ui/modules/transparent-fail.rs new file mode 100644 index 0000000000000..b4efc88da0679 --- /dev/null +++ b/tests/ui/modules/transparent-fail.rs @@ -0,0 +1,26 @@ +//@ check-fail +//@ edition:2018 +#![feature(transparent_modules)] + +mod limit_macro_lexical_scope { + #[macro_export] // add path-based scope name for macro to crate root + macro_rules! s { + () => {}; + } +} + +struct S; + +mod non_transparent { + fn foo() { + #[transparent] + mod transparent { + // early resolution + s!(); //~ ERROR cannot find macro `s` in this scope + // late resolution + struct Y(S); //~ ERROR cannot find type `S` in this scope + } + } +} + +fn main() {} diff --git a/tests/ui/modules/transparent-fail.stderr b/tests/ui/modules/transparent-fail.stderr new file mode 100644 index 0000000000000..d73dc984476bb --- /dev/null +++ b/tests/ui/modules/transparent-fail.stderr @@ -0,0 +1,26 @@ +error: cannot find macro `s` in this scope + --> $DIR/transparent-fail.rs:19:13 + | +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::s; + | + +error[E0412]: cannot find type `S` in this scope + --> $DIR/transparent-fail.rs:21:22 + | +LL | struct Y(S); + | ^ not found in this scope + | +help: consider importing this struct + | +LL + use crate::S; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/modules/transparent-pass.rs b/tests/ui/modules/transparent-pass.rs new file mode 100644 index 0000000000000..6c1a0da944d2d --- /dev/null +++ b/tests/ui/modules/transparent-pass.rs @@ -0,0 +1,48 @@ +//@ check-pass +//@ edition:2018 +#![feature(transparent_modules)] + +mod y { + macro_rules! s { + () => {}; + } + + pub(crate) use s; +} + +trait IWantAMethod { + fn method(&self) {} +} + +impl IWantAMethod for () {} + +fn foo() { + struct S; + impl S { + const NAME: &'static str = "S"; + } + enum C { + B, + } + + use y::s; + #[transparent] + mod x { + // early resolution + s!(); + // late resolution + struct Y(S); + impl Y { + // hir_typeck type dependent name resolution of associated const + const SNAME: &'static str = S::NAME; + } + fn bar() -> C { + // method lookup, resolving appropriate trait in scope + ().method(); + // hir ty lowering type dependent name resolution of associated enum variant + C::B + } + } +} + +pub fn main() {}