Auto merge of #139558 - camelid:mgca-const-items, r=oli-obk,BoxyUwU

mgca: Add ConstArg representation for const items

tracking issue: rust-lang/rust#132980
fixes rust-lang/rust#131046
fixes rust-lang/rust#134641

As part of implementing `min_generic_const_args`, we need to distinguish const items that can be used in the type system, such as in associated const equality projections, from const items containing arbitrary const code, which must be kept out of the type system. Specifically, all "type consts" must be either concrete (no generics) or generic with a trivial expression like `N` or a path to another type const item.

To syntactically distinguish these cases, we require, for now at least, that users annotate all type consts with the `#[type_const]` attribute. Then, we validate that the const's right-hand side is indeed eligible to be a type const and represent it differently in the HIR.

We accomplish this representation using a new `ConstItemRhs` enum in the HIR, and a similar but simpler enum in the AST. When `#[type_const]` is **not** applied to a const (e.g. on stable), we represent const item right-hand sides (rhs's) as HIR bodies, like before. However, when the attribute is applied, we instead lower to a `hir::ConstArg`. This syntactically distinguishes between trivial const args (paths) and arbitrary expressions, which are represented using `AnonConst`s. Then in `generics_of`, we can take advantage of the existing machinery to bar the `AnonConst` rhs's from using parent generics.
This commit is contained in:
bors 2025-11-08 22:31:33 +00:00
commit 72b21e1a64
117 changed files with 1180 additions and 758 deletions

View file

@ -3759,10 +3759,29 @@ pub struct ConstItem {
pub ident: Ident,
pub generics: Generics,
pub ty: Box<Ty>,
pub expr: Option<Box<Expr>>,
pub rhs: Option<ConstItemRhs>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum ConstItemRhs {
TypeConst(AnonConst),
Body(Box<Expr>),
}
impl ConstItemRhs {
pub fn span(&self) -> Span {
self.expr().span
}
pub fn expr(&self) -> &Expr {
match self {
ConstItemRhs::TypeConst(anon_const) => &anon_const.value,
ConstItemRhs::Body(expr) => expr,
}
}
}
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ItemKind {

View file

@ -423,6 +423,7 @@ macro_rules! common_visitor_and_walkers {
Closure,
Const,
ConstItem,
ConstItemRhs,
Defaultness,
Delegation,
DelegationMac,

View file

@ -170,37 +170,36 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
ItemKind::Static(box ast::StaticItem {
ident,
ty: t,
ty,
safety: _,
mutability: m,
expr: e,
define_opaque,
}) => {
let ident = self.lower_ident(*ident);
let (ty, body_id) =
self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
let ty =
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let body_id = self.lower_const_body(span, e.as_deref());
self.lower_define_opaque(hir_id, define_opaque);
hir::ItemKind::Static(*m, ident, ty, body_id)
}
ItemKind::Const(box ast::ConstItem {
ident,
generics,
ty,
expr,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => {
let ident = self.lower_ident(*ident);
let (generics, (ty, body_id)) = self.lower_generics(
let (generics, (ty, rhs)) = self.lower_generics(
generics,
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy)
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span);
(ty, rhs)
},
);
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, generics, ty, body_id)
hir::ItemKind::Const(ident, generics, ty, rhs)
}
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
@ -462,17 +461,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_const_item(
&mut self,
ty: &Ty,
span: Span,
body: Option<&Expr>,
impl_trait_position: ImplTraitPosition,
) -> (&'hir hir::Ty<'hir>, hir::BodyId) {
let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(impl_trait_position));
(ty, self.lower_const_body(span, body))
}
#[instrument(level = "debug", skip(self))]
fn lower_use_tree(
&mut self,
@ -804,12 +792,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (ident, generics, kind, has_default) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
expr,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => {
let (generics, kind) = self.lower_generics(
generics,
@ -818,14 +801,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
|this| {
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let body = expr.as_ref().map(|x| this.lower_const_body(i.span, Some(x)));
hir::TraitItemKind::Const(ty, body)
let rhs = rhs
.as_ref()
.map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span));
hir::TraitItemKind::Const(ty, rhs)
},
);
if define_opaque.is_some() {
if expr.is_some() {
if rhs.is_some() {
self.lower_define_opaque(hir_id, &define_opaque);
} else {
self.dcx().span_err(
@ -835,7 +819,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
(*ident, generics, kind, expr.is_some())
(*ident, generics, kind, rhs.is_some())
}
AssocItemKind::Fn(box Fn {
sig, ident, generics, body: None, define_opaque, ..
@ -1022,12 +1006,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (ident, (generics, kind)) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
expr,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => (
*ident,
self.lower_generics(
@ -1037,9 +1016,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|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)
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span);
hir::ImplItemKind::Const(ty, rhs)
},
),
),

View file

@ -2327,6 +2327,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind })
}
fn lower_const_item_rhs(
&mut self,
attrs: &[hir::Attribute],
rhs: Option<&ConstItemRhs>,
span: Span,
) -> hir::ConstItemRhs<'hir> {
match rhs {
Some(ConstItemRhs::TypeConst(anon)) => {
hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg(anon))
}
None if attr::contains_name(attrs, sym::type_const) => {
let const_arg = ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Error(
DUMMY_SP,
self.dcx().span_delayed_bug(DUMMY_SP, "no block"),
),
};
hir::ConstItemRhs::TypeConst(self.arena.alloc(const_arg))
}
Some(ConstItemRhs::Body(body)) => {
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
}
None => hir::ConstItemRhs::Body(self.lower_const_body(span, None)),
}
}
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_anon_const`].
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> {

View file

@ -1239,9 +1239,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
});
}
ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
ItemKind::Const(box ConstItem { defaultness, rhs, .. }) => {
self.check_defaultness(item.span, *defaultness);
if expr.is_none() {
if rhs.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
@ -1581,7 +1581,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let AssocCtxt::Impl { .. } = ctxt {
match &item.kind {
AssocItemKind::Const(box ConstItem { expr: None, .. }) => {
AssocItemKind::Const(box ConstItem { rhs: None, .. }) => {
self.dcx().emit_err(errors::AssocConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),

View file

@ -210,7 +210,7 @@ impl<'a> State<'a> {
ident,
generics,
ty,
expr,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -218,7 +218,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
expr.as_deref(),
rhs.as_ref().map(|ct| ct.expr()),
&item.vis,
ast::Safety::Default,
*defaultness,
@ -566,7 +566,7 @@ impl<'a> State<'a> {
ident,
generics,
ty,
expr,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -574,7 +574,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
expr.as_deref(),
rhs.as_ref().map(|ct| ct.expr()),
vis,
ast::Safety::Default,
*defaultness,

View file

@ -43,7 +43,7 @@ pub(crate) fn expand(
// Generate anonymous constant serving as container for the allocator methods.
let const_ty = ecx.ty(sig_span, TyKind::Tup(ThinVec::new()));
let const_body = ecx.expr_block(ecx.block(span, stmts));
let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts)));
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
let const_item = if is_stmt {
Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item)))

View file

@ -47,7 +47,7 @@ pub(crate) fn expand(
// Generate anonymous constant serving as container for the allocator methods.
let const_ty = ecx.ty(ty_span, TyKind::Tup(ThinVec::new()));
let const_body = ecx.expr_block(ecx.block(span, stmts));
let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts)));
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
let const_item = if is_stmt {
Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item)))

View file

@ -385,9 +385,9 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box<ast::Item> {
cx.attr_nested_word(sym::allow, sym::deprecated, span),
]);
let block = cx.expr_block(
let block = ast::ConstItemRhs::Body(cx.expr_block(
cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
);
));
let anon_constant = cx.item_const(
span,

View file

@ -289,7 +289,7 @@ pub(crate) fn expand_test_or_bench(
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
define_opaque: None,
// test::TestDescAndFn {
expr: Some(
rhs: Some(ast::ConstItemRhs::Body(
cx.expr_struct(
sp,
test_path("TestDescAndFn"),
@ -371,7 +371,7 @@ pub(crate) fn expand_test_or_bench(
field("testfn", test_fn), // }
],
), // }
),
)),
}
.into(),
),

View file

@ -6,7 +6,9 @@
// having basically only two use-cases that act in different ways.
use rustc_errors::ErrorGuaranteed;
use rustc_hir::LangItem;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::{LangItem, find_attr};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, AdtDef, Ty};
@ -365,8 +367,14 @@ where
// check performed after the promotion. Verify that with an assertion.
assert!(promoted.is_none() || Q::ALLOW_PROMOTED);
// Don't peek inside trait associated constants.
if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() {
// Avoid looking at attrs of anon consts as that will ICE
let is_type_const_item =
matches!(cx.tcx.def_kind(def), DefKind::Const | DefKind::AssocConst)
&& find_attr!(cx.tcx.get_all_attrs(def), AttributeKind::TypeConst(_));
// Don't peak inside trait associated constants, also `#[type_const] const` items
// don't have bodies so there's nothing to look at
if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() && !is_type_const_item {
let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def);
if !Q::in_qualifs(&qualifs) {

View file

@ -104,6 +104,10 @@ impl<'a> ExtCtxt<'a> {
}
}
pub fn anon_const_block(&self, b: Box<ast::Block>) -> Box<ast::AnonConst> {
Box::new(self.anon_const(b.span, ast::ExprKind::Block(b, None)))
}
pub fn const_ident(&self, span: Span, ident: Ident) -> ast::AnonConst {
self.anon_const(span, ast::ExprKind::Path(None, self.path_ident(span, ident)))
}
@ -722,7 +726,7 @@ impl<'a> ExtCtxt<'a> {
span: Span,
ident: Ident,
ty: Box<ast::Ty>,
expr: Box<ast::Expr>,
rhs: ast::ConstItemRhs,
) -> Box<ast::Item> {
let defaultness = ast::Defaultness::Final;
self.item(
@ -735,7 +739,7 @@ impl<'a> ExtCtxt<'a> {
// FIXME(generic_const_items): Pass the generics as a parameter.
generics: ast::Generics::default(),
ty,
expr: Some(expr),
rhs: Some(rhs),
define_opaque: None,
}
.into(),

View file

@ -403,6 +403,28 @@ impl<'hir> PathSegment<'hir> {
}
}
#[derive(Clone, Copy, Debug, HashStable_Generic)]
pub enum ConstItemRhs<'hir> {
Body(BodyId),
TypeConst(&'hir ConstArg<'hir>),
}
impl<'hir> ConstItemRhs<'hir> {
pub fn hir_id(&self) -> HirId {
match self {
ConstItemRhs::Body(body_id) => body_id.hir_id,
ConstItemRhs::TypeConst(ct_arg) => ct_arg.hir_id,
}
}
pub fn span<'tcx>(&self, tcx: impl crate::intravisit::HirTyCtxt<'tcx>) -> Span {
match self {
ConstItemRhs::Body(body_id) => tcx.hir_body(*body_id).value.span,
ConstItemRhs::TypeConst(ct_arg) => ct_arg.span(),
}
}
}
/// A constant that enters the type system, used for arguments to const generics (e.g. array lengths).
///
/// These are distinct from [`AnonConst`] as anon consts in the type system are not allowed
@ -474,6 +496,7 @@ impl<'hir, Unambig> ConstArg<'hir, Unambig> {
match self.kind {
ConstArgKind::Path(path) => path.span(),
ConstArgKind::Anon(anon) => anon.span,
ConstArgKind::Error(span, _) => span,
ConstArgKind::Infer(span, _) => span,
}
}
@ -490,6 +513,8 @@ pub enum ConstArgKind<'hir, Unambig = ()> {
/// However, in the future, we'll be using it for all of those.
Path(QPath<'hir>),
Anon(&'hir AnonConst),
/// Error const
Error(Span, ErrorGuaranteed),
/// This variant is not always used to represent inference consts, sometimes
/// [`GenericArg::Infer`] is used instead.
Infer(Span, Unambig),
@ -3076,8 +3101,8 @@ impl<'hir> TraitItem<'hir> {
}
expect_methods_self_kind! {
expect_const, (&'hir Ty<'hir>, Option<BodyId>),
TraitItemKind::Const(ty, body), (ty, *body);
expect_const, (&'hir Ty<'hir>, Option<ConstItemRhs<'hir>>),
TraitItemKind::Const(ty, rhs), (ty, *rhs);
expect_fn, (&FnSig<'hir>, &TraitFn<'hir>),
TraitItemKind::Fn(ty, trfn), (ty, trfn);
@ -3101,7 +3126,7 @@ pub enum TraitFn<'hir> {
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum TraitItemKind<'hir> {
/// An associated constant with an optional value (otherwise `impl`s must contain a value).
Const(&'hir Ty<'hir>, Option<BodyId>),
Const(&'hir Ty<'hir>, Option<ConstItemRhs<'hir>>),
/// An associated function with an optional body.
Fn(FnSig<'hir>, TraitFn<'hir>),
/// An associated type with (possibly empty) bounds and optional concrete
@ -3170,9 +3195,9 @@ impl<'hir> ImplItem<'hir> {
}
expect_methods_self_kind! {
expect_const, (&'hir Ty<'hir>, BodyId), ImplItemKind::Const(ty, body), (ty, *body);
expect_fn, (&FnSig<'hir>, BodyId), ImplItemKind::Fn(ty, body), (ty, *body);
expect_type, &'hir Ty<'hir>, ImplItemKind::Type(ty), ty;
expect_const, (&'hir Ty<'hir>, ConstItemRhs<'hir>), ImplItemKind::Const(ty, rhs), (ty, *rhs);
expect_fn, (&FnSig<'hir>, BodyId), ImplItemKind::Fn(ty, body), (ty, *body);
expect_type, &'hir Ty<'hir>, ImplItemKind::Type(ty), ty;
}
}
@ -3181,7 +3206,7 @@ impl<'hir> ImplItem<'hir> {
pub enum ImplItemKind<'hir> {
/// An associated constant of the given type, set to the constant result
/// of the expression.
Const(&'hir Ty<'hir>, BodyId),
Const(&'hir Ty<'hir>, ConstItemRhs<'hir>),
/// An associated function implementation with the given signature and body.
Fn(FnSig<'hir>, BodyId),
/// An associated type.
@ -4110,8 +4135,8 @@ impl<'hir> Item<'hir> {
expect_static, (Mutability, Ident, &'hir Ty<'hir>, BodyId),
ItemKind::Static(mutbl, ident, ty, body), (*mutbl, *ident, ty, *body);
expect_const, (Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, BodyId),
ItemKind::Const(ident, generics, ty, body), (*ident, generics, ty, *body);
expect_const, (Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, ConstItemRhs<'hir>),
ItemKind::Const(ident, generics, ty, rhs), (*ident, generics, ty, *rhs);
expect_fn, (Ident, &FnSig<'hir>, &'hir Generics<'hir>, BodyId),
ItemKind::Fn { ident, sig, generics, body, .. }, (*ident, sig, generics, *body);
@ -4282,7 +4307,7 @@ pub enum ItemKind<'hir> {
/// A `static` item.
Static(Mutability, Ident, &'hir Ty<'hir>, BodyId),
/// A `const` item.
Const(Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, BodyId),
Const(Ident, &'hir Generics<'hir>, &'hir Ty<'hir>, ConstItemRhs<'hir>),
/// A function declaration.
Fn {
sig: FnSig<'hir>,
@ -4520,17 +4545,18 @@ impl<'hir> OwnerNode<'hir> {
OwnerNode::Item(Item {
kind:
ItemKind::Static(_, _, _, body)
| ItemKind::Const(_, _, _, body)
| ItemKind::Const(.., ConstItemRhs::Body(body))
| ItemKind::Fn { body, .. },
..
})
| OwnerNode::TraitItem(TraitItem {
kind:
TraitItemKind::Fn(_, TraitFn::Provided(body)) | TraitItemKind::Const(_, Some(body)),
TraitItemKind::Fn(_, TraitFn::Provided(body))
| TraitItemKind::Const(_, Some(ConstItemRhs::Body(body))),
..
})
| OwnerNode::ImplItem(ImplItem {
kind: ImplItemKind::Fn(_, body) | ImplItemKind::Const(_, body),
kind: ImplItemKind::Fn(_, body) | ImplItemKind::Const(_, ConstItemRhs::Body(body)),
..
}) => Some(*body),
_ => None,
@ -4781,7 +4807,7 @@ impl<'hir> Node<'hir> {
Node::Item(Item {
owner_id,
kind:
ItemKind::Const(_, _, _, body)
ItemKind::Const(.., ConstItemRhs::Body(body))
| ItemKind::Static(.., body)
| ItemKind::Fn { body, .. },
..
@ -4789,12 +4815,13 @@ impl<'hir> Node<'hir> {
| Node::TraitItem(TraitItem {
owner_id,
kind:
TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)),
TraitItemKind::Const(.., Some(ConstItemRhs::Body(body)))
| TraitItemKind::Fn(_, TraitFn::Provided(body)),
..
})
| Node::ImplItem(ImplItem {
owner_id,
kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body),
kind: ImplItemKind::Const(.., ConstItemRhs::Body(body)) | ImplItemKind::Fn(_, body),
..
}) => Some((owner_id.def_id, *body)),

View file

@ -369,6 +369,10 @@ pub trait Visitor<'v>: Sized {
walk_ty(self, t)
}
fn visit_const_item_rhs(&mut self, c: ConstItemRhs<'v>) -> Self::Result {
walk_const_item_rhs(self, c)
}
/// All consts are treated as ambiguous consts for the purposes of hir visiting in
/// order to ensure that visitors can handle infer vars without it being too error-prone.
///
@ -547,11 +551,11 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
try_visit!(visitor.visit_ty_unambig(typ));
try_visit!(visitor.visit_nested_body(body));
}
ItemKind::Const(ident, ref generics, ref typ, body) => {
ItemKind::Const(ident, ref generics, ref typ, rhs) => {
try_visit!(visitor.visit_ident(ident));
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_ty_unambig(typ));
try_visit!(visitor.visit_nested_body(body));
try_visit!(visitor.visit_const_item_rhs(rhs));
}
ItemKind::Fn { ident, sig, generics, body: body_id, .. } => {
try_visit!(visitor.visit_ident(ident));
@ -1036,6 +1040,16 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -
V::Result::output()
}
pub fn walk_const_item_rhs<'v, V: Visitor<'v>>(
visitor: &mut V,
ct_rhs: ConstItemRhs<'v>,
) -> V::Result {
match ct_rhs {
ConstItemRhs::Body(body_id) => visitor.visit_nested_body(body_id),
ConstItemRhs::TypeConst(const_arg) => visitor.visit_const_arg_unambig(const_arg),
}
}
pub fn walk_unambig_const_arg<'v, V: Visitor<'v>>(
visitor: &mut V,
const_arg: &'v ConstArg<'v>,
@ -1058,6 +1072,7 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
match kind {
ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()),
ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
ConstArgKind::Error(_, _) => V::Result::output(), // errors and spans are not important
}
}
@ -1220,7 +1235,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(
match *kind {
TraitItemKind::Const(ref ty, default) => {
try_visit!(visitor.visit_ty_unambig(ty));
visit_opt!(visitor, visit_nested_body, default);
visit_opt!(visitor, visit_const_item_rhs, default);
}
TraitItemKind::Fn(ref sig, TraitFn::Required(param_idents)) => {
try_visit!(visitor.visit_fn_decl(sig.decl));
@ -1273,9 +1288,9 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(
}
}
match *kind {
ImplItemKind::Const(ref ty, body) => {
ImplItemKind::Const(ref ty, rhs) => {
try_visit!(visitor.visit_ty_unambig(ty));
visitor.visit_nested_body(body)
visitor.visit_const_item_rhs(rhs)
}
ImplItemKind::Fn(ref sig, body_id) => visitor.visit_fn(
FnKind::Method(impl_item.ident, sig),

View file

@ -96,6 +96,7 @@ pub(crate) fn provide(providers: &mut Providers) {
rendered_precise_capturing_args,
const_param_default,
anon_const_kind,
const_of_item,
..*providers
};
}
@ -1562,6 +1563,7 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
let const_arg_id = tcx.parent_hir_id(hir_id);
match tcx.hir_node(const_arg_id) {
hir::Node::ConstArg(_) => {
let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id));
if tcx.features().generic_const_exprs() {
ty::AnonConstKind::GCE
} else if tcx.features().min_generic_const_args() {
@ -1569,7 +1571,7 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
} else if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Repeat(_, repeat_count),
..
}) = tcx.hir_node(tcx.parent_hir_id(const_arg_id))
}) = parent_hir_node
&& repeat_count.hir_id == const_arg_id
{
ty::AnonConstKind::RepeatExprCount
@ -1580,3 +1582,38 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
_ => ty::AnonConstKind::NonTypeSystem,
}
}
#[instrument(level = "debug", skip(tcx), ret)]
fn const_of_item<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
let ct_rhs = match tcx.hir_node_by_def_id(def_id) {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(.., ct), .. }) => *ct,
hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Const(.., ct), ..
}) => ct.expect("no default value for trait assoc const"),
hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(.., ct), .. }) => *ct,
_ => {
span_bug!(tcx.def_span(def_id), "`const_of_item` expected a const or assoc const item")
}
};
let ct_arg = match ct_rhs {
hir::ConstItemRhs::TypeConst(ct_arg) => ct_arg,
hir::ConstItemRhs::Body(body_id) => {
bug!("cannot call const_of_item on a non-type_const {body_id:?}")
}
};
let icx = ItemCtxt::new(tcx, def_id);
let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id);
let ct = icx
.lowerer()
.lower_const_arg(ct_arg, FeedConstTy::Param(def_id.to_def_id(), identity_args));
if let Err(e) = icx.check_tainted_by_errors()
&& !ct.references_error()
{
ty::EarlyBinder::bind(Const::new_error(tcx, e))
} else {
ty::EarlyBinder::bind(ct)
}
}

View file

@ -158,14 +158,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
}
TraitItemKind::Const(ty, body_id) => body_id
.and_then(|body_id| {
TraitItemKind::Const(ty, rhs) => rhs
.and_then(|rhs| {
ty.is_suggestable_infer_ty().then(|| {
infer_placeholder_type(
icx.lowerer(),
def_id,
body_id,
rhs.hir_id(),
ty.span,
rhs.span(tcx),
item.ident,
"associated constant",
)
@ -183,13 +184,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
}
ImplItemKind::Const(ty, body_id) => {
ImplItemKind::Const(ty, rhs) => {
if ty.is_suggestable_infer_ty() {
infer_placeholder_type(
icx.lowerer(),
def_id,
body_id,
rhs.hir_id(),
ty.span,
rhs.span(tcx),
item.ident,
"associated constant",
)
@ -212,8 +214,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
infer_placeholder_type(
icx.lowerer(),
def_id,
body_id,
body_id.hir_id,
ty.span,
tcx.hir_body(body_id).value.span,
ident,
"static variable",
)
@ -229,13 +232,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
}
}
}
ItemKind::Const(ident, _, ty, body_id) => {
ItemKind::Const(ident, _, ty, rhs) => {
if ty.is_suggestable_infer_ty() {
infer_placeholder_type(
icx.lowerer(),
def_id,
body_id,
rhs.hir_id(),
ty.span,
rhs.span(tcx),
ident,
"constant",
)
@ -425,13 +429,14 @@ pub(super) fn type_of_opaque_hir_typeck(
fn infer_placeholder_type<'tcx>(
cx: &dyn HirTyLowerer<'tcx>,
def_id: LocalDefId,
body_id: hir::BodyId,
span: Span,
hir_id: HirId,
ty_span: Span,
body_span: Span,
item_ident: Ident,
kind: &'static str,
) -> Ty<'tcx> {
let tcx = cx.tcx();
let ty = tcx.typeck(def_id).node_type(body_id.hir_id);
let ty = tcx.typeck(def_id).node_type(hir_id);
// If this came from a free `const` or `static mut?` item,
// then the user may have written e.g. `const A = 42;`.
@ -439,10 +444,10 @@ fn infer_placeholder_type<'tcx>(
// us to improve in typeck so we do that now.
let guar = cx
.dcx()
.try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
.try_steal_modify_and_emit_err(ty_span, StashKey::ItemNoType, |err| {
if !ty.references_error() {
// Only suggest adding `:` if it was missing (and suggested by parsing diagnostic).
let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
let colon = if ty_span == item_ident.span.shrink_to_hi() { ":" } else { "" };
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
// We are typeck and have the real type, so remove that and suggest the actual type.
@ -452,14 +457,14 @@ fn infer_placeholder_type<'tcx>(
if let Some(ty) = ty.make_suggestable(tcx, false, None) {
err.span_suggestion(
span,
ty_span,
format!("provide a type for the {kind}"),
format!("{colon} {ty}"),
Applicability::MachineApplicable,
);
} else {
with_forced_trimmed_paths!(err.span_note(
tcx.hir_body(body_id).value.span,
body_span,
format!("however, the inferred type `{ty}` cannot be named"),
));
}
@ -473,7 +478,7 @@ fn infer_placeholder_type<'tcx>(
}
// If we didn't find any infer tys, then just fallback to `span`.
if visitor.spans.is_empty() {
visitor.spans.push(span);
visitor.spans.push(ty_span);
}
let mut diag = bad_placeholder(cx, visitor.spans, kind);
@ -482,20 +487,20 @@ fn infer_placeholder_type<'tcx>(
// same span. If this happens, we will fall through to this arm, so
// we need to suppress the suggestion since it's invalid. Ideally we
// would suppress the duplicated error too, but that's really hard.
if span.is_empty() && span.from_expansion() {
if ty_span.is_empty() && ty_span.from_expansion() {
// An approximately better primary message + no suggestion...
diag.primary_message("missing type for item");
} else if !ty.references_error() {
if let Some(ty) = ty.make_suggestable(tcx, false, None) {
diag.span_suggestion_verbose(
span,
ty_span,
"replace this with a fully-specified type",
ty,
Applicability::MachineApplicable,
);
} else {
with_forced_trimmed_paths!(diag.span_note(
tcx.hir_body(body_id).value.span,
body_span,
format!("however, the inferred type `{ty}` cannot be named"),
));
}

View file

@ -4,9 +4,10 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::PolyTraitRef;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_hir::{PolyTraitRef, find_attr};
use rustc_middle::bug;
use rustc_middle::ty::{
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@ -602,7 +603,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
term,
})
});
bounds.push((bound.upcast(tcx), constraint.span));
if let ty::AssocTag::Const = assoc_tag
&& !find_attr!(
self.tcx().get_all_attrs(assoc_item.def_id),
AttributeKind::TypeConst(_)
)
{
if tcx.features().min_generic_const_args()
|| tcx.features().associated_const_equality()
{
let mut err = self.dcx().struct_span_err(
constraint.span,
"use of trait associated const without `#[type_const]`",
);
err.note("the declaration in the trait must be marked with `#[type_const]`");
return Err(err.emit());
} else {
let err = self.dcx().span_delayed_bug(
constraint.span,
"use of trait associated const without `#[type_const]`",
);
return Err(err);
}
} else {
bounds.push((bound.upcast(tcx), constraint.span));
}
}
// SelfTraitThatDefines is only interested in trait predicates.
PredicateFilter::SelfTraitThatDefines(_) => {}

View file

@ -27,9 +27,10 @@ use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err,
};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId, find_attr};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::DynCompatibilityViolation;
use rustc_macros::{TypeFoldable, TypeVisitable};
@ -1278,7 +1279,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
LowerTypeRelativePathMode::Const,
)? {
TypeRelativePath::AssocItem(def_id, args) => {
if !tcx.associated_item(def_id).is_type_const_capable(tcx) {
if !find_attr!(self.tcx().get_all_attrs(def_id), AttributeKind::TypeConst(_)) {
let mut err = self.dcx().struct_span_err(
span,
"use of trait associated const without `#[type_const]`",
@ -1716,6 +1717,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::AssocTag::Const,
) {
Ok((item_def_id, item_args)) => {
if !find_attr!(self.tcx().get_all_attrs(item_def_id), AttributeKind::TypeConst(_)) {
let mut err = self.dcx().struct_span_err(
span,
"use of `const` in the type system without `#[type_const]`",
);
err.note("the declaration must be marked with `#[type_const]`");
return Const::new_error(self.tcx(), err.emit());
}
let uv = ty::UnevaluatedConst::new(item_def_id, item_args);
Const::new_unevaluated(self.tcx(), uv)
}
@ -2242,6 +2252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
hir::ConstArgKind::Error(_, e) => ty::Const::new_error(tcx, e),
}
}

View file

@ -87,13 +87,16 @@ mod variance;
pub use errors::NoVariantNamed;
use rustc_abi::{CVariadicStatus, ExternAbi};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::lints::DelayedLint;
use rustc_hir::{self as hir};
use rustc_middle::middle;
use rustc_hir::{
find_attr, {self as hir},
};
use rustc_middle::mir::interpret::GlobalId;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_middle::ty::{Const, Ty, TyCtxt};
use rustc_middle::{middle, ty};
use rustc_session::parse::feature_err;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
@ -223,7 +226,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
tcx.ensure_ok().eval_static_initializer(item_def_id);
check::maybe_check_static_with_link_section(tcx, item_def_id);
}
DefKind::Const if !tcx.generics_of(item_def_id).own_requires_monomorphization() => {
DefKind::Const
if !tcx.generics_of(item_def_id).own_requires_monomorphization()
&& !find_attr!(tcx.get_all_attrs(item_def_id), AttributeKind::TypeConst(_)) =>
{
// FIXME(generic_const_items): Passing empty instead of identity args is fishy but
// seems to be fine for now. Revisit this!
let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty());

View file

@ -523,17 +523,17 @@ impl<'a> State<'a> {
ident: Ident,
generics: &hir::Generics<'_>,
ty: &hir::Ty<'_>,
default: Option<hir::BodyId>,
default: Option<hir::ConstItemRhs<'_>>,
) {
self.word_space("const");
self.print_ident(ident);
self.print_generic_params(generics.params);
self.word_space(":");
self.print_type(ty);
if let Some(expr) = default {
if let Some(ct_rhs) = default {
self.space();
self.word_space("=");
self.ann.nested(self, Nested::Body(expr));
self.print_const_item_rhs(ct_rhs);
}
self.print_where_clause(generics);
self.word(";")
@ -616,7 +616,7 @@ impl<'a> State<'a> {
self.word(";");
self.end(cb);
}
hir::ItemKind::Const(ident, generics, ty, expr) => {
hir::ItemKind::Const(ident, generics, ty, rhs) => {
let (cb, ib) = self.head("const");
self.print_ident(ident);
self.print_generic_params(generics.params);
@ -626,7 +626,7 @@ impl<'a> State<'a> {
self.end(ib);
self.word_space("=");
self.ann.nested(self, Nested::Body(expr));
self.print_const_item_rhs(rhs);
self.print_where_clause(generics);
self.word(";");
self.end(cb);
@ -1127,10 +1127,18 @@ impl<'a> State<'a> {
self.ann.nested(self, Nested::Body(constant.body))
}
fn print_const_item_rhs(&mut self, ct_rhs: hir::ConstItemRhs<'_>) {
match ct_rhs {
hir::ConstItemRhs::Body(body_id) => self.ann.nested(self, Nested::Body(body_id)),
hir::ConstItemRhs::TypeConst(const_arg) => self.print_const_arg(const_arg),
}
}
fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) {
match &const_arg.kind {
ConstArgKind::Path(qpath) => self.print_qpath(qpath, true),
ConstArgKind::Anon(anon) => self.print_anon_const(anon),
ConstArgKind::Error(_, _) => self.word("/*ERROR*/"),
ConstArgKind::Infer(..) => self.word("_"),
}
}

View file

@ -1583,13 +1583,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> bool {
if let Some(def_id) = opt_def_id
&& let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Const(_, _, _, body_id),
kind: hir::ItemKind::Const(_, _, _, ct_rhs),
..
})) = self.tcx.hir_get_if_local(def_id)
&& let hir::Node::Expr(expr) = self.tcx.hir_node(body_id.hir_id)
&& let hir::Node::Expr(expr) = self.tcx.hir_node(ct_rhs.hir_id())
&& hir::is_range_literal(expr)
{
let span = self.tcx.hir_span(body_id.hir_id);
let span = self.tcx.hir_span(ct_rhs.hir_id());
if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) {
e.span_suggestion_verbose(
ident.span,

View file

@ -978,9 +978,9 @@ impl<'tcx> LateContext<'tcx> {
..
}) => *init,
hir::Node::Item(item) => match item.kind {
hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => {
Some(self.tcx.hir_body(body_id).value)
}
// FIXME(mgca): figure out how to handle ConstArgKind::Path (or don't but add warning in docs here)
hir::ItemKind::Const(.., hir::ConstItemRhs::Body(body_id))
| hir::ItemKind::Static(.., body_id) => Some(self.tcx.hir_body(body_id).value),
_ => None,
},
_ => None,

View file

@ -1032,19 +1032,22 @@ trait UnusedDelimLint {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
use ast::ItemKind::*;
if let Const(box ast::ConstItem { expr: Some(expr), .. })
| Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
{
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::AssignedValue,
false,
None,
None,
false,
);
}
let expr = if let Const(box ast::ConstItem { rhs: Some(rhs), .. }) = &item.kind {
rhs.expr()
} else if let Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind {
expr
} else {
return;
};
self.check_unused_delims_expr(
cx,
expr,
UnusedDelimsCtx::AssignedValue,
false,
None,
None,
false,
);
}
}

View file

@ -403,6 +403,7 @@ provide! { tcx, def_id, other, cdata,
tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index))
}
anon_const_kind => { table }
const_of_item => { table }
}
pub(in crate::rmeta) fn provide(providers: &mut Providers) {

View file

@ -1350,6 +1350,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool {
fn should_encode_const(def_kind: DefKind) -> bool {
match def_kind {
// FIXME(mgca): should we remove Const and AssocConst here?
DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst => true,
DefKind::Struct
@ -1382,6 +1383,21 @@ fn should_encode_const(def_kind: DefKind) -> bool {
}
}
fn should_encode_const_of_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool {
matches!(def_kind, DefKind::Const | DefKind::AssocConst)
&& find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_))
// AssocConst ==> assoc item has value
&& (!matches!(def_kind, DefKind::AssocConst) || assoc_item_has_value(tcx, def_id))
}
fn assoc_item_has_value<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
let assoc_item = tcx.associated_item(def_id);
match assoc_item.container {
ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true,
ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(),
}
}
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_attrs(&mut self, def_id: LocalDefId) {
let tcx = self.tcx;
@ -1428,7 +1444,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
&& match tcx.hir_node_by_def_id(local_id) {
hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind {
// Skip encoding defs for these as they should not have had a `DefId` created
hir::ConstArgKind::Path(..) | hir::ConstArgKind::Infer(..) => true,
hir::ConstArgKind::Error(..)
| hir::ConstArgKind::Path(..)
| hir::ConstArgKind::Infer(..) => true,
hir::ConstArgKind::Anon(..) => false,
},
_ => false,
@ -1601,6 +1619,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if let DefKind::AnonConst = def_kind {
record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id));
}
if should_encode_const_of_item(self.tcx, def_id, def_kind) {
record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id));
}
if tcx.impl_method_has_trait_impl_trait_tys(def_id)
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
{

View file

@ -471,6 +471,7 @@ define_tables! {
assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
opaque_ty_origin: Table<DefIndex, LazyValue<hir::OpaqueTyOrigin<DefId>>>,
anon_const_kind: Table<DefIndex, LazyValue<ty::AnonConstKind>>,
const_of_item: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::Const<'static>>>>,
associated_types_for_impl_traits_in_trait_or_impl: Table<DefIndex, LazyValue<DefIdMap<Vec<DefId>>>>,
}

View file

@ -47,7 +47,7 @@ macro_rules! arena_types {
rustc_middle::traits::query::DropckOutlivesResult<'tcx>
>
>,
[] normalize_canonicalized_projection_ty:
[] normalize_canonicalized_projection:
rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx,
rustc_middle::traits::query::NormalizationResult<'tcx>

View file

@ -293,6 +293,19 @@ rustc_queries! {
separate_provide_extern
}
/// Returns the const of the RHS of a (free or assoc) const item, if it is a `#[type_const]`.
///
/// When a const item is used in a type-level expression, like in equality for an assoc const
/// projection, this allows us to retrieve the typesystem-appropriate representation of the
/// const value.
///
/// This query will ICE if given a const that is not marked with `#[type_const]`.
query const_of_item(def_id: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> {
desc { |tcx| "computing the type-level value for `{}`", tcx.def_path_str(def_id) }
cache_on_disk_if { def_id.is_local() }
separate_provide_extern
}
/// Returns the *type* of the definition given by `DefId`.
///
/// For type aliases (whether eager or lazy) and associated types, this returns
@ -2417,7 +2430,7 @@ rustc_queries! {
/// Do not call this query directly: Invoke `normalize` instead.
///
/// </div>
query normalize_canonicalized_projection_ty(
query normalize_canonicalized_projection(
goal: CanonicalAliasGoal<'tcx>
) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
@ -2445,7 +2458,7 @@ rustc_queries! {
/// Do not call this query directly: Invoke `normalize` instead.
///
/// </div>
query normalize_canonicalized_inherent_projection_ty(
query normalize_canonicalized_inherent_projection(
goal: CanonicalAliasGoal<'tcx>
) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,

View file

@ -66,7 +66,7 @@ pub mod type_op {
}
pub type CanonicalAliasGoal<'tcx> =
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTerm<'tcx>>>;
pub type CanonicalMethodAutoderefStepsGoal<'tcx> =
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, MethodAutoderefSteps<'tcx>>>;
@ -192,11 +192,11 @@ pub struct MethodAutoderefBadTy<'tcx> {
pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
}
/// Result of the `normalize_canonicalized_{{,inherent_}projection,free}_ty` queries.
/// Result of the `normalize_canonicalized_{{,inherent_}projection,free}` queries.
#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct NormalizationResult<'tcx> {
/// Result of the normalization.
pub normalized_ty: Ty<'tcx>,
pub normalized_term: ty::Term<'tcx>,
}
/// Outlives bounds are relationships between generic parameters,

View file

@ -1,9 +1,7 @@
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_hir::find_attr;
use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_span::{ErrorGuaranteed, Ident, Symbol};
@ -173,24 +171,6 @@ impl AssocItem {
pub fn is_impl_trait_in_trait(&self) -> bool {
matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) })
}
/// Returns true if:
/// - This trait associated item has the `#[type_const]` attribute,
/// - If it is in a trait impl, the item from the original trait has this attribute, or
/// - It is an inherent assoc const.
pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
if !matches!(self.kind, ty::AssocKind::Const { .. }) {
return false;
}
let def_id = match self.container {
AssocContainer::Trait => self.def_id,
AssocContainer::TraitImpl(Ok(trait_item_did)) => trait_item_did,
AssocContainer::TraitImpl(Err(_)) => return false,
AssocContainer::InherentImpl => return true,
};
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_))
}
}
#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]

View file

@ -242,6 +242,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
self.type_of_opaque_hir_typeck(def_id)
}
fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
self.const_of_item(def_id)
}
type AdtDef = ty::AdtDef<'tcx>;
fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef {

View file

@ -30,14 +30,12 @@ where
);
let actual = if free_alias.kind(cx).is_type() {
cx.type_of(free_alias.def_id).instantiate(cx, free_alias.args)
cx.type_of(free_alias.def_id).instantiate(cx, free_alias.args).into()
} else {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead return that.
panic!("normalizing free const aliases in the type system is unsupported");
cx.const_of_item(free_alias.def_id).instantiate(cx, free_alias.args).into()
};
self.instantiate_normalizes_to_term(goal, actual.into());
self.instantiate_normalizes_to_term(goal, actual);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}

View file

@ -54,8 +54,7 @@ where
let normalized = if inherent.kind(cx).is_type() {
cx.type_of(inherent.def_id).instantiate(cx, inherent_args).into()
} else {
// FIXME(mgca): Properly handle IACs in the type system
panic!("normalizing inherent associated consts in the type system is unsupported");
cx.const_of_item(inherent.def_id).instantiate(cx, inherent_args).into()
};
self.instantiate_normalizes_to_term(goal, normalized);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)

View file

@ -366,19 +366,7 @@ where
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
}
ty::AliasTermKind::ProjectionConst => {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead return that.
if cx.features().associated_const_equality() {
panic!("associated const projection is not supported yet")
} else {
ty::EarlyBinder::bind(
Const::new_error_with_message(
cx,
"associated const projection is not supported yet",
)
.into(),
)
}
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
}
kind => panic!("expected projection, found {kind:?}"),
};

View file

@ -6,7 +6,9 @@ use rustc_ast::ast::*;
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast::{self as ast};
use rustc_ast::{
attr, {self as ast},
};
use rustc_ast_pretty::pprust;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err};
@ -255,13 +257,13 @@ impl<'a> Parser<'a> {
} else {
self.recover_const_mut(const_span);
self.recover_missing_kw_before_item()?;
let (ident, generics, ty, expr) = self.parse_const_item()?;
let (ident, generics, ty, rhs) = self.parse_const_item(attrs)?;
ItemKind::Const(Box::new(ConstItem {
defaultness: def_(),
ident,
generics,
ty,
expr,
rhs,
define_opaque: None,
}))
}
@ -1010,12 +1012,13 @@ impl<'a> Parser<'a> {
define_opaque,
}) => {
self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span });
let rhs = expr.map(ConstItemRhs::Body);
AssocItemKind::Const(Box::new(ConstItem {
defaultness: Defaultness::Final,
ident,
generics: Generics::default(),
ty,
expr,
rhs,
define_opaque,
}))
}
@ -1249,7 +1252,7 @@ impl<'a> Parser<'a> {
let kind = match ForeignItemKind::try_from(kind) {
Ok(kind) => kind,
Err(kind) => match kind {
ItemKind::Const(box ConstItem { ident, ty, expr, .. }) => {
ItemKind::Const(box ConstItem { ident, ty, rhs, .. }) => {
let const_span = Some(span.with_hi(ident.span.lo()))
.filter(|span| span.can_be_used_for_suggestions());
self.dcx().emit_err(errors::ExternItemCannotBeConst {
@ -1260,7 +1263,10 @@ impl<'a> Parser<'a> {
ident,
ty,
mutability: Mutability::Not,
expr,
expr: rhs.map(|b| match b {
ConstItemRhs::TypeConst(anon_const) => anon_const.value,
ConstItemRhs::Body(expr) => expr,
}),
safety: Safety::Default,
define_opaque: None,
}))
@ -1431,7 +1437,8 @@ impl<'a> Parser<'a> {
/// ```
fn parse_const_item(
&mut self,
) -> PResult<'a, (Ident, Generics, Box<Ty>, Option<Box<ast::Expr>>)> {
attrs: &[Attribute],
) -> PResult<'a, (Ident, Generics, Box<Ty>, Option<ast::ConstItemRhs>)> {
let ident = self.parse_ident_or_underscore()?;
let mut generics = self.parse_generics()?;
@ -1458,7 +1465,15 @@ impl<'a> Parser<'a> {
let before_where_clause =
if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
let expr = if self.eat(exp!(Eq)) { Some(self.parse_expr()?) } else { None };
let rhs = if self.eat(exp!(Eq)) {
if attr::contains_name(attrs, sym::type_const) {
Some(ConstItemRhs::TypeConst(self.parse_expr_anon_const()?))
} else {
Some(ConstItemRhs::Body(self.parse_expr()?))
}
} else {
None
};
let after_where_clause = self.parse_where_clause()?;
@ -1466,18 +1481,18 @@ impl<'a> Parser<'a> {
// Users may be tempted to write such code if they are still used to the deprecated
// where-clause location on type aliases and associated types. See also #89122.
if before_where_clause.has_where_token
&& let Some(expr) = &expr
&& let Some(rhs) = &rhs
{
self.dcx().emit_err(errors::WhereClauseBeforeConstBody {
span: before_where_clause.span,
name: ident.span,
body: expr.span,
body: rhs.span(),
sugg: if !after_where_clause.has_where_token {
self.psess.source_map().span_to_snippet(expr.span).ok().map(|body| {
self.psess.source_map().span_to_snippet(rhs.span()).ok().map(|body_s| {
errors::WhereClauseBeforeConstBodySugg {
left: before_where_clause.span.shrink_to_lo(),
snippet: body,
right: before_where_clause.span.shrink_to_hi().to(expr.span),
snippet: body_s,
right: before_where_clause.span.shrink_to_hi().to(rhs.span()),
}
})
} else {
@ -1515,7 +1530,7 @@ impl<'a> Parser<'a> {
self.expect_semi()?;
Ok((ident, generics, ty, expr))
Ok((ident, generics, ty, rhs))
}
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.

View file

@ -2115,19 +2115,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
fn check_type_const(&self, hir_id: HirId, attr_span: Span, target: Target) {
let tcx = self.tcx;
if target == Target::AssocConst
&& let parent = tcx.parent(hir_id.expect_owner().to_def_id())
&& self.tcx.def_kind(parent) == DefKind::Trait
{
fn check_type_const(&self, _hir_id: HirId, attr_span: Span, target: Target) {
if matches!(target, Target::AssocConst | Target::Const) {
return;
} else {
self.dcx()
.struct_span_err(
attr_span,
"`#[type_const]` must only be applied to trait associated constants",
)
.struct_span_err(attr_span, "`#[type_const]` must only be applied to const items")
.emit();
}
}

View file

@ -217,7 +217,7 @@ impl<'tcx> ReachableContext<'tcx> {
// We can't figure out which value the constant will evaluate to. In
// lieu of that, we have to consider everything mentioned in the const
// initializer reachable, since it *may* end up in the final value.
Err(ErrorHandled::TooGeneric(_)) => self.visit_nested_body(init),
Err(ErrorHandled::TooGeneric(_)) => self.visit_const_item_rhs(init),
// If there was an error evaluating the const, nothing can be reachable
// via it, and anyway compilation will fail.
Err(ErrorHandled::Reported(..)) => {}
@ -253,16 +253,16 @@ impl<'tcx> ReachableContext<'tcx> {
| hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => {
// Keep going, nothing to get exported
}
hir::TraitItemKind::Const(_, Some(body_id))
| hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => {
hir::TraitItemKind::Const(_, Some(rhs)) => self.visit_const_item_rhs(rhs),
hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => {
self.visit_nested_body(body_id);
}
hir::TraitItemKind::Type(..) => {}
}
}
Node::ImplItem(impl_item) => match impl_item.kind {
hir::ImplItemKind::Const(_, body) => {
self.visit_nested_body(body);
hir::ImplItemKind::Const(_, rhs) => {
self.visit_const_item_rhs(rhs);
}
hir::ImplItemKind::Fn(_, body) => {
if recursively_reachable(self.tcx, impl_item.hir_id().owner.to_def_id()) {

View file

@ -2827,7 +2827,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
if let Some(expr) = expr {
// We already forbid generic params because of the above item rib,
// so it doesn't matter whether this is a trivial constant.
this.resolve_const_body(expr, Some((ident, ConstantItemKind::Static)));
this.resolve_static_body(expr, Some((ident, ConstantItemKind::Static)));
}
});
self.resolve_define_opaques(define_opaque);
@ -2837,7 +2837,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ident,
ref generics,
ref ty,
ref expr,
ref rhs,
ref define_opaque,
..
}) => {
@ -2862,8 +2862,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|this| this.visit_ty(ty),
);
if let Some(expr) = expr {
this.resolve_const_body(expr, Some((ident, ConstantItemKind::Const)));
if let Some(rhs) = rhs {
this.resolve_const_item_rhs(
rhs,
Some((ident, ConstantItemKind::Const)),
);
}
},
);
@ -3181,7 +3184,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
AssocItemKind::Const(box ast::ConstItem {
generics,
ty,
expr,
rhs,
define_opaque,
..
}) => {
@ -3203,13 +3206,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Only impose the restrictions of `ConstRibKind` for an
// actual constant expression in a provided default.
if let Some(expr) = expr {
if let Some(rhs) = rhs {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
this.resolve_const_body(expr, None);
this.resolve_const_item_rhs(rhs, None);
}
},
)
@ -3388,7 +3391,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ident,
generics,
ty,
expr,
rhs,
define_opaque,
..
}) => {
@ -3430,13 +3433,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
this.visit_generics(generics);
this.visit_ty(ty);
if let Some(expr) = expr {
if let Some(rhs) = rhs {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
this.resolve_const_body(expr, None);
this.resolve_const_item_rhs(rhs, None);
}
},
)
@ -3647,7 +3650,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
);
}
fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) {
fn resolve_static_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) {
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| {
this.visit_expr(expr)
@ -3655,6 +3658,23 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
})
}
fn resolve_const_item_rhs(
&mut self,
rhs: &'ast ConstItemRhs,
item: Option<(Ident, ConstantItemKind)>,
) {
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| match rhs {
ConstItemRhs::TypeConst(anon_const) => {
this.resolve_anon_const(anon_const, AnonConstKind::ConstArg(IsRepeatExpr::No));
}
ConstItemRhs::Body(expr) => {
this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| {
this.visit_expr(expr)
});
}
})
}
fn resolve_delegation(&mut self, delegation: &'ast Delegation) {
self.smart_resolve_path(
delegation.id,

View file

@ -333,10 +333,11 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
let res = if free.kind(infcx.tcx).is_type() {
infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into()
} else {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead use that rather than evaluating.
super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env)
.super_fold_with(self)
infcx
.tcx
.const_of_item(free.def_id)
.instantiate(infcx.tcx, free.args)
.fold_with(self)
.into()
};
self.depth -= 1;
@ -436,51 +437,47 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
return ct;
}
// Doing "proper" normalization of const aliases is inherently cyclic until const items
// are real aliases instead of having bodies. We gate proper const alias handling behind
// mgca to avoid breaking stable code, though this should become the "main" codepath long
// before mgca is stabilized.
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
_ => return ct.super_fold_with(self),
};
// Note that the AssocConst and Const cases are unreachable on stable,
// unless a `min_generic_const_args` feature gate error has already
// been emitted earlier in compilation.
//
// FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items
// are represented.
if tcx.features().min_generic_const_args() {
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
_ => return ct.super_fold_with(self),
};
let ct = match tcx.def_kind(uv.def) {
DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) {
DefKind::Trait => self.normalize_trait_projection(uv.into()),
DefKind::Impl { of_trait: false } => {
self.normalize_inherent_projection(uv.into())
}
kind => unreachable!(
"unexpected `DefKind` for const alias' resolution's parent def: {:?}",
kind
),
},
DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()),
kind => {
unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind)
// That's because we can only end up with an Unevaluated ty::Const for a const item
// if it was marked with `#[type_const]`. Using this attribute without the mgca
// feature gate causes a parse error.
let ct = match tcx.def_kind(uv.def) {
DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) {
DefKind::Trait => self.normalize_trait_projection(uv.into()).expect_const(),
DefKind::Impl { of_trait: false } => {
self.normalize_inherent_projection(uv.into()).expect_const()
}
};
kind => unreachable!(
"unexpected `DefKind` for const alias' resolution's parent def: {:?}",
kind
),
},
DefKind::Const => self.normalize_free_alias(uv.into()).expect_const(),
DefKind::AnonConst => {
let ct = ct.super_fold_with(self);
super::with_replaced_escaping_bound_vars(
self.selcx.infcx,
&mut self.universes,
ct,
|ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env),
)
}
kind => {
unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind)
}
};
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
ct.expect_const().super_fold_with(self)
} else {
let ct = ct.super_fold_with(self);
return super::with_replaced_escaping_bound_vars(
self.selcx.infcx,
&mut self.universes,
ct,
|ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env),
)
.super_fold_with(self);
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
}
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
ct.super_fold_with(self)
}
#[inline]

View file

@ -546,7 +546,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
let term: Term<'tcx> = if alias_term.kind(tcx).is_type() {
tcx.type_of(alias_term.def_id).instantiate(tcx, args).into()
} else {
get_associated_const_value(selcx, alias_term.to_term(tcx).expect_const(), param_env).into()
tcx.const_of_item(alias_term.def_id).instantiate(tcx, args).into()
};
let mut term = selcx.infcx.resolve_vars_if_possible(term);
@ -2034,14 +2034,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let term = if obligation.predicate.kind(tcx).is_type() {
tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into())
} else {
ty::EarlyBinder::bind(
get_associated_const_value(
selcx,
obligation.predicate.to_term(tcx).expect_const(),
param_env,
)
.into(),
)
tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into())
};
let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) {
@ -2133,15 +2126,3 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
})
}
}
fn get_associated_const_value<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
alias_ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::Const<'tcx> {
// FIXME(mgca): We shouldn't be invoking ctfe here, instead const items should be aliases to type
// system consts that we can retrieve with some `query const_arg_of_alias` query. Evaluating the
// constant is "close enough" to getting the actual rhs of the const item for now even if it might
// lead to some cycles
super::evaluate_const(selcx.infcx, alias_ct, param_env)
}

View file

@ -1,9 +1,10 @@
//! Code for the 'normalization' query. This consists of a wrapper
//! which folds deeply, invoking the underlying
//! `normalize_canonicalized_projection_ty` query when it encounters projections.
//! `normalize_canonicalized_projection` query when it encounters projections.
use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def::DefKind;
use rustc_infer::traits::PredicateObligations;
use rustc_macros::extension;
pub use rustc_middle::traits::query::NormalizationResult;
@ -255,76 +256,9 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
}
}
ty::Projection | ty::Inherent | ty::Free => {
// See note in `rustc_trait_selection::traits::project`
let infcx = self.infcx;
let tcx = infcx.tcx;
// Just an optimization: When we don't have escaping bound vars,
// we don't need to replace them with placeholders.
let (data, maps) = if data.has_escaping_bound_vars() {
let (data, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
(data, Some((mapped_regions, mapped_types, mapped_consts)))
} else {
(data, None)
};
let data = data.try_fold_with(self)?;
let mut orig_values = OriginalQueryValues::default();
let c_data = infcx.canonicalize_query(self.param_env.and(data), &mut orig_values);
debug!("QueryNormalizer: c_data = {:#?}", c_data);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let result = match kind {
ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
ty::Free => tcx.normalize_canonicalized_free_alias(c_data),
ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
kind => unreachable!("did not expect {kind:?} due to match arm above"),
}?;
// We don't expect ambiguity.
if !result.value.is_proven() {
// Rustdoc normalizes possibly not well-formed types, so only
// treat this as a bug if we're not in rustdoc.
if !tcx.sess.opts.actually_rustdoc {
tcx.dcx()
.delayed_bug(format!("unexpected ambiguity: {c_data:?} {result:?}"));
}
return Err(NoSolution);
}
let InferOk { value: result, obligations } = infcx
.instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
result,
)?;
debug!("QueryNormalizer: result = {:#?}", result);
debug!("QueryNormalizer: obligations = {:#?}", obligations);
self.obligations.extend(obligations);
let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps {
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
result.normalized_ty,
)
} else {
result.normalized_ty
};
// `tcx.normalize_canonicalized_projection_ty` may normalize to a type that
// still has unevaluated consts, so keep normalizing here if that's the case.
// Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer
// of type and we need to continue folding it to reveal the TAIT behind it.
if res != ty
&& (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Free)
{
res.try_fold_with(self)?
} else {
res
}
}
ty::Projection | ty::Inherent | ty::Free => self
.try_fold_free_or_assoc(ty::AliasTerm::new(self.cx(), data.def_id, data.args))?
.expect_type(),
};
self.cache.insert(ty, res);
@ -339,12 +273,22 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
return Ok(constant);
}
let constant = crate::traits::with_replaced_escaping_bound_vars(
self.infcx,
&mut self.universes,
constant,
|constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env),
);
let uv = match constant.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
_ => return constant.try_super_fold_with(self),
};
let constant = match self.cx().def_kind(uv.def) {
DefKind::AnonConst => crate::traits::with_replaced_escaping_bound_vars(
self.infcx,
&mut self.universes,
constant,
|constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env),
),
_ => self
.try_fold_free_or_assoc(ty::AliasTerm::new(self.cx(), uv.def, uv.args))?
.expect_const(),
};
debug!(?constant, ?self.param_env);
constant.try_super_fold_with(self)
}
@ -361,3 +305,85 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
}
}
}
impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> {
fn try_fold_free_or_assoc(
&mut self,
term: ty::AliasTerm<'tcx>,
) -> Result<ty::Term<'tcx>, NoSolution> {
let infcx = self.infcx;
let tcx = infcx.tcx;
// Just an optimization: When we don't have escaping bound vars,
// we don't need to replace them with placeholders.
let (term, maps) = if term.has_escaping_bound_vars() {
let (term, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, term);
(term, Some((mapped_regions, mapped_types, mapped_consts)))
} else {
(term, None)
};
let term = term.try_fold_with(self)?;
let mut orig_values = OriginalQueryValues::default();
let c_term = infcx.canonicalize_query(self.param_env.and(term), &mut orig_values);
debug!("QueryNormalizer: c_term = {:#?}", c_term);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let result = match term.kind(tcx) {
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
tcx.normalize_canonicalized_projection(c_term)
}
ty::AliasTermKind::FreeTy | ty::AliasTermKind::FreeConst => {
tcx.normalize_canonicalized_free_alias(c_term)
}
ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => {
tcx.normalize_canonicalized_inherent_projection(c_term)
}
kind @ (ty::AliasTermKind::OpaqueTy | ty::AliasTermKind::UnevaluatedConst) => {
unreachable!("did not expect {kind:?} due to match arm above")
}
}?;
// We don't expect ambiguity.
if !result.value.is_proven() {
// Rustdoc normalizes possibly not well-formed types, so only
// treat this as a bug if we're not in rustdoc.
if !tcx.sess.opts.actually_rustdoc {
tcx.dcx().delayed_bug(format!("unexpected ambiguity: {c_term:?} {result:?}"));
}
return Err(NoSolution);
}
let InferOk { value: result, obligations } = infcx
.instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
result,
)?;
debug!("QueryNormalizer: result = {:#?}", result);
debug!("QueryNormalizer: obligations = {:#?}", obligations);
self.obligations.extend(obligations);
let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps {
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
result.normalized_term,
)
} else {
result.normalized_term
};
// `tcx.normalize_canonicalized_projection` may normalize to a type that
// still has unevaluated consts, so keep normalizing here if that's the case.
// Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer
// of type and we need to continue folding it to reveal the TAIT behind it.
if res != term.to_term(tcx)
&& (res.as_type().map_or(false, |t| t.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION))
|| term.kind(tcx) == ty::AliasTermKind::FreeTy)
{
res.try_fold_with(self)
} else {
Ok(res)
}
}
}

View file

@ -12,18 +12,18 @@ use tracing::debug;
pub(crate) fn provide(p: &mut Providers) {
*p = Providers {
normalize_canonicalized_projection_ty,
normalize_canonicalized_projection,
normalize_canonicalized_free_alias,
normalize_canonicalized_inherent_projection_ty,
normalize_canonicalized_inherent_projection,
..*p
};
}
fn normalize_canonicalized_projection_ty<'tcx>(
fn normalize_canonicalized_projection<'tcx>(
tcx: TyCtxt<'tcx>,
goal: CanonicalAliasGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
debug!("normalize_canonicalized_projection_ty(goal={:#?})", goal);
debug!("normalize_canonicalized_projection(goal={:#?})", goal);
tcx.infer_ctxt().enter_canonical_trait_query(
&goal,
@ -32,7 +32,7 @@ fn normalize_canonicalized_projection_ty<'tcx>(
let selcx = &mut SelectionContext::new(ocx.infcx);
let cause = ObligationCause::dummy();
let mut obligations = PredicateObligations::new();
let answer = traits::normalize_projection_term(
let normalized_term = traits::normalize_projection_term(
selcx,
param_env,
goal.into(),
@ -61,10 +61,7 @@ fn normalize_canonicalized_projection_ty<'tcx>(
return Err(NoSolution);
}
// FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty
// expected a type, but there is the possibility it could've been a const now.
// Maybe change it to a Term later?
Ok(NormalizationResult { normalized_ty: answer.expect_type() })
Ok(NormalizationResult { normalized_term })
},
)
}
@ -89,17 +86,21 @@ fn normalize_canonicalized_free_alias<'tcx>(
},
);
ocx.register_obligations(obligations);
let normalized_ty = tcx.type_of(goal.def_id).instantiate(tcx, goal.args);
Ok(NormalizationResult { normalized_ty })
let normalized_term = if goal.kind(tcx).is_type() {
tcx.type_of(goal.def_id).instantiate(tcx, goal.args).into()
} else {
tcx.const_of_item(goal.def_id).instantiate(tcx, goal.args).into()
};
Ok(NormalizationResult { normalized_term })
},
)
}
fn normalize_canonicalized_inherent_projection_ty<'tcx>(
fn normalize_canonicalized_inherent_projection<'tcx>(
tcx: TyCtxt<'tcx>,
goal: CanonicalAliasGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
debug!("normalize_canonicalized_inherent_projection_ty(goal={:#?})", goal);
debug!("normalize_canonicalized_inherent_projection(goal={:#?})", goal);
tcx.infer_ctxt().enter_canonical_trait_query(
&goal,
@ -107,7 +108,7 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>(
let selcx = &mut SelectionContext::new(ocx.infcx);
let cause = ObligationCause::dummy();
let mut obligations = PredicateObligations::new();
let answer = traits::normalize_inherent_projection(
let normalized_term = traits::normalize_inherent_projection(
selcx,
param_env,
goal.into(),
@ -117,7 +118,7 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>(
);
ocx.register_obligations(obligations);
Ok(NormalizationResult { normalized_ty: answer.expect_type() })
Ok(NormalizationResult { normalized_term })
},
)
}

View file

@ -205,6 +205,7 @@ pub trait Interner:
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId)
-> ty::EarlyBinder<Self, Self::Ty>;
fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Const>;
type AdtDef: AdtDef<Self>;
fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef;

View file

@ -306,16 +306,23 @@ pub(crate) fn clean_precise_capturing_arg(
}
}
pub(crate) fn clean_const<'tcx>(
constant: &hir::ConstArg<'tcx>,
_cx: &mut DocContext<'tcx>,
pub(crate) fn clean_const_item_rhs<'tcx>(
ct_rhs: hir::ConstItemRhs<'tcx>,
parent: DefId,
) -> ConstantKind {
match ct_rhs {
hir::ConstItemRhs::Body(body) => ConstantKind::Local { def_id: parent, body },
hir::ConstItemRhs::TypeConst(ct) => clean_const(ct),
}
}
pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg<'tcx>) -> ConstantKind {
match &constant.kind {
hir::ConstArgKind::Path(qpath) => {
ConstantKind::Path { path: qpath_to_string(qpath).into() }
}
hir::ConstArgKind::Anon(anon) => ConstantKind::Anonymous { body: anon.body },
hir::ConstArgKind::Infer(..) => ConstantKind::Infer,
hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => ConstantKind::Infer,
}
}
@ -1194,7 +1201,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
hir::TraitItemKind::Const(ty, Some(default)) => {
ProvidedAssocConstItem(Box::new(Constant {
generics: enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)),
kind: ConstantKind::Local { def_id: local_did, body: default },
kind: clean_const_item_rhs(default, local_did),
type_: clean_ty(ty, cx),
}))
}
@ -1244,7 +1251,7 @@ pub(crate) fn clean_impl_item<'tcx>(
let inner = match impl_.kind {
hir::ImplItemKind::Const(ty, expr) => ImplAssocConstItem(Box::new(Constant {
generics: clean_generics(impl_.generics, cx),
kind: ConstantKind::Local { def_id: local_did, body: expr },
kind: clean_const_item_rhs(expr, local_did),
type_: clean_ty(ty, cx),
})),
hir::ImplItemKind::Fn(ref sig, body) => {
@ -1799,7 +1806,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
// results in an ICE while manually constructing the constant and using `eval`
// does nothing for `ConstKind::Param`.
let length = match const_arg.kind {
hir::ConstArgKind::Infer(..) => "_".to_string(),
hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => "_".to_string(),
hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) => {
let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No);
let typing_env = ty::TypingEnv::post_analysis(cx.tcx, *def_id);
@ -2516,7 +2523,7 @@ fn clean_generic_args<'tcx>(
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)),
hir::GenericArg::Const(ct) => {
GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx)))
GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct())))
}
hir::GenericArg::Infer(_inf) => GenericArg::Infer,
})
@ -2785,10 +2792,10 @@ fn clean_maybe_renamed_item<'tcx>(
mutability,
expr: Some(body_id),
}),
ItemKind::Const(_, generics, ty, body_id) => ConstantItem(Box::new(Constant {
ItemKind::Const(_, generics, ty, rhs) => ConstantItem(Box::new(Constant {
generics: clean_generics(generics, cx),
type_: clean_ty(ty, cx),
kind: ConstantKind::Local { body: body_id, def_id },
kind: clean_const_item_rhs(rhs, def_id),
})),
ItemKind::TyAlias(_, generics, ty) => {
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;

View file

@ -18,7 +18,7 @@
// definitely contains interior mutability.
use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::consts::{ConstEvalCtxt, Constant, const_item_rhs_to_expr};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::is_in_const_context;
use clippy_utils::macros::macro_backtrace;
@ -28,7 +28,8 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{
Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp,
ConstArgKind, ConstItemRhs, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr,
TraitItem, TraitItemKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::mir::{ConstValue, UnevaluatedConst};
@ -272,6 +273,7 @@ impl<'tcx> NonCopyConst<'tcx> {
/// Checks if a value of the given type is `Freeze`, or may be depending on the value.
fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze {
// FIXME: this should probably be using the trait solver
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
match self.freeze_tys.entry(ty) {
Entry::Occupied(e) => *e.get(),
@ -695,7 +697,7 @@ impl<'tcx> NonCopyConst<'tcx> {
impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Const(ident, .., body_id) = item.kind
if let ItemKind::Const(ident, .., ct_rhs) = item.kind
&& !ident.is_special()
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
@ -703,13 +705,14 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
IsFreeze::Yes => false,
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
_ => !self.is_init_expr_freeze(
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| !self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
cx.tcx.hir_body(body_id).value,
),
e
)),
},
}
&& !item.span.in_external_macro(cx.sess().source_map())
@ -736,22 +739,25 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Const(_, body_id_opt) = item.kind
if let TraitItemKind::Const(_, ct_rhs_opt) = item.kind
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
IsFreeze::No => true,
IsFreeze::Maybe if let Some(body_id) = body_id_opt => {
IsFreeze::Maybe if let Some(ct_rhs) = ct_rhs_opt => {
match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => {
!is_freeze
},
_ => !self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
cx.tcx.hir_body(body_id).value,
),
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
!self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
e,
)
}),
}
},
IsFreeze::Yes | IsFreeze::Maybe => false,
@ -768,7 +774,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if let ImplItemKind::Const(_, body_id) = item.kind
if let ImplItemKind::Const(_, ct_rhs) = item.kind
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
IsFreeze::Yes => false,
@ -799,13 +805,16 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
// interior mutability.
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
_ => !self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
cx.tcx.hir_body(body_id).value,
),
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
!self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
e,
)
}),
},
}
&& !item.span.in_external_macro(cx.sess().source_map())
@ -913,20 +922,26 @@ fn get_const_hir_value<'tcx>(
args: GenericArgsRef<'tcx>,
) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> {
let did = did.as_local()?;
let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id),
Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
let (did, ct_rhs) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
Node::Item(item) if let ItemKind::Const(.., ct_rhs) = item.kind => (did, ct_rhs),
Node::ImplItem(item) if let ImplItemKind::Const(.., ct_rhs) = item.kind => (did, ct_rhs),
Node::TraitItem(_)
if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args)
&& let Some(did) = inst.def_id().as_local() =>
{
match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id),
Node::ImplItem(item) if let ImplItemKind::Const(.., ct_rhs) = item.kind => (did, ct_rhs),
Node::TraitItem(item) if let TraitItemKind::Const(.., Some(ct_rhs)) = item.kind => (did, ct_rhs),
_ => return None,
}
},
_ => return None,
};
Some((tcx.typeck(did), tcx.hir_body(body_id).value))
match ct_rhs {
ConstItemRhs::Body(body_id) => Some((tcx.typeck(did), tcx.hir_body(body_id).value)),
ConstItemRhs::TypeConst(ct_arg) => match ct_arg.kind {
ConstArgKind::Anon(anon_const) => Some((tcx.typeck(did), tcx.hir_body(anon_const.body).value)),
_ => None,
},
}
}

View file

@ -2,6 +2,7 @@ use std::ops::ControlFlow;
use std::sync::Arc;
use clippy_config::Conf;
use clippy_utils::consts::const_item_rhs_to_expr;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_lint_allowed;
use clippy_utils::source::walk_span_to_context;
@ -184,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
}
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &hir::Item<'tcx>) {
if item.span.in_external_macro(cx.tcx.sess.source_map()) {
return;
}
@ -214,7 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
}
}
fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span), is_doc: bool) {
fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, (span, help_span): (Span, Span), is_doc: bool) {
match &item.kind {
ItemKind::Impl(Impl {
of_trait: Some(of_trait),
@ -234,7 +235,29 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h
},
ItemKind::Impl(_) => {},
// const and static items only need a safety comment if their body is an unsafe block, lint otherwise
&ItemKind::Const(.., body) | &ItemKind::Static(.., body) => {
&ItemKind::Const(.., ct_rhs) => {
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) {
let expr = const_item_rhs_to_expr(cx.tcx, ct_rhs);
if let Some(expr) = expr && !matches!(
expr.kind, hir::ExprKind::Block(block, _)
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
) {
span_lint_and_then(
cx,
UNNECESSARY_SAFETY_COMMENT,
span,
format!(
"{} has unnecessary safety comment",
cx.tcx.def_descr(item.owner_id.to_def_id()),
),
|diag| {
diag.span_help(help_span, "consider removing the safety comment");
},
);
}
}
}
&ItemKind::Static(.., body) => {
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
let body = cx.tcx.hir_body(body);
if !matches!(

View file

@ -320,6 +320,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.body(field!(anon_const.body));
},
ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"),
ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"),
}
}

View file

@ -356,7 +356,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
ident: li,
generics: lg,
ty: lt,
expr: le,
rhs: lb,
define_opaque: _,
}),
Const(box ConstItem {
@ -364,7 +364,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
ident: ri,
generics: rg,
ty: rt,
expr: re,
rhs: rb,
define_opaque: _,
}),
) => {
@ -372,7 +372,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& eq_expr_opt(le.as_deref(), re.as_deref())
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
},
(
Fn(box ast::Fn {
@ -610,7 +610,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
ident: li,
generics: lg,
ty: lt,
expr: le,
rhs: lb,
define_opaque: _,
}),
Const(box ConstItem {
@ -618,7 +618,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
ident: ri,
generics: rg,
ty: rt,
expr: re,
rhs: rb,
define_opaque: _,
}),
) => {
@ -626,7 +626,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& eq_expr_opt(le.as_deref(), re.as_deref())
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
},
(
Fn(box ast::Fn {
@ -784,6 +784,15 @@ pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
eq_expr(&l.value, &r.value)
}
pub fn eq_const_item_rhs(l: &ConstItemRhs, r: &ConstItemRhs) -> bool {
use ConstItemRhs::*;
match (l, r) {
(TypeConst(l), TypeConst(r)) => eq_anon_const(l, r),
(Body(l), Body(r)) => eq_expr(l, r),
(TypeConst(..), Body(..)) | (Body(..), TypeConst(..)) => false,
}
}
pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
use UseTreeKind::*;
match (l, r) {

View file

@ -13,7 +13,10 @@ use rustc_apfloat::Float;
use rustc_apfloat::ieee::{Half, Quad};
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp};
use rustc_hir::{
BinOpKind, Block, ConstArgKind, ConstBlock, ConstItemRhs, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath,
TyKind, UnOp,
};
use rustc_lexer::{FrontmatterAllowed, tokenize};
use rustc_lint::LateContext;
use rustc_middle::mir::ConstValue;
@ -1130,3 +1133,14 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext)
pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool {
integer_const(cx, expr, ctxt) == Some(0)
}
pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx>) -> Option<&'tcx Expr<'tcx>> {
match ct_rhs {
ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value),
ConstItemRhs::TypeConst(const_arg) => match const_arg.kind {
ConstArgKind::Path(_) => None,
ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value),
ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None,
},
}
}

View file

@ -481,7 +481,9 @@ impl HirEqInterExpr<'_, '_, '_> {
(ConstArgKind::Path(..), ConstArgKind::Anon(..))
| (ConstArgKind::Anon(..), ConstArgKind::Path(..))
| (ConstArgKind::Infer(..), _)
| (_, ConstArgKind::Infer(..)) => false,
| (_, ConstArgKind::Infer(..))
| (ConstArgKind::Error(..), _)
| (_, ConstArgKind::Error(..)) => false,
}
}
@ -1330,7 +1332,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
match &const_arg.kind {
ConstArgKind::Path(path) => self.hash_qpath(path),
ConstArgKind::Anon(anon) => self.hash_body(anon.body),
ConstArgKind::Infer(..) => {},
ConstArgKind::Infer(..) | ConstArgKind::Error(..) => {},
}
}

View file

@ -192,12 +192,12 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersi
let msrv_attr = msrv_attrs.next()?;
if let Some(duplicate) = msrv_attrs.next_back() {
sess.dcx()
.struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
.with_span_note(msrv_attr.span(), "first definition found here")
.emit();
}
if let Some(duplicate) = msrv_attrs.next_back() {
sess.dcx()
.struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
.with_span_note(msrv_attr.span(), "first definition found here")
.emit();
}
let Some(msrv) = msrv_attr.value_str() else {
sess.dcx().span_err(msrv_attr.span(), "bad clippy attribute");
@ -205,8 +205,8 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersi
};
let Some(version) = parse_version(msrv) else {
sess.dcx()
.span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version"));
sess.dcx()
.span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version"));
return None;
};

View file

@ -9,7 +9,7 @@
/// unimplemented!();
/// }
/// ```
///
///
/// With an explicit return type it should lint too
/// ```edition2015
/// fn main() -> () {
@ -17,7 +17,7 @@
/// unimplemented!();
/// }
/// ```
///
///
/// This should, too.
/// ```rust
/// fn main() {
@ -25,7 +25,7 @@
/// unimplemented!();
/// }
/// ```
///
///
/// This one too.
/// ```no_run
/// // the fn is not always the first line

View file

@ -1,6 +1,6 @@
#![deny(clippy::trait_duplication_in_bounds)]
#![allow(unused)]
#![feature(associated_const_equality, const_trait_impl)]
#![feature(const_trait_impl)]
use std::any::Any;
@ -194,12 +194,3 @@ where
T: Iterator<Item: Clone> + Iterator<Item: Clone>,
{
}
trait AssocConstTrait {
const ASSOC: usize;
}
fn assoc_const_args<T>()
where
T: AssocConstTrait<ASSOC = 0>,
//~^ trait_duplication_in_bounds
{
}

View file

@ -1,6 +1,6 @@
#![deny(clippy::trait_duplication_in_bounds)]
#![allow(unused)]
#![feature(associated_const_equality, const_trait_impl)]
#![feature(const_trait_impl)]
use std::any::Any;
@ -194,12 +194,3 @@ where
T: Iterator<Item: Clone> + Iterator<Item: Clone>,
{
}
trait AssocConstTrait {
const ASSOC: usize;
}
fn assoc_const_args<T>()
where
T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
//~^ trait_duplication_in_bounds
{
}

View file

@ -70,11 +70,5 @@ error: these where clauses contain repeated elements
LL | T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `IntoIterator<Item = U::Owned>`
error: these where clauses contain repeated elements
--> tests/ui/trait_duplication_in_bounds.rs:202:8
|
LL | T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
error: aborting due to 12 previous errors
error: aborting due to 11 previous errors

View file

@ -0,0 +1,14 @@
#![deny(clippy::trait_duplication_in_bounds)]
#![expect(incomplete_features)]
#![feature(associated_const_equality, min_generic_const_args)]
trait AssocConstTrait {
#[type_const]
const ASSOC: usize;
}
fn assoc_const_args<T>()
where
T: AssocConstTrait<ASSOC = 0>,
//~^ trait_duplication_in_bounds
{
}

View file

@ -0,0 +1,14 @@
#![deny(clippy::trait_duplication_in_bounds)]
#![expect(incomplete_features)]
#![feature(associated_const_equality, min_generic_const_args)]
trait AssocConstTrait {
#[type_const]
const ASSOC: usize;
}
fn assoc_const_args<T>()
where
T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
//~^ trait_duplication_in_bounds
{
}

View file

@ -0,0 +1,14 @@
error: these where clauses contain repeated elements
--> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:8
|
LL | T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
|
note: the lint level is defined here
--> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:1:9
|
LL | #![deny(clippy::trait_duplication_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -2004,37 +2004,37 @@ pub(crate) struct StaticParts<'a> {
generics: Option<&'a ast::Generics>,
ty: &'a ast::Ty,
mutability: ast::Mutability,
expr_opt: Option<&'a Box<ast::Expr>>,
expr_opt: Option<&'a ast::Expr>,
defaultness: Option<ast::Defaultness>,
span: Span,
}
impl<'a> StaticParts<'a> {
pub(crate) fn from_item(item: &'a ast::Item) -> Self {
let (defaultness, prefix, safety, ident, ty, mutability, expr, generics) = match &item.kind
{
ast::ItemKind::Static(s) => (
None,
"static",
s.safety,
s.ident,
&s.ty,
s.mutability,
&s.expr,
None,
),
ast::ItemKind::Const(c) => (
Some(c.defaultness),
"const",
ast::Safety::Default,
c.ident,
&c.ty,
ast::Mutability::Not,
&c.expr,
Some(&c.generics),
),
_ => unreachable!(),
};
let (defaultness, prefix, safety, ident, ty, mutability, expr_opt, generics) =
match &item.kind {
ast::ItemKind::Static(s) => (
None,
"static",
s.safety,
s.ident,
&s.ty,
s.mutability,
s.expr.as_deref(),
None,
),
ast::ItemKind::Const(c) => (
Some(c.defaultness),
"const",
ast::Safety::Default,
c.ident,
&c.ty,
ast::Mutability::Not,
c.rhs.as_ref().map(|rhs| rhs.expr()),
Some(&c.generics),
),
_ => unreachable!(),
};
StaticParts {
prefix,
safety,
@ -2043,7 +2043,7 @@ impl<'a> StaticParts<'a> {
generics,
ty,
mutability,
expr_opt: expr.as_ref(),
expr_opt,
defaultness,
span: item.span,
}
@ -2051,7 +2051,12 @@ impl<'a> StaticParts<'a> {
pub(crate) fn from_trait_item(ti: &'a ast::AssocItem, ident: Ident) -> Self {
let (defaultness, ty, expr_opt, generics) = match &ti.kind {
ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)),
ast::AssocItemKind::Const(c) => (
c.defaultness,
&c.ty,
c.rhs.as_ref().map(|rhs| rhs.expr()),
Some(&c.generics),
),
_ => unreachable!(),
};
StaticParts {
@ -2062,15 +2067,20 @@ impl<'a> StaticParts<'a> {
generics,
ty,
mutability: ast::Mutability::Not,
expr_opt: expr_opt.as_ref(),
expr_opt,
defaultness: Some(defaultness),
span: ti.span,
}
}
pub(crate) fn from_impl_item(ii: &'a ast::AssocItem, ident: Ident) -> Self {
let (defaultness, ty, expr, generics) = match &ii.kind {
ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)),
let (defaultness, ty, expr_opt, generics) = match &ii.kind {
ast::AssocItemKind::Const(c) => (
c.defaultness,
&c.ty,
c.rhs.as_ref().map(|rhs| rhs.expr()),
Some(&c.generics),
),
_ => unreachable!(),
};
StaticParts {
@ -2081,7 +2091,7 @@ impl<'a> StaticParts<'a> {
generics,
ty,
mutability: ast::Mutability::Not,
expr_opt: expr.as_ref(),
expr_opt,
defaultness: Some(defaultness),
span: ii.span,
}
@ -2144,7 +2154,7 @@ fn rewrite_static(
rewrite_assign_rhs_with_comments(
context,
&lhs,
&**expr,
expr,
Shape::legacy(remaining_width, offset.block_only()),
&RhsAssignKind::Expr(&expr.kind, expr.span),
RhsTactics::Default,

View file

@ -1,7 +1,10 @@
//@ known-bug: #119783
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
trait Trait { const F: fn(); }
trait Trait {
#[type_const]
const F: fn();
}
fn take(_: impl Trait<F = { || {} }>) {}

View file

@ -1,15 +0,0 @@
//@ known-bug: #131046
trait Owner {
const C<const N: u32>: u32;
}
impl Owner for () {
const C<const N: u32>: u32 = N;
}
fn take0<const N: u64>(_: impl Owner<C<N> = { N }>) {}
fn main() {
take0::<128>(());
}

View file

@ -1,13 +0,0 @@
//@ known-bug: #134641
#![feature(associated_const_equality)]
pub trait IsVoid {
const IS_VOID: bool;
}
impl IsVoid for () {
const IS_VOID: bool = true;
}
pub trait Maybe {}
impl Maybe for () {}
impl Maybe for () where (): IsVoid<IS_VOID = true> {}

View file

@ -5,6 +5,7 @@
struct OnDiskDirEntry<'a> {}
impl<'a> OnDiskDirEntry<'a> {
#[type_const]
const LFN_FRAGMENT_LEN: i64 = 2;
fn lfn_contents() -> [char; Self::LFN_FRAGMENT_LEN] {

View file

@ -0,0 +1,16 @@
//@ known-bug: #132980
// Originally a rustdoc test. Should be moved back there with @has checks
// readded once fixed.
// Previous issue (before mgca): https://github.com/rust-lang/rust/issues/105952
#![crate_name = "foo"]
#![feature(associated_const_equality, min_generic_const_args)]
pub enum ParseMode {
Raw,
}
pub trait Parse {
#[type_const]
const PARSE_MODE: ParseMode;
}
pub trait RenderRaw {}
impl<T: Parse<PARSE_MODE = { ParseMode::Raw }>> RenderRaw for T {}

View file

@ -0,0 +1,23 @@
//@ known-bug: #132980
// Move this test to tests/ui/const-generics/mgca/type_const-only-in-trait.rs
// once fixed.
#![expect(incomplete_features)]
#![feature(associated_const_equality, min_generic_const_args)]
trait GoodTr {
#[type_const]
const NUM: usize;
}
struct BadS;
impl GoodTr for BadS {
const NUM: usize = 42;
}
fn accept_good_tr<const N: usize, T: GoodTr<NUM = { N }>>(_x: &T) {}
fn main() {
accept_good_tr(&BadS);
}

View file

@ -1,7 +1,8 @@
// Regression test for <https://github.com/rust-lang/rust/issues/102467>.
// It ensures that the expected error is displayed.
#![feature(associated_const_equality)]
#![expect(incomplete_features)]
#![feature(associated_const_equality, min_generic_const_args)]
trait T {
type A: S<C<X = 0i32> = 34>;
@ -10,6 +11,7 @@ trait T {
}
trait S {
#[type_const]
const C: i32;
}

View file

@ -1,5 +1,5 @@
error[E0229]: associated item constraints are not allowed here
--> $DIR/associated-constant-not-allowed-102467.rs:7:17
--> $DIR/associated-constant-not-allowed-102467.rs:8:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here
@ -11,7 +11,7 @@ LL + type A: S<C = 34>;
|
error[E0229]: associated item constraints are not allowed here
--> $DIR/associated-constant-not-allowed-102467.rs:7:17
--> $DIR/associated-constant-not-allowed-102467.rs:8:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here

View file

@ -1,11 +0,0 @@
#![feature(associated_const_equality)]
trait T {
type A: S<C<X = 0i32> = 34>;
//~^ ERROR associated item constraints are not allowed here
//~| ERROR associated item constraints are not allowed here
}
trait S {
const C: i32;
}

View file

@ -1,28 +0,0 @@
error[E0229]: associated item constraints are not allowed here
--> $DIR/invalid_associated_const.rs:4:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here
|
help: consider removing this associated item binding
|
LL - type A: S<C<X = 0i32> = 34>;
LL + type A: S<C = 34>;
|
error[E0229]: associated item constraints are not allowed here
--> $DIR/invalid_associated_const.rs:4:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider removing this associated item binding
|
LL - type A: S<C<X = 0i32> = 34>;
LL + type A: S<C = 34>;
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0229`.

View file

@ -1,15 +0,0 @@
// https://github.com/rust-lang/rust/issues/105952
#![crate_name = "foo"]
#![feature(associated_const_equality)]
pub enum ParseMode {
Raw,
}
pub trait Parse {
const PARSE_MODE: ParseMode;
}
pub trait RenderRaw {}
//@ hasraw foo/trait.RenderRaw.html 'impl'
//@ hasraw foo/trait.RenderRaw.html 'ParseMode::Raw'
impl<T: Parse<PARSE_MODE = { ParseMode::Raw }>> RenderRaw for T {}

View file

@ -1,7 +1,9 @@
#![feature(associated_const_equality)]
#![expect(incomplete_features)]
#![feature(associated_const_equality, min_generic_const_args)]
pub fn accept(_: impl Trait<K = 0>) {}
pub trait Trait {
#[type_const]
const K: i32;
}

View file

@ -1,17 +1,27 @@
// We used to say "ambiguous associated type" on ambiguous associated consts.
// Ensure that we now use the correct label.
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait0: Parent0<i32> + Parent0<u32> {}
trait Parent0<T> { const K: (); }
trait Parent0<T> {
#[type_const]
const K: ();
}
fn take0(_: impl Trait0<K = { () }>) {}
//~^ ERROR ambiguous associated constant `K` in bounds of `Trait0`
trait Trait1: Parent1 + Parent2 {}
trait Parent1 { const C: i32; }
trait Parent2 { const C: &'static str; }
trait Parent1 {
#[type_const]
const C: i32;
}
trait Parent2 {
#[type_const]
const C: &'static str;
}
fn take1(_: impl Trait1<C = "?">) {}
//~^ ERROR ambiguous associated constant `C` in bounds of `Trait1`

View file

@ -1,12 +1,12 @@
error[E0222]: ambiguous associated constant `K` in bounds of `Trait0`
--> $DIR/assoc-const-eq-ambiguity.rs:9:25
--> $DIR/assoc-const-eq-ambiguity.rs:13:25
|
LL | trait Parent0<T> { const K: (); }
| -----------
| |
| ambiguous `K` from `Parent0<u32>`
| ambiguous `K` from `Parent0<i32>`
LL |
LL | const K: ();
| -----------
| |
| ambiguous `K` from `Parent0<u32>`
| ambiguous `K` from `Parent0<i32>`
...
LL | fn take0(_: impl Trait0<K = { () }>) {}
| ^^^^^^^^^^ ambiguous associated constant `K`
|
@ -17,13 +17,14 @@ LL | fn take0(_: impl Trait0<K = { () }>) {}
T: Parent0<i32>::K = { () }
error[E0222]: ambiguous associated constant `C` in bounds of `Trait1`
--> $DIR/assoc-const-eq-ambiguity.rs:16:25
--> $DIR/assoc-const-eq-ambiguity.rs:26:25
|
LL | trait Parent1 { const C: i32; }
| ------------ ambiguous `C` from `Parent1`
LL | trait Parent2 { const C: &'static str; }
| --------------------- ambiguous `C` from `Parent2`
LL |
LL | const C: i32;
| ------------ ambiguous `C` from `Parent1`
...
LL | const C: &'static str;
| --------------------- ambiguous `C` from `Parent2`
...
LL | fn take1(_: impl Trait1<C = "?">) {}
| ^^^^^^^ ambiguous associated constant `C`

View file

@ -1,8 +1,10 @@
// Check that we eventually catch types of assoc const bounds
// (containing late-bound vars) that are ill-formed.
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait<T> {
#[type_const]
const K: T;
}

View file

@ -1,11 +1,11 @@
error: higher-ranked subtype error
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:14:13
|
LL | K = { () }
| ^^^^^^
error: higher-ranked subtype error
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:14:13
|
LL | K = { () }
| ^^^^^^
@ -13,7 +13,7 @@ LL | K = { () }
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: implementation of `Project` is not general enough
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:10:13
--> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
|
LL | _: impl Trait<
| _____________^

View file

@ -3,9 +3,11 @@
//
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait<T> {
#[type_const]
const K: T;
}

View file

@ -4,9 +4,13 @@
//
// issue: <https://github.com/rust-lang/rust/issues/108220>
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
pub trait TraitA<T> { const K: u8 = 0; }
pub trait TraitA<T> {
#[type_const]
const K: u8 = 0;
}
pub trait TraitB<T> {}
impl<T> TraitA<T> for () {}

View file

@ -1,8 +1,10 @@
// Detect and reject escaping late-bound generic params in
// the type of assoc consts used in an equality bound.
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait<'a> {
#[type_const]
const K: &'a ();
}

View file

@ -1,5 +1,5 @@
error: the type of the associated constant `K` cannot capture late-bound generic parameters
--> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:9:35
--> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:11:35
|
LL | fn take(_: impl for<'r> Trait<'r, K = { &() }>) {}
| -- ^ its type cannot capture the late-bound lifetime parameter `'r`

View file

@ -1,8 +1,10 @@
// Regression test for issue #108271.
// Detect and reject generic params in the type of assoc consts used in an equality bound.
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait<'a, T: 'a, const N: usize> {
#[type_const]
const K: &'a [T; N];
}
@ -21,6 +23,7 @@ fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
//~| NOTE `K` has type `&'r [A; Q]`
trait Project {
#[type_const]
const SELF: Self;
}

View file

@ -1,5 +1,5 @@
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:9:61
--> $DIR/assoc-const-eq-param-in-ty.rs:11:61
|
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
| -- the lifetime parameter `'r` is defined here ^ its type must not depend on the lifetime parameter `'r`
@ -7,7 +7,7 @@ LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }
= note: `K` has type `&'r [A; Q]`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:9:61
--> $DIR/assoc-const-eq-param-in-ty.rs:11:61
|
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
| - the type parameter `A` is defined here ^ its type must not depend on the type parameter `A`
@ -15,7 +15,7 @@ LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }
= note: `K` has type `&'r [A; Q]`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:9:61
--> $DIR/assoc-const-eq-param-in-ty.rs:11:61
|
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
| - ^ its type must not depend on the const parameter `Q`
@ -25,7 +25,7 @@ LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }
= note: `K` has type `&'r [A; Q]`
error: the type of the associated constant `SELF` must not depend on `impl Trait`
--> $DIR/assoc-const-eq-param-in-ty.rs:27:26
--> $DIR/assoc-const-eq-param-in-ty.rs:30:26
|
LL | fn take1(_: impl Project<SELF = {}>) {}
| -------------^^^^------
@ -34,7 +34,7 @@ LL | fn take1(_: impl Project<SELF = {}>) {}
| the `impl Trait` is specified here
error: the type of the associated constant `SELF` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:32:21
--> $DIR/assoc-const-eq-param-in-ty.rs:35:21
|
LL | fn take2<P: Project<SELF = {}>>(_: P) {}
| - ^^^^ its type must not depend on the type parameter `P`
@ -44,7 +44,7 @@ LL | fn take2<P: Project<SELF = {}>>(_: P) {}
= note: `SELF` has type `P`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | trait Iface<'r> {
| -- the lifetime parameter `'r` is defined here
@ -55,7 +55,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
= note: `K` has type `&'r [Self; Q]`
error: the type of the associated constant `K` must not depend on `Self`
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
| ^ its type must not depend on `Self`
@ -63,7 +63,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
= note: `K` has type `&'r [Self; Q]`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
| - ^ its type must not depend on the const parameter `Q`
@ -73,7 +73,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
= note: `K` has type `&'r [Self; Q]`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | trait Iface<'r> {
| -- the lifetime parameter `'r` is defined here
@ -85,7 +85,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: the type of the associated constant `K` must not depend on `Self`
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
| ^ its type must not depend on `Self`
@ -94,7 +94,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: the type of the associated constant `K` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:41:52
--> $DIR/assoc-const-eq-param-in-ty.rs:44:52
|
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
| - ^ its type must not depend on the const parameter `Q`

View file

@ -3,11 +3,13 @@
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait: SuperTrait {}
trait SuperTrait: SuperSuperTrait<i32> {}
trait SuperSuperTrait<T> {
#[type_const]
const K: T;
}

View file

@ -5,16 +5,19 @@
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait Trait: SuperTrait {
type N;
type Q;
#[type_const]
const N: usize;
}
trait SuperTrait {
#[type_const]
const Q: &'static str;
}

View file

@ -1,14 +1,16 @@
//@ run-pass
#![feature(associated_const_equality)]
#![allow(unused)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(unused, incomplete_features)]
pub trait Foo {
#[type_const]
const N: usize;
}
pub struct Bar;
impl Foo for Bar {
#[type_const]
const N: usize = 3;
}

View file

@ -1,25 +1,32 @@
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
#![deny(dead_code)]
trait Tr {
#[type_const]
const I: i32;
}
impl Tr for () {
#[type_const]
const I: i32 = 1;
}
fn foo() -> impl Tr<I = 1> {}
trait Tr2 {
#[type_const]
const J: i32;
#[type_const]
const K: i32;
}
impl Tr2 for () {
#[type_const]
const J: i32 = 1;
#[type_const]
const K: i32 = 1;
}
@ -27,10 +34,12 @@ fn foo2() -> impl Tr2<J = 1, K = 1> {}
mod t {
pub trait Tr3 {
#[type_const]
const L: i32;
}
impl Tr3 for () {
#[type_const]
const L: i32 = 1;
}
}

View file

@ -1,4 +1,5 @@
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
trait T {
type A: S<C<X = 0i32> = 34>;
@ -7,6 +8,7 @@ trait T {
}
trait S {
#[type_const]
const C: i32;
}

View file

@ -1,5 +1,5 @@
error[E0229]: associated item constraints are not allowed here
--> $DIR/issue-102335-const.rs:4:17
--> $DIR/issue-102335-const.rs:5:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here
@ -11,7 +11,7 @@ LL + type A: S<C = 34>;
|
error[E0229]: associated item constraints are not allowed here
--> $DIR/issue-102335-const.rs:4:17
--> $DIR/issue-102335-const.rs:5:17
|
LL | type A: S<C<X = 0i32> = 34>;
| ^^^^^^^^ associated item constraint not allowed here

View file

@ -1,8 +1,10 @@
//@ check-pass
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
pub trait Trait {
#[type_const]
const ASSOC: usize;
}

View file

@ -1,8 +1,10 @@
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
// Issue 110549
pub trait TraitWAssocConst {
#[type_const]
const A: usize;
}

View file

@ -1,5 +1,5 @@
error[E0271]: type mismatch resolving `<T as TraitWAssocConst>::A == 32`
--> $DIR/projection-unspecified-but-bounded.rs:12:11
--> $DIR/projection-unspecified-but-bounded.rs:14:11
|
LL | foo::<T>();
| ^ expected `32`, found `<T as TraitWAssocConst>::A`
@ -7,7 +7,7 @@ LL | foo::<T>();
= note: expected constant `32`
found constant `<T as TraitWAssocConst>::A`
note: required by a bound in `foo`
--> $DIR/projection-unspecified-but-bounded.rs:9:28
--> $DIR/projection-unspecified-but-bounded.rs:11:28
|
LL | fn foo<T: TraitWAssocConst<A = 32>>() {}
| ^^^^^^ required by this bound in `foo`

View file

@ -1,24 +1,15 @@
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/const-projection-err.rs:4:26
|
LL | #![cfg_attr(gce, feature(generic_const_exprs))]
| ^^^^^^^^^^^^^^^^^^^
|
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0271]: type mismatch resolving `<T as TraitWAssocConst>::A == 1`
--> $DIR/const-projection-err.rs:14:11
--> $DIR/const-projection-err.rs:16:11
|
LL | foo::<T>();
| ^ expected `0`, found `1`
|
note: required by a bound in `foo`
--> $DIR/const-projection-err.rs:11:28
--> $DIR/const-projection-err.rs:13:28
|
LL | fn foo<T: TraitWAssocConst<A = 1>>() {}
| ^^^^^ required by this bound in `foo`
error: aborting due to 1 previous error; 1 warning emitted
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View file

@ -1,10 +1,12 @@
//@ revisions: stock gce
#![feature(associated_const_equality)]
#![feature(associated_const_equality, min_generic_const_args)]
#![allow(incomplete_features)]
#![cfg_attr(gce, feature(generic_const_exprs))]
//[gce]~^ WARN the feature `generic_const_exprs` is incomplete
trait TraitWAssocConst {
#[type_const]
const A: usize;
}

View file

@ -1,13 +1,13 @@
error[E0271]: type mismatch resolving `<T as TraitWAssocConst>::A == 1`
--> $DIR/const-projection-err.rs:14:11
--> $DIR/const-projection-err.rs:16:11
|
LL | foo::<T>();
| ^ expected `1`, found `<T as TraitWAssocConst>::A`
| ^ expected `1`, found `0`
|
= note: expected constant `1`
found constant `<T as TraitWAssocConst>::A`
found constant `0`
note: required by a bound in `foo`
--> $DIR/const-projection-err.rs:11:28
--> $DIR/const-projection-err.rs:13:28
|
LL | fn foo<T: TraitWAssocConst<A = 1>>() {}
| ^^^^^ required by this bound in `foo`

View file

@ -1,6 +1,12 @@
//@ edition: 2024
#![feature(associated_const_equality, type_alias_impl_trait, return_type_notation)]
#![feature(
associated_const_equality,
min_generic_const_args,
type_alias_impl_trait,
return_type_notation
)]
#![expect(incomplete_features)]
#![allow(refining_impl_trait_internal)]
use std::iter;
@ -45,6 +51,7 @@ fn mismatch_2() -> impl Iterator<Item: Copy, Item: Send> {
trait Trait {
type Gat<T>;
#[type_const]
const ASSOC: i32;
fn foo() -> impl Sized;
@ -53,6 +60,7 @@ trait Trait {
impl Trait for () {
type Gat<T> = ();
#[type_const]
const ASSOC: i32 = 3;
fn foo() {}
@ -61,6 +69,7 @@ impl Trait for () {
impl Trait for u32 {
type Gat<T> = ();
#[type_const]
const ASSOC: i32 = 4;
fn foo() -> u32 {
@ -79,6 +88,7 @@ type MustFail = dyn Iterator<Item = i32, Item = u32>;
//~| ERROR conflicting associated type bounds
trait Trait2 {
#[type_const]
const ASSOC: u32;
}

View file

@ -1,5 +1,5 @@
error[E0282]: type annotations needed
--> $DIR/duplicate-bound-err.rs:9:5
--> $DIR/duplicate-bound-err.rs:15:5
|
LL | iter::empty()
| ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@ -10,7 +10,7 @@ LL | iter::empty::<T>()
| +++++
error[E0282]: type annotations needed
--> $DIR/duplicate-bound-err.rs:13:5
--> $DIR/duplicate-bound-err.rs:19:5
|
LL | iter::empty()
| ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@ -21,7 +21,7 @@ LL | iter::empty::<T>()
| +++++
error[E0282]: type annotations needed
--> $DIR/duplicate-bound-err.rs:17:5
--> $DIR/duplicate-bound-err.rs:23:5
|
LL | iter::empty()
| ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@ -32,7 +32,7 @@ LL | iter::empty::<T>()
| +++++
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:21:51
--> $DIR/duplicate-bound-err.rs:27:51
|
LL | type Tait1<T: Iterator<Item: Copy, Item: Send>> = impl Copy;
| ^^^^^^^^^
@ -40,7 +40,7 @@ LL | type Tait1<T: Iterator<Item: Copy, Item: Send>> = impl Copy;
= note: `Tait1` must be used in combination with a concrete type within the same crate
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:23:51
--> $DIR/duplicate-bound-err.rs:29:51
|
LL | type Tait2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy;
| ^^^^^^^^^
@ -48,7 +48,7 @@ LL | type Tait2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy;
= note: `Tait2` must be used in combination with a concrete type within the same crate
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:25:57
--> $DIR/duplicate-bound-err.rs:31:57
|
LL | type Tait3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy;
| ^^^^^^^^^
@ -56,7 +56,7 @@ LL | type Tait3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy;
= note: `Tait3` must be used in combination with a concrete type within the same crate
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:28:14
--> $DIR/duplicate-bound-err.rs:34:14
|
LL | type Tait4 = impl Iterator<Item: Copy, Item: Send>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -64,7 +64,7 @@ LL | type Tait4 = impl Iterator<Item: Copy, Item: Send>;
= note: `Tait4` must be used in combination with a concrete type within the same crate
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:30:14
--> $DIR/duplicate-bound-err.rs:36:14
|
LL | type Tait5 = impl Iterator<Item: Copy, Item: Copy>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -72,7 +72,7 @@ LL | type Tait5 = impl Iterator<Item: Copy, Item: Copy>;
= note: `Tait5` must be used in combination with a concrete type within the same crate
error: unconstrained opaque type
--> $DIR/duplicate-bound-err.rs:32:14
--> $DIR/duplicate-bound-err.rs:38:14
|
LL | type Tait6 = impl Iterator<Item: 'static, Item: 'static>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -80,7 +80,7 @@ LL | type Tait6 = impl Iterator<Item: 'static, Item: 'static>;
= note: `Tait6` must be used in combination with a concrete type within the same crate
error[E0277]: `*const ()` cannot be sent between threads safely
--> $DIR/duplicate-bound-err.rs:35:18
--> $DIR/duplicate-bound-err.rs:41:18
|
LL | fn mismatch() -> impl Iterator<Item: Copy, Item: Send> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const ()` cannot be sent between threads safely
@ -91,7 +91,7 @@ LL | iter::empty::<*const ()>()
= help: the trait `Send` is not implemented for `*const ()`
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/duplicate-bound-err.rs:40:20
--> $DIR/duplicate-bound-err.rs:46:20
|
LL | fn mismatch_2() -> impl Iterator<Item: Copy, Item: Send> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
@ -100,7 +100,7 @@ LL | iter::empty::<String>()
| ----------------------- return type was inferred to be `std::iter::Empty<String>` here
error[E0271]: expected `IntoIter<u32, 1>` to be an iterator that yields `i32`, but it yields `u32`
--> $DIR/duplicate-bound-err.rs:100:17
--> $DIR/duplicate-bound-err.rs:110:17
|
LL | fn foo() -> impl Iterator<Item = i32, Item = u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
@ -109,7 +109,7 @@ LL | [2u32].into_iter()
| ------------------ return type was inferred to be `std::array::IntoIter<u32, 1>` here
error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
--> $DIR/duplicate-bound-err.rs:77:42
--> $DIR/duplicate-bound-err.rs:86:42
|
LL | type MustFail = dyn Iterator<Item = i32, Item = u32>;
| ---------- ^^^^^^^^^^ re-bound here
@ -117,7 +117,7 @@ LL | type MustFail = dyn Iterator<Item = i32, Item = u32>;
| `Item` bound here first
error: conflicting associated type bounds for `Item`
--> $DIR/duplicate-bound-err.rs:77:17
--> $DIR/duplicate-bound-err.rs:86:17
|
LL | type MustFail = dyn Iterator<Item = i32, Item = u32>;
| ^^^^^^^^^^^^^----------^^----------^
@ -126,7 +126,7 @@ LL | type MustFail = dyn Iterator<Item = i32, Item = u32>;
| `Item` is specified to be `i32` here
error[E0719]: the value of the associated type `ASSOC` in trait `Trait2` is already specified
--> $DIR/duplicate-bound-err.rs:85:43
--> $DIR/duplicate-bound-err.rs:95:43
|
LL | type MustFail2 = dyn Trait2<ASSOC = 3u32, ASSOC = 4u32>;
| ------------ ^^^^^^^^^^^^ re-bound here
@ -134,7 +134,7 @@ LL | type MustFail2 = dyn Trait2<ASSOC = 3u32, ASSOC = 4u32>;
| `ASSOC` bound here first
error: conflicting associated type bounds for `ASSOC`
--> $DIR/duplicate-bound-err.rs:85:18
--> $DIR/duplicate-bound-err.rs:95:18
|
LL | type MustFail2 = dyn Trait2<ASSOC = 3u32, ASSOC = 4u32>;
| ^^^^^^^^^^^------------^^------------^
@ -143,7 +143,7 @@ LL | type MustFail2 = dyn Trait2<ASSOC = 3u32, ASSOC = 4u32>;
| `ASSOC` is specified to be `3` here
error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
--> $DIR/duplicate-bound-err.rs:89:43
--> $DIR/duplicate-bound-err.rs:99:43
|
LL | type MustFail3 = dyn Iterator<Item = i32, Item = i32>;
| ---------- ^^^^^^^^^^ re-bound here
@ -151,7 +151,7 @@ LL | type MustFail3 = dyn Iterator<Item = i32, Item = i32>;
| `Item` bound here first
error[E0719]: the value of the associated type `ASSOC` in trait `Trait2` is already specified
--> $DIR/duplicate-bound-err.rs:92:43
--> $DIR/duplicate-bound-err.rs:102:43
|
LL | type MustFail4 = dyn Trait2<ASSOC = 3u32, ASSOC = 3u32>;
| ------------ ^^^^^^^^^^^^ re-bound here
@ -159,19 +159,19 @@ LL | type MustFail4 = dyn Trait2<ASSOC = 3u32, ASSOC = 3u32>;
| `ASSOC` bound here first
error[E0271]: expected `impl Iterator<Item = u32>` to be an iterator that yields `i32`, but it yields `u32`
--> $DIR/duplicate-bound-err.rs:100:17
--> $DIR/duplicate-bound-err.rs:110:17
|
LL | fn foo() -> impl Iterator<Item = i32, Item = u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
|
note: required by a bound in `Trait3::foo::{anon_assoc#0}`
--> $DIR/duplicate-bound-err.rs:96:31
--> $DIR/duplicate-bound-err.rs:106:31
|
LL | fn foo() -> impl Iterator<Item = i32, Item = u32>;
| ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}`
error[E0271]: expected `Empty<u32>` to be an iterator that yields `i32`, but it yields `u32`
--> $DIR/duplicate-bound-err.rs:108:16
--> $DIR/duplicate-bound-err.rs:118:16
|
LL | uncallable(iter::empty::<u32>());
| ---------- ^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
@ -179,13 +179,13 @@ LL | uncallable(iter::empty::<u32>());
| required by a bound introduced by this call
|
note: required by a bound in `uncallable`
--> $DIR/duplicate-bound-err.rs:71:32
--> $DIR/duplicate-bound-err.rs:80:32
|
LL | fn uncallable(_: impl Iterator<Item = i32, Item = u32>) {}
| ^^^^^^^^^^ required by this bound in `uncallable`
error[E0271]: expected `Empty<i32>` to be an iterator that yields `u32`, but it yields `i32`
--> $DIR/duplicate-bound-err.rs:109:16
--> $DIR/duplicate-bound-err.rs:119:16
|
LL | uncallable(iter::empty::<i32>());
| ---------- ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `i32`
@ -193,13 +193,13 @@ LL | uncallable(iter::empty::<i32>());
| required by a bound introduced by this call
|
note: required by a bound in `uncallable`
--> $DIR/duplicate-bound-err.rs:71:44
--> $DIR/duplicate-bound-err.rs:80:44
|
LL | fn uncallable(_: impl Iterator<Item = i32, Item = u32>) {}
| ^^^^^^^^^^ required by this bound in `uncallable`
error[E0271]: type mismatch resolving `<() as Trait>::ASSOC == 4`
--> $DIR/duplicate-bound-err.rs:110:22
--> $DIR/duplicate-bound-err.rs:120:22
|
LL | uncallable_const(());
| ---------------- ^^ expected `4`, found `3`
@ -209,13 +209,13 @@ LL | uncallable_const(());
= note: expected constant `4`
found constant `3`
note: required by a bound in `uncallable_const`
--> $DIR/duplicate-bound-err.rs:73:46
--> $DIR/duplicate-bound-err.rs:82:46
|
LL | fn uncallable_const(_: impl Trait<ASSOC = 3, ASSOC = 4>) {}
| ^^^^^^^^^ required by this bound in `uncallable_const`
error[E0271]: type mismatch resolving `<u32 as Trait>::ASSOC == 3`
--> $DIR/duplicate-bound-err.rs:111:22
--> $DIR/duplicate-bound-err.rs:121:22
|
LL | uncallable_const(4u32);
| ---------------- ^^^^ expected `3`, found `4`
@ -225,13 +225,13 @@ LL | uncallable_const(4u32);
= note: expected constant `3`
found constant `4`
note: required by a bound in `uncallable_const`
--> $DIR/duplicate-bound-err.rs:73:35
--> $DIR/duplicate-bound-err.rs:82:35
|
LL | fn uncallable_const(_: impl Trait<ASSOC = 3, ASSOC = 4>) {}
| ^^^^^^^^^ required by this bound in `uncallable_const`
error[E0271]: type mismatch resolving `<() as Trait>::ASSOC == 4`
--> $DIR/duplicate-bound-err.rs:112:20
--> $DIR/duplicate-bound-err.rs:122:20
|
LL | uncallable_rtn(());
| -------------- ^^ expected `4`, found `3`
@ -241,13 +241,13 @@ LL | uncallable_rtn(());
= note: expected constant `4`
found constant `3`
note: required by a bound in `uncallable_rtn`
--> $DIR/duplicate-bound-err.rs:75:75
--> $DIR/duplicate-bound-err.rs:84:75
|
LL | fn uncallable_rtn(_: impl Trait<foo(..): Trait<ASSOC = 3>, foo(..): Trait<ASSOC = 4>>) {}
| ^^^^^^^^^ required by this bound in `uncallable_rtn`
error[E0271]: type mismatch resolving `<u32 as Trait>::ASSOC == 3`
--> $DIR/duplicate-bound-err.rs:113:20
--> $DIR/duplicate-bound-err.rs:123:20
|
LL | uncallable_rtn(17u32);
| -------------- ^^^^^ expected `3`, found `4`
@ -257,7 +257,7 @@ LL | uncallable_rtn(17u32);
= note: expected constant `3`
found constant `4`
note: required by a bound in `uncallable_rtn`
--> $DIR/duplicate-bound-err.rs:75:48
--> $DIR/duplicate-bound-err.rs:84:48
|
LL | fn uncallable_rtn(_: impl Trait<foo(..): Trait<ASSOC = 3>, foo(..): Trait<ASSOC = 4>>) {}
| ^^^^^^^^^ required by this bound in `uncallable_rtn`

View file

@ -1,7 +1,8 @@
//@ edition: 2024
//@ run-pass
#![feature(associated_const_equality, return_type_notation)]
#![feature(associated_const_equality, min_generic_const_args, return_type_notation)]
#![expect(incomplete_features)]
#![allow(dead_code, refining_impl_trait_internal, type_alias_bounds)]
use std::iter;
@ -188,6 +189,7 @@ trait Tra3 {
trait Trait {
type Gat<T>;
#[type_const]
const ASSOC: i32;
fn foo() -> impl Sized;
@ -196,6 +198,7 @@ trait Trait {
impl Trait for () {
type Gat<T> = ();
#[type_const]
const ASSOC: i32 = 3;
fn foo() {}

View file

@ -0,0 +1,18 @@
#![feature(associated_const_equality, min_generic_const_args)]
#![expect(incomplete_features)]
pub trait IsVoid {
#[type_const]
const IS_VOID: bool;
}
impl IsVoid for () {
#[type_const]
const IS_VOID: bool = true;
}
pub trait Maybe {}
impl Maybe for () {}
impl Maybe for () where (): IsVoid<IS_VOID = true> {}
//~^ ERROR conflicting implementations of trait `Maybe` for type `()`
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0119]: conflicting implementations of trait `Maybe` for type `()`
--> $DIR/coherence.rs:15:1
|
LL | impl Maybe for () {}
| ----------------- first implementation here
LL | impl Maybe for () where (): IsVoid<IS_VOID = true> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0119`.

View file

@ -1,14 +1,18 @@
#![feature(associated_const_equality, generic_const_items)]
//@ check-pass
#![feature(associated_const_equality, min_generic_const_args, generic_const_items)]
#![expect(incomplete_features)]
// Regression test for #133066 where we would try to evaluate `<() as Foo>::ASSOC<_>` even
// though it contained inference variables, which would cause ICEs.
trait Foo {
#[type_const]
const ASSOC<const N: u32>: u32;
}
impl Foo for () {
#[type_const]
const ASSOC<const N: u32>: u32 = N;
}
@ -16,9 +20,4 @@ fn bar<const N: u32, T: Foo<ASSOC<N> = 10>>() {}
fn main() {
bar::<_, ()>();
//~^ ERROR: type mismatch resolving `<() as Foo>::ASSOC<_> == 10`
// FIXME(mgca):
// FIXME(associated_const_equality):
// This ought to start compiling once const items are aliases rather than bodies
}

View file

@ -1,17 +0,0 @@
error[E0271]: type mismatch resolving `<() as Foo>::ASSOC<_> == 10`
--> $DIR/equality_bound_with_infer.rs:18:14
|
LL | bar::<_, ()>();
| ^^ expected `10`, found `<() as Foo>::ASSOC::<_>`
|
= note: expected constant `10`
found constant `<() as Foo>::ASSOC::<_>`
note: required by a bound in `bar`
--> $DIR/equality_bound_with_infer.rs:15:29
|
LL | fn bar<const N: u32, T: Foo<ASSOC<N> = 10>>() {}
| ^^^^^^^^^^^^^ required by this bound in `bar`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

Some files were not shown because too many files have changed in this diff Show more