Skip to content

Allow defining opaques in statics and consts #138911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 26, 2025
Merged
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
2 changes: 2 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -3535,6 +3535,7 @@ pub struct StaticItem {
pub safety: Safety,
pub mutability: Mutability,
pub expr: Option<P<Expr>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
}

#[derive(Clone, Encodable, Decodable, Debug)]
@@ -3543,6 +3544,7 @@ pub struct ConstItem {
pub generics: Generics,
pub ty: P<Ty>,
pub expr: Option<P<Expr>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
}

// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
54 changes: 38 additions & 16 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
@@ -987,10 +987,7 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
}
vis.visit_span(span);

for (id, path) in define_opaque.iter_mut().flatten() {
vis.visit_id(id);
vis.visit_path(path)
}
walk_define_opaques(vis, define_opaque);
}
FnKind::Closure(binder, coroutine_kind, decl, body) => {
vis.visit_closure_binder(binder);
@@ -1258,12 +1255,19 @@ impl WalkItemKind for ItemKind {
match self {
ItemKind::ExternCrate(_orig_name) => {}
ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => {
ItemKind::Static(box StaticItem {
ty,
safety: _,
mutability: _,
expr,
define_opaque,
}) => {
vis.visit_ty(ty);
visit_opt(expr, |expr| vis.visit_expr(expr));
walk_define_opaques(vis, define_opaque);
}
ItemKind::Const(item) => {
visit_const_item(item, vis);
walk_const_item(vis, item);
}
ItemKind::Fn(func) => {
vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id);
@@ -1382,7 +1386,7 @@ impl WalkItemKind for AssocItemKind {
) {
match self {
AssocItemKind::Const(item) => {
visit_const_item(item, visitor);
walk_const_item(visitor, item);
}
AssocItemKind::Fn(func) => {
visitor.visit_fn(
@@ -1442,14 +1446,13 @@ impl WalkItemKind for AssocItemKind {
}
}

fn visit_const_item<T: MutVisitor>(
ConstItem { defaultness, generics, ty, expr }: &mut ConstItem,
visitor: &mut T,
) {
visit_defaultness(visitor, defaultness);
visitor.visit_generics(generics);
visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr));
fn walk_const_item<T: MutVisitor>(vis: &mut T, item: &mut ConstItem) {
let ConstItem { defaultness, generics, ty, expr, define_opaque } = item;
visit_defaultness(vis, defaultness);
vis.visit_generics(generics);
vis.visit_ty(ty);
visit_opt(expr, |expr| vis.visit_expr(expr));
walk_define_opaques(vis, define_opaque);
}

fn walk_fn_header<T: MutVisitor>(vis: &mut T, header: &mut FnHeader) {
@@ -1526,9 +1529,16 @@ impl WalkItemKind for ForeignItemKind {
visitor: &mut impl MutVisitor,
) {
match self {
ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => {
ForeignItemKind::Static(box StaticItem {
ty,
mutability: _,
expr,
safety: _,
define_opaque,
}) => {
visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr));
walk_define_opaques(visitor, define_opaque);
}
ForeignItemKind::Fn(func) => {
visitor.visit_fn(
@@ -1929,6 +1939,18 @@ fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
}
}

fn walk_define_opaques<T: MutVisitor>(
vis: &mut T,
define_opaque: &mut Option<ThinVec<(NodeId, Path)>>,
) {
if let Some(define_opaque) = define_opaque {
for (id, path) in define_opaque {
vis.visit_id(id);
vis.visit_path(path)
}
}
}

/// Some value for the AST node that is valid but possibly meaningless. Similar
/// to `Default` but not intended for wide use. The value will never be used
/// meaningfully, it exists just to support unwinding in `visit_clobber` in the
53 changes: 46 additions & 7 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
pub use rustc_ast_ir::visit::VisitorResult;
pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
use rustc_span::{Ident, Span};
use thin_vec::ThinVec;

use crate::ast::*;
use crate::ptr::P;
@@ -371,14 +372,28 @@ impl WalkItemKind for ItemKind {
match self {
ItemKind::ExternCrate(_rename) => {}
ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, id, false)),
ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => {
ItemKind::Static(box StaticItem {
ty,
safety: _,
mutability: _,
expr,
define_opaque,
}) => {
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
ItemKind::Const(box ConstItem {
defaultness: _,
generics,
ty,
expr,
define_opaque,
}) => {
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
ItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func);
@@ -729,9 +744,16 @@ impl WalkItemKind for ForeignItemKind {
visitor: &mut V,
) -> V::Result {
match self {
ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => {
ForeignItemKind::Static(box StaticItem {
ty,
mutability: _,
expr,
safety: _,
define_opaque,
}) => {
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
ForeignItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func);
@@ -907,9 +929,7 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
try_visit!(visitor.visit_fn_decl(decl));
visit_opt!(visitor, visit_contract, contract);
visit_opt!(visitor, visit_block, body);
for (id, path) in define_opaque.iter().flatten() {
try_visit!(visitor.visit_path(path, *id))
}
try_visit!(walk_define_opaques(visitor, define_opaque));
}
FnKind::Closure(binder, coroutine_kind, decl, body) => {
try_visit!(visitor.visit_closure_binder(binder));
@@ -933,10 +953,17 @@ impl WalkItemKind for AssocItemKind {
visitor: &mut V,
) -> V::Result {
match self {
AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
AssocItemKind::Const(box ConstItem {
defaultness: _,
generics,
ty,
expr,
define_opaque,
}) => {
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
AssocItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func);
@@ -1337,3 +1364,15 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) -
}
V::Result::output()
}

fn walk_define_opaques<'a, V: Visitor<'a>>(
visitor: &mut V,
define_opaque: &'a Option<ThinVec<(NodeId, Path)>>,
) -> V::Result {
if let Some(define_opaque) = define_opaque {
for (id, path) in define_opaque {
try_visit!(visitor.visit_path(path, *id));
}
}
V::Result::output()
}
72 changes: 54 additions & 18 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -188,14 +188,21 @@ impl<'hir> LoweringContext<'_, 'hir> {

self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs)
}
ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => {
ItemKind::Static(box ast::StaticItem {
ty: t,
safety: _,
mutability: m,
expr: e,
define_opaque,
}) => {
debug_assert_ne!(ident.name, kw::Empty);
let ident = self.lower_ident(ident);
let (ty, body_id) =
self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
self.lower_define_opaque(hir_id, define_opaque);
hir::ItemKind::Static(ident, ty, *m, body_id)
}
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
ItemKind::Const(box ast::ConstItem { generics, ty, expr, define_opaque, .. }) => {
debug_assert_ne!(ident.name, kw::Empty);
let ident = self.lower_ident(ident);
let (generics, (ty, body_id)) = self.lower_generics(
@@ -206,6 +213,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy)
},
);
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, ty, generics, body_id)
}
ItemKind::Fn(box Fn {
@@ -243,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
span: this.lower_span(*fn_sig_span),
};
this.lower_define_opaque(hir_id, &define_opaque);
this.lower_define_opaque(hir_id, define_opaque);
let ident = this.lower_ident(ident);
hir::ItemKind::Fn {
ident,
@@ -662,7 +670,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
owner_id,
ident: self.lower_ident(i.ident),
kind: match &i.kind {
ForeignItemKind::Fn(box Fn { sig, generics, .. }) => {
ForeignItemKind::Fn(box Fn { sig, generics, define_opaque, .. }) => {
let fdec = &sig.decl;
let itctx = ImplTraitContext::Universal;
let (generics, (decl, fn_args)) =
@@ -683,17 +691,31 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Unmarked safety in unsafe block defaults to unsafe.
let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);

if define_opaque.is_some() {
self.dcx().span_err(i.span, "foreign functions cannot define opaque types");
}

hir::ForeignItemKind::Fn(
hir::FnSig { header, decl, span: self.lower_span(sig.span) },
fn_args,
generics,
)
}
ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => {
ForeignItemKind::Static(box StaticItem {
ty,
mutability,
expr: _,
safety,
define_opaque,
}) => {
let ty = self
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let safety = self.lower_safety(*safety, hir::Safety::Unsafe);

if define_opaque.is_some() {
self.dcx().span_err(i.span, "foreign statics cannot define opaque types");
}

hir::ForeignItemKind::Static(ty, *mutability, safety)
}
ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
@@ -801,7 +823,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let trait_item_def_id = hir_id.expect_owner();

let (generics, kind, has_default) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
AssocItemKind::Const(box ConstItem { generics, ty, expr, define_opaque, .. }) => {
let (generics, kind) = self.lower_generics(
generics,
i.id,
@@ -814,6 +836,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::TraitItemKind::Const(ty, body)
},
);

if define_opaque.is_some() {
if expr.is_some() {
self.lower_define_opaque(hir_id, &define_opaque);
} else {
self.dcx().span_err(
i.span,
"only trait consts with default bodies can define opaque types",
);
}
}

(generics, kind, expr.is_some())
}
AssocItemKind::Fn(box Fn { sig, generics, body: None, define_opaque, .. }) => {
@@ -951,18 +985,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);

let (generics, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
generics,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty =
this.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let body = this.lower_const_body(i.span, expr.as_deref());

hir::ImplItemKind::Const(ty, body)
},
),
AssocItemKind::Const(box ConstItem { generics, ty, expr, define_opaque, .. }) => self
.lower_generics(
generics,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let body = this.lower_const_body(i.span, expr.as_deref());
this.lower_define_opaque(hir_id, &define_opaque);

hir::ImplItemKind::Const(ty, body)
},
),
AssocItemKind::Fn(box Fn { sig, generics, body, contract, define_opaque, .. }) => {
let body_id = self.lower_maybe_coroutine_body(
sig.span,
Loading