Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-05-28 05:00:57 +00:00
commit 15f0fb03bb
330 changed files with 3717 additions and 2941 deletions

View file

@ -79,9 +79,8 @@ jobs:
# This also ensures that PR CI (which doesn't get write access to S3) works, as it cannot
# access the environment.
#
# We only enable the environment for the rust-lang/rust repository, so that rust-lang-ci/rust
# CI works until we migrate off it (since that repository doesn't contain the environment).
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/auto')) && 'bors') || '' }}
# We only enable the environment for the rust-lang/rust repository, so that CI works on forks.
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/auto')) && 'bors') || '' }}
env:
CI_JOB_NAME: ${{ matrix.name }}
CI_JOB_DOC_URL: ${{ matrix.doc_url }}
@ -234,8 +233,8 @@ jobs:
fi
exit ${STATUS}
env:
AWS_ACCESS_KEY_ID: ${{ (github.repository == 'rust-lang/rust' && secrets.CACHES_AWS_ACCESS_KEY_ID) || env.CACHES_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ (github.repository == 'rust-lang/rust' && secrets.CACHES_AWS_SECRET_ACCESS_KEY) || secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}
AWS_ACCESS_KEY_ID: ${{ secrets.CACHES_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CACHES_AWS_SECRET_ACCESS_KEY }}
- name: create github artifacts
run: src/ci/scripts/create-doc-artifacts.sh
@ -257,8 +256,8 @@ jobs:
- name: upload artifacts to S3
run: src/ci/scripts/upload-artifacts.sh
env:
AWS_ACCESS_KEY_ID: ${{ (github.repository == 'rust-lang/rust' && secrets.ARTIFACTS_AWS_ACCESS_KEY_ID) || env.ARTIFACTS_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ (github.repository == 'rust-lang/rust' && secrets.ARTIFACTS_AWS_SECRET_ACCESS_KEY) || secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}
AWS_ACCESS_KEY_ID: ${{ secrets.ARTIFACTS_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTIFACTS_AWS_SECRET_ACCESS_KEY }}
# Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy
# builders *should* have the AWS credentials available. Still, explicitly
# adding the condition is helpful as this way CI will not silently skip

View file

@ -1259,16 +1259,16 @@ dependencies = [
[[package]]
name = "fluent-bundle"
version = "0.15.3"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493"
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash 1.1.0",
"self_cell 0.10.3",
"rustc-hash 2.1.1",
"self_cell",
"smallvec",
"unic-langid",
]
@ -1284,11 +1284,12 @@ dependencies = [
[[package]]
name = "fluent-syntax"
version = "0.11.1"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"thiserror 1.0.69",
"memchr",
"thiserror 2.0.12",
]
[[package]]
@ -1934,9 +1935,9 @@ dependencies = [
[[package]]
name = "intl-memoizer"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda"
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
@ -4832,15 +4833,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "self_cell"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
dependencies = [
"self_cell 1.2.0",
]
[[package]]
name = "self_cell"
version = "1.2.0"

View file

@ -40,12 +40,6 @@ pub trait MutVisitor: Sized {
// fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare
// fn filter_map_t(&mut self, t: T) -> Option<T>; // rarest
//
// Any additions to this trait should happen in form of a call to a public
// `noop_*` function that only calls out to the visitor again, not other
// `noop_*` functions. This is a necessary API workaround to the problem of
// not being able to call out to the super default method in an overridden
// default method.
//
// When writing these methods, it is better to use destructuring like this:
//
// fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
@ -179,7 +173,7 @@ pub trait MutVisitor: Sized {
}
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
noop_filter_map_expr(self, e)
walk_filter_map_expr(self, e)
}
fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
@ -306,10 +300,6 @@ pub trait MutVisitor: Sized {
walk_precise_capturing_arg(self, arg);
}
fn visit_mt(&mut self, mt: &mut MutTy) {
walk_mt(self, mt);
}
fn visit_expr_field(&mut self, f: &mut ExprField) {
walk_expr_field(self, f);
}
@ -381,14 +371,11 @@ super::common_visitor_and_walkers!((mut) MutVisitor);
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
/// when using a `flat_map_*` or `filter_map_*` method within a `visit_`
/// method.
//
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_clobber<T: DummyAstNode>(t: &mut T, f: impl FnOnce(T) -> T) {
let old_t = std::mem::replace(t, T::dummy());
*t = f(old_t);
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
fn visit_vec<T, F>(elems: &mut Vec<T>, mut visit_elem: F)
where
@ -399,7 +386,6 @@ where
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
fn visit_thin_vec<T, F>(elems: &mut ThinVec<T>, mut visit_elem: F)
where
@ -410,7 +396,6 @@ where
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
fn visit_opt<T, F>(opt: &mut Option<T>, mut visit_elem: F)
where
@ -421,25 +406,21 @@ where
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
fn visit_attrs<T: MutVisitor>(vis: &mut T, attrs: &mut AttrVec) {
for attr in attrs.iter_mut() {
vis.visit_attribute(attr);
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[allow(unused)]
fn visit_exprs<T: MutVisitor>(vis: &mut T, exprs: &mut Vec<P<Expr>>) {
exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
fn visit_thin_exprs<T: MutVisitor>(vis: &mut T, exprs: &mut ThinVec<P<Expr>>) {
exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
match args {
AttrArgs::Empty => {}
@ -451,7 +432,6 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
fn visit_delim_args<T: MutVisitor>(vis: &mut T, args: &mut DelimArgs) {
let DelimArgs { dspan, delim: _, tokens: _ } = args;
let DelimSpan { open, close } = dspan;
@ -535,10 +515,10 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => {
}
TyKind::Slice(ty) => vis.visit_ty(ty),
TyKind::Ptr(mt) => vis.visit_mt(mt),
TyKind::Ref(lt, mt) | TyKind::PinnedRef(lt, mt) => {
TyKind::Ptr(MutTy { ty, mutbl: _ }) => vis.visit_ty(ty),
TyKind::Ref(lt, MutTy { ty, mutbl: _ }) | TyKind::PinnedRef(lt, MutTy { ty, mutbl: _ }) => {
visit_opt(lt, |lt| vis.visit_lifetime(lt));
vis.visit_mt(mt);
vis.visit_ty(ty);
}
TyKind::BareFn(bft) => {
let BareFnTy { safety, ext: _, generic_params, decl, decl_span } = bft.deref_mut();
@ -1019,10 +999,6 @@ pub fn walk_flat_map_expr_field<T: MutVisitor>(
smallvec![f]
}
fn walk_mt<T: MutVisitor>(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) {
vis.visit_ty(ty);
}
pub fn walk_block<T: MutVisitor>(vis: &mut T, block: &mut P<Block>) {
let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
vis.visit_id(id);
@ -1041,78 +1017,6 @@ pub fn walk_item_kind<K: WalkItemKind>(
kind.walk(span, id, visibility, ctxt, vis)
}
impl WalkItemKind for AssocItemKind {
type Ctxt = AssocCtxt;
fn walk<V: MutVisitor>(
&mut self,
span: Span,
id: NodeId,
visibility: &mut Visibility,
ctxt: Self::Ctxt,
visitor: &mut V,
) {
match self {
AssocItemKind::Const(item) => {
walk_const_item(visitor, item);
}
AssocItemKind::Fn(func) => {
visitor.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &mut *func), span, id);
}
AssocItemKind::Type(box TyAlias {
defaultness,
ident,
generics,
where_clauses,
bounds,
ty,
}) => {
visit_defaultness(visitor, defaultness);
visitor.visit_ident(ident);
visitor.visit_generics(generics);
visit_bounds(visitor, bounds, BoundKind::Bound);
visit_opt(ty, |ty| visitor.visit_ty(ty));
walk_ty_alias_where_clauses(visitor, where_clauses);
}
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
AssocItemKind::Delegation(box Delegation {
id,
qself,
path,
ident,
rename,
body,
from_glob: _,
}) => {
visitor.visit_id(id);
visitor.visit_qself(qself);
visitor.visit_path(path);
visitor.visit_ident(ident);
if let Some(rename) = rename {
visitor.visit_ident(rename);
}
if let Some(body) = body {
visitor.visit_block(body);
}
}
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
visitor.visit_qself(qself);
visitor.visit_path(prefix);
if let Some(suffixes) = suffixes {
for (ident, rename) in suffixes {
visitor.visit_ident(ident);
if let Some(rename) = rename {
visitor.visit_ident(rename);
}
}
}
if let Some(body) = body {
visitor.visit_block(body);
}
}
}
}
}
pub fn walk_crate<T: MutVisitor>(vis: &mut T, krate: &mut Crate) {
let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
vis.visit_id(id);
@ -1123,14 +1027,6 @@ pub fn walk_crate<T: MutVisitor>(vis: &mut T, krate: &mut Crate) {
vis.visit_span(inject_use_span);
}
pub fn walk_item(visitor: &mut impl MutVisitor, item: &mut P<Item<impl WalkItemKind<Ctxt = ()>>>) {
walk_item_ctxt(visitor, item, ())
}
pub fn walk_assoc_item(visitor: &mut impl MutVisitor, item: &mut P<AssocItem>, ctxt: AssocCtxt) {
walk_item_ctxt(visitor, item, ctxt)
}
pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P<Item>) -> SmallVec<[P<Item>; 1]> {
vis.visit_item(&mut item);
smallvec![item]
@ -1153,53 +1049,6 @@ pub fn walk_flat_map_assoc_item(
smallvec![item]
}
impl WalkItemKind for ForeignItemKind {
type Ctxt = ();
fn walk<V: MutVisitor>(
&mut self,
span: Span,
id: NodeId,
visibility: &mut Visibility,
_ctxt: Self::Ctxt,
visitor: &mut V,
) {
match self {
ForeignItemKind::Static(box StaticItem {
ident,
ty,
mutability: _,
expr,
safety: _,
define_opaque,
}) => {
visitor.visit_ident(ident);
visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr));
walk_define_opaques(visitor, define_opaque);
}
ForeignItemKind::Fn(func) => {
visitor.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &mut *func), span, id);
}
ForeignItemKind::TyAlias(box TyAlias {
defaultness,
ident,
generics,
where_clauses,
bounds,
ty,
}) => {
visit_defaultness(visitor, defaultness);
visitor.visit_ident(ident);
visitor.visit_generics(generics);
visit_bounds(visitor, bounds, BoundKind::Bound);
visit_opt(ty, |ty| visitor.visit_ty(ty));
walk_ty_alias_where_clauses(visitor, where_clauses);
}
ForeignItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
}
}
}
pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
let Pat { id, kind, span, tokens: _ } = pat.deref_mut();
vis.visit_id(id);
@ -1500,11 +1349,9 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
vis.visit_span(span);
}
pub fn noop_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
Some({
vis.visit_expr(&mut e);
e
})
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
vis.visit_expr(&mut e);
Some(e)
}
pub fn walk_flat_map_stmt<T: MutVisitor>(

View file

@ -393,9 +393,7 @@ macro_rules! common_visitor_and_walkers {
pub fn walk_fn_header<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, header: &$($lt)? $($mut)? FnHeader) $(-> <V as Visitor<$lt>>::Result)? {
let FnHeader { safety, coroutine_kind, constness, ext: _ } = header;
try_visit!(visit_constness(visitor, constness));
if let Some(coroutine_kind) = coroutine_kind {
try_visit!(visitor.visit_coroutine_kind(coroutine_kind));
}
visit_opt!(visitor, visit_coroutine_kind, coroutine_kind);
visit_safety(visitor, safety)
}
@ -417,6 +415,21 @@ macro_rules! common_visitor_and_walkers {
visit_span(visitor, span)
}
pub fn walk_item<$($lt,)? V: $Visitor$(<$lt>)?, K: WalkItemKind<Ctxt = ()>>(
visitor: &mut V,
item: &$($mut P<Item<K>>)? $($lt Item<K>)?,
) $(-> <V as Visitor<$lt>>::Result)? {
walk_item_ctxt(visitor, item, ())
}
pub fn walk_assoc_item<$($lt,)? V: $Visitor$(<$lt>)?>(
visitor: &mut V,
item: &$($mut P<AssocItem>)? $($lt AssocItem)?,
ctxt: AssocCtxt,
) $(-> <V as Visitor<$lt>>::Result)? {
walk_item_ctxt(visitor, item, ctxt)
}
impl WalkItemKind for ItemKind {
type Ctxt = ();
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
@ -580,12 +593,8 @@ macro_rules! common_visitor_and_walkers {
try_visit!(vis.visit_qself(qself));
try_visit!(vis.visit_path(path$(${ignore($lt)}, *id)?));
try_visit!(vis.visit_ident(ident));
if let Some(rename) = rename {
try_visit!(vis.visit_ident(rename));
}
if let Some(body) = body {
try_visit!(vis.visit_block(body));
}
visit_opt!(vis, visit_ident, rename);
visit_opt!(vis, visit_block, body);
$(<V as Visitor<$lt>>::Result::output())?
}
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
@ -594,14 +603,10 @@ macro_rules! common_visitor_and_walkers {
if let Some(suffixes) = suffixes {
for (ident, rename) in suffixes {
try_visit!(vis.visit_ident(ident));
if let Some(rename) = rename {
try_visit!(vis.visit_ident(rename));
}
visit_opt!(vis, visit_ident, rename);
}
}
if let Some(body) = body {
try_visit!(vis.visit_block(body));
}
visit_opt!(vis, visit_block, body);
$(<V as Visitor<$lt>>::Result::output())?
}
}
@ -643,6 +648,131 @@ macro_rules! common_visitor_and_walkers {
}
$(<V as Visitor<$lt>>::Result::output())?
}
impl WalkItemKind for AssocItemKind {
type Ctxt = AssocCtxt;
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
ctxt: Self::Ctxt,
vis: &mut V,
) $(-> <V as Visitor<$lt>>::Result)? {
match self {
AssocItemKind::Const(item) => {
walk_const_item(vis, item)
}
AssocItemKind::Fn(func) => {
vis.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func), span, id)
}
AssocItemKind::Type(box TyAlias {
generics,
ident,
bounds,
ty,
defaultness,
$(${ignore($lt)} #[expect(unused)])?
where_clauses,
}) => {
try_visit!(visit_defaultness(vis, defaultness));
try_visit!(vis.visit_ident(ident));
try_visit!(vis.visit_generics(generics));
try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
visit_opt!(vis, visit_ty, ty);
$(${ignore($mut)}
walk_ty_alias_where_clauses(vis, where_clauses);
)?
$(<V as Visitor<$lt>>::Result::output())?
}
AssocItemKind::MacCall(mac) => {
vis.visit_mac_call(mac)
}
AssocItemKind::Delegation(box Delegation {
id,
qself,
path,
ident,
rename,
body,
from_glob: _,
}) => {
try_visit!(visit_id(vis, id));
try_visit!(vis.visit_qself(qself));
try_visit!(vis.visit_path(path $(${ignore($lt)}, *id)?));
try_visit!(vis.visit_ident(ident));
visit_opt!(vis, visit_ident, rename);
visit_opt!(vis, visit_block, body);
$(<V as Visitor<$lt>>::Result::output())?
}
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
try_visit!(vis.visit_qself(qself));
try_visit!(vis.visit_path(prefix$(${ignore($lt)}, id)?));
if let Some(suffixes) = suffixes {
for (ident, rename) in suffixes {
try_visit!(vis.visit_ident(ident));
visit_opt!(vis, visit_ident, rename);
}
}
visit_opt!(vis, visit_block, body);
$(<V as Visitor<$lt>>::Result::output())?
}
}
}
}
impl WalkItemKind for ForeignItemKind {
type Ctxt = ();
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
&$($lt)? $($mut)? self,
span: Span,
id: NodeId,
visibility: &$($lt)? $($mut)? Visibility,
_ctxt: Self::Ctxt,
vis: &mut V,
) $(-> <V as Visitor<$lt>>::Result)? {
match self {
ForeignItemKind::Static(box StaticItem {
ident,
ty,
mutability: _,
expr,
safety: _,
define_opaque,
}) => {
try_visit!(vis.visit_ident(ident));
try_visit!(vis.visit_ty(ty));
visit_opt!(vis, visit_expr, expr);
walk_define_opaques(vis, define_opaque)
}
ForeignItemKind::Fn(func) => {
vis.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func), span, id)
}
ForeignItemKind::TyAlias(box TyAlias {
defaultness,
ident,
generics,
bounds,
ty,
$(${ignore($lt)} #[expect(unused)])?
where_clauses,
}) => {
try_visit!(visit_defaultness(vis, defaultness));
try_visit!(vis.visit_ident(ident));
try_visit!(vis.visit_generics(generics));
try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
visit_opt!(vis, visit_ty, ty);
$(${ignore($mut)}
walk_ty_alias_where_clauses(vis, where_clauses);
)?
$(<V as Visitor<$lt>>::Result::output())?
}
ForeignItemKind::MacCall(mac) => {
vis.visit_mac_call(mac)
}
}
}
}
};
}
@ -928,55 +1058,6 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
V::Result::output()
}
impl WalkItemKind for ForeignItemKind {
type Ctxt = ();
fn walk<'a, V: Visitor<'a>>(
&'a self,
span: Span,
id: NodeId,
vis: &'a Visibility,
_ctxt: Self::Ctxt,
visitor: &mut V,
) -> V::Result {
match self {
ForeignItemKind::Static(box StaticItem {
ident,
ty,
mutability: _,
expr,
safety: _,
define_opaque,
}) => {
try_visit!(visitor.visit_ident(ident));
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
ForeignItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Foreign, vis, &*func);
try_visit!(visitor.visit_fn(kind, span, id));
}
ForeignItemKind::TyAlias(box TyAlias {
generics,
ident,
bounds,
ty,
defaultness: _,
where_clauses: _,
}) => {
try_visit!(visitor.visit_ident(ident));
try_visit!(visitor.visit_generics(generics));
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
visit_opt!(visitor, visit_ty, ty);
}
ForeignItemKind::MacCall(mac) => {
try_visit!(visitor.visit_mac_call(mac));
}
}
V::Result::output()
}
}
pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) -> V::Result {
match bound {
GenericBound::Trait(trait_ref) => visitor.visit_poly_trait_ref(trait_ref),
@ -1135,99 +1216,6 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
V::Result::output()
}
impl WalkItemKind for AssocItemKind {
type Ctxt = AssocCtxt;
fn walk<'a, V: Visitor<'a>>(
&'a self,
span: Span,
id: NodeId,
vis: &'a Visibility,
ctxt: Self::Ctxt,
visitor: &mut V,
) -> V::Result {
match self {
AssocItemKind::Const(box ConstItem {
defaultness: _,
ident,
generics,
ty,
expr,
define_opaque,
}) => {
try_visit!(visitor.visit_ident(ident));
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
try_visit!(walk_define_opaques(visitor, define_opaque));
}
AssocItemKind::Fn(func) => {
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), vis, &*func);
try_visit!(visitor.visit_fn(kind, span, id));
}
AssocItemKind::Type(box TyAlias {
generics,
ident,
bounds,
ty,
defaultness: _,
where_clauses: _,
}) => {
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_ident(ident));
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
visit_opt!(visitor, visit_ty, ty);
}
AssocItemKind::MacCall(mac) => {
try_visit!(visitor.visit_mac_call(mac));
}
AssocItemKind::Delegation(box Delegation {
id,
qself,
path,
ident,
rename,
body,
from_glob: _,
}) => {
try_visit!(visitor.visit_qself(qself));
try_visit!(visitor.visit_path(path, *id));
try_visit!(visitor.visit_ident(ident));
visit_opt!(visitor, visit_ident, rename);
visit_opt!(visitor, visit_block, body);
}
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
try_visit!(visitor.visit_qself(qself));
try_visit!(visitor.visit_path(prefix, id));
if let Some(suffixes) = suffixes {
for (ident, rename) in suffixes {
visitor.visit_ident(ident);
if let Some(rename) = rename {
visitor.visit_ident(rename);
}
}
}
visit_opt!(visitor, visit_block, body);
}
}
V::Result::output()
}
}
pub fn walk_item<'a, V: Visitor<'a>>(
visitor: &mut V,
item: &'a Item<impl WalkItemKind<Ctxt = ()>>,
) -> V::Result {
walk_item_ctxt(visitor, item, ())
}
pub fn walk_assoc_item<'a, V: Visitor<'a>>(
visitor: &mut V,
item: &'a AssocItem,
ctxt: AssocCtxt,
) -> V::Result {
walk_item_ctxt(visitor, item, ctxt)
}
pub fn walk_struct_def<'a, V: Visitor<'a>>(
visitor: &mut V,
struct_definition: &'a VariantData,

View file

@ -4,14 +4,29 @@ use rustc_attr_data_structures::RustcVersion;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::lint::{BuiltinLintDiag, Lint};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
/// Emitter of a builtin lint from `cfg_matches`.
///
/// Used to support emiting a lint (currently on check-cfg), either:
/// - as an early buffered lint (in `rustc`)
/// - or has a "normal" lint from HIR (in `rustdoc`)
pub trait CfgMatchesLintEmitter {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
}
impl CfgMatchesLintEmitter for NodeId {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) {
sess.psess.buffer_lint(lint, sp, *self, diag);
}
}
#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
@ -25,17 +40,17 @@ pub struct Condition {
pub fn cfg_matches(
cfg: &MetaItemInner,
sess: &Session,
lint_node_id: NodeId,
lint_emitter: impl CfgMatchesLintEmitter,
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
match sess.psess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
sess.psess.buffer_lint(
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
BuiltinLintDiag::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
@ -43,10 +58,10 @@ pub fn cfg_matches(
);
}
None if sess.psess.check_config.exhaustive_names => {
sess.psess.buffer_lint(
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
lint_node_id,
BuiltinLintDiag::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),

View file

@ -606,8 +606,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
hir_args: &'hir hir::GenericArgs<'hir>,
search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>,
) -> Option<&'hir hir::Lifetime> {
for (kind, hir_arg) in iter::zip(args, hir_args.args) {
match (kind.unpack(), hir_arg) {
for (arg, hir_arg) in iter::zip(args, hir_args.args) {
match (arg.kind(), hir_arg) {
(GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => {
if r.as_var() == needle_fr {
return Some(lt);
@ -631,7 +631,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
) => {
self.dcx().span_delayed_bug(
hir_arg.span(),
format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"),
format!("unmatched arg and hir arg: found {arg:?} vs {hir_arg:?}"),
);
}
}
@ -997,7 +997,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
) -> bool {
let tcx = self.infcx.tcx;
ty.walk().any(|arg| {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Param(_) = ty.kind()
{
clauses.iter().any(|pred| {

View file

@ -148,7 +148,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
let mut next_outlives_predicates = vec![];
for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
match k1.unpack() {
match k1.kind() {
GenericArgKind::Lifetime(r1) => {
let r1_vid = self.to_region_vid(r1);
let r2_vid = self.to_region_vid(r2);

View file

@ -221,7 +221,7 @@ fn register_member_constraints<'tcx>(
.iter()
.enumerate()
.filter(|(i, _)| variances[*i] == ty::Invariant)
.filter_map(|(_, arg)| match arg.unpack() {
.filter_map(|(_, arg)| match arg.kind() {
GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
})

View file

@ -1,6 +1,11 @@
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
builtin_macros_alloc_must_statics = allocators must be statics
builtin_macros_asm_attribute_not_supported =
this attribute is not supported on assembly
builtin_macros_asm_cfg =
the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable
builtin_macros_asm_clobber_abi = clobber_abi
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
builtin_macros_asm_clobber_outputs = generic outputs
@ -9,17 +14,6 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}`
.label = previously here
.arg = duplicate argument
builtin_macros_asm_expected_comma = expected token: `,`
.label = expected `,`
builtin_macros_asm_expected_other = expected operand, {$is_inline_asm ->
[false] options
*[true] clobber_abi, options
}, or additional template string
builtin_macros_asm_expected_string_literal = expected string literal
.label = not a string literal
builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
@ -45,17 +39,8 @@ builtin_macros_asm_pure_combine = the `pure` option must be combined with either
builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output
builtin_macros_asm_requires_template = requires at least a template string argument
builtin_macros_asm_sym_no_path = expected a path for argument to `sym`
builtin_macros_asm_underscore_input = _ cannot be used for input operands
builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!`
builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!`
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!`
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
.suggestion = remove this option
@ -162,7 +147,10 @@ builtin_macros_expected_comma_in_list = expected token: `,`
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register
builtin_macros_expected_other = expected operand, {$is_inline_asm ->
[false] options
*[true] clobber_abi, options
}, or additional template string
builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently
@ -255,8 +243,6 @@ builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[defa
.label = this enum needs a unit variant marked with `#[default]`
.suggestion = make this unit variant default by placing `#[default]` on it
builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi`
builtin_macros_non_exhaustive_default = default variant must be exhaustive
.label = declared `#[non_exhaustive]` here
.help = consider a manual implementation of `Default`

View file

@ -1,4 +1,3 @@
use ast::token::IdentIsRaw;
use lint::BuiltinLintDiag;
use rustc_ast::ptr::P;
use rustc_ast::tokenstream::TokenStream;
@ -7,39 +6,16 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::PResult;
use rustc_expand::base::*;
use rustc_index::bit_set::GrowableBitSet;
use rustc_parse::exp;
use rustc_parse::parser::{ExpKeywordPair, Parser};
use rustc_parse::parser::asm::*;
use rustc_session::lint;
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
use rustc_session::parse::feature_err;
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym};
use rustc_target::asm::InlineAsmArch;
use smallvec::smallvec;
use {rustc_ast as ast, rustc_parse_format as parse};
use crate::errors;
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
/// not validated at all.
pub struct AsmArg {
pub kind: AsmArgKind,
pub span: Span,
}
pub enum AsmArgKind {
Template(P<ast::Expr>),
Operand(Option<Symbol>, ast::InlineAsmOperand),
Options(Vec<AsmOption>),
ClobberAbi(Vec<(Symbol, Span)>),
}
pub struct AsmOption {
pub symbol: Symbol,
pub span: Span,
// A bitset, with only the bit for this option's symbol set.
pub options: ast::InlineAsmOptions,
// Used when suggesting to remove an option.
pub span_with_comma: Span,
}
use crate::{errors, fluent_generated as fluent};
/// Validated assembly arguments, ready for macro expansion.
struct ValidatedAsmArgs {
@ -52,215 +28,6 @@ struct ValidatedAsmArgs {
pub options_spans: Vec<Span>,
}
/// Used for better error messages when operand types are used that are not
/// supported by the current macro (e.g. `in` or `out` for `global_asm!`)
///
/// returns
///
/// - `Ok(true)` if the current token matches the keyword, and was expected
/// - `Ok(false)` if the current token does not match the keyword
/// - `Err(_)` if the current token matches the keyword, but was not expected
fn eat_operand_keyword<'a>(
p: &mut Parser<'a>,
exp: ExpKeywordPair,
asm_macro: AsmMacro,
) -> PResult<'a, bool> {
if matches!(asm_macro, AsmMacro::Asm) {
Ok(p.eat_keyword(exp))
} else {
let span = p.token.span;
if p.eat_keyword_noexpect(exp.kw) {
// in gets printed as `r#in` otherwise
let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
span,
symbol,
macro_name: asm_macro.macro_name(),
}))
} else {
Ok(false)
}
}
}
fn parse_asm_operand<'a>(
p: &mut Parser<'a>,
asm_macro: AsmMacro,
) -> PResult<'a, Option<ast::InlineAsmOperand>> {
let dcx = p.dcx();
Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(exp!(Const)) {
let anon_const = p.parse_expr_anon_const()?;
ast::InlineAsmOperand::Const { anon_const }
} else if p.eat_keyword(exp!(Sym)) {
let expr = p.parse_expr()?;
let ast::ExprKind::Path(qself, path) = &expr.kind else {
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
return Err(err);
};
let sym =
ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
ast::InlineAsmOperand::Sym { sym }
} else {
return Ok(None);
}))
}
// Public for rustfmt.
pub fn parse_asm_args<'a>(
p: &mut Parser<'a>,
sp: Span,
asm_macro: AsmMacro,
) -> PResult<'a, Vec<AsmArg>> {
let dcx = p.dcx();
if p.token == token::Eof {
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
}
let mut args = Vec::new();
let first_template = p.parse_expr()?;
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
let mut allow_templates = true;
while p.token != token::Eof {
if !p.eat(exp!(Comma)) {
if allow_templates {
// After a template string, we always expect *only* a comma...
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
} else {
// ...after that delegate to `expect` to also include the other expected tokens.
return Err(p.expect(exp!(Comma)).err().unwrap());
}
}
// Accept trailing commas.
if p.token == token::Eof {
break;
}
let span_start = p.token.span;
// Parse `clobber_abi`.
if p.eat_keyword(exp!(ClobberAbi)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
span: span_start.to(p.prev_token.span),
});
continue;
}
// Parse `options`.
if p.eat_keyword(exp!(Options)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
span: span_start.to(p.prev_token.span),
});
continue;
}
// Parse operand names.
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
let (ident, _) = p.token.ident().unwrap();
p.bump();
p.expect(exp!(Eq))?;
allow_templates = false;
Some(ident.name)
} else {
None
};
if let Some(op) = parse_asm_operand(p, asm_macro)? {
allow_templates = false;
args.push(AsmArg {
span: span_start.to(p.prev_token.span),
kind: AsmArgKind::Operand(name, op),
});
} else if allow_templates {
let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other
// things it could have been.
match template.kind {
ast::ExprKind::Lit(token_lit)
if matches!(
token_lit.kind,
token::LitKind::Str | token::LitKind::StrRaw(_)
) => {}
ast::ExprKind::MacCall(..) => {}
_ => {
let err = dcx.create_err(errors::AsmExpectedOther {
span: template.span,
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
});
return Err(err);
}
}
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
} else {
p.unexpected_any()?
}
}
Ok(args)
}
fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
@ -278,6 +45,13 @@ fn validate_asm_args<'a>(
) -> PResult<'a, ValidatedAsmArgs> {
let dcx = ecx.dcx();
let strip_unconfigured = rustc_expand::config::StripUnconfigured {
sess: ecx.sess,
features: Some(ecx.ecfg.features),
config_tokens: false,
lint_node_id: ecx.current_expansion.lint_node_id,
};
let mut validated = ValidatedAsmArgs {
templates: vec![],
operands: vec![],
@ -291,6 +65,26 @@ fn validate_asm_args<'a>(
let mut allow_templates = true;
for arg in args {
for attr in arg.attributes.0.iter() {
match attr.name() {
Some(sym::cfg | sym::cfg_attr) => {
if !ecx.ecfg.features.asm_cfg() {
let span = attr.span();
feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg)
.emit();
}
}
_ => {
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
}
}
}
// Skip arguments that are configured out.
if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() {
continue;
}
match arg.kind {
AsmArgKind::Template(template) => {
// The error for the first template is delayed.
@ -479,103 +273,6 @@ fn validate_asm_args<'a>(
Ok(validated)
}
fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
p.expect(exp!(OpenParen))?;
let mut asm_options = Vec::new();
while !p.eat(exp!(CloseParen)) {
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
(exp!(Pure), ast::InlineAsmOptions::PURE),
(exp!(Nomem), ast::InlineAsmOptions::NOMEM),
(exp!(Readonly), ast::InlineAsmOptions::READONLY),
(exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
(exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
(exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
(exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
(exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
(exp!(Raw), ast::InlineAsmOptions::RAW),
];
'blk: {
for (exp, options) in OPTIONS {
// Gives a more accurate list of expected next tokens.
let kw_matched = if asm_macro.is_supported_option(options) {
p.eat_keyword(exp)
} else {
p.eat_keyword_noexpect(exp.kw)
};
if kw_matched {
let span = p.prev_token.span;
let span_with_comma =
if p.token == token::Comma { span.to(p.token.span) } else { span };
asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
break 'blk;
}
}
return p.unexpected_any();
}
// Allow trailing commas.
if p.eat(exp!(CloseParen)) {
break;
}
p.expect(exp!(Comma))?;
}
Ok(asm_options)
}
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
p.expect(exp!(OpenParen))?;
if p.eat(exp!(CloseParen)) {
return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
}
let mut new_abis = Vec::new();
while !p.eat(exp!(CloseParen)) {
match p.parse_str_lit() {
Ok(str_lit) => {
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
}
Err(opt_lit) => {
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
}
};
// Allow trailing commas
if p.eat(exp!(CloseParen)) {
break;
}
p.expect(exp!(Comma))?;
}
Ok(new_abis)
}
fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
p.expect(exp!(OpenParen))?;
let result = match p.token.uninterpolate().kind {
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
ast::InlineAsmRegOrRegClass::Reg(symbol)
}
_ => {
return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
span: p.token.span,
}));
}
};
p.bump();
p.expect(exp!(CloseParen))?;
Ok(result)
}
fn expand_preparsed_asm(
ecx: &mut ExtCtxt<'_>,
asm_macro: AsmMacro,

View file

@ -109,13 +109,6 @@ pub(crate) struct ProcMacro {
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_non_abi)]
pub(crate) struct NonABI {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_trace_macros)]
pub(crate) struct TraceMacros {
@ -789,51 +782,12 @@ pub(crate) struct AsmModifierInvalid {
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_requires_template)]
pub(crate) struct AsmRequiresTemplate {
#[diag(builtin_macros_asm_attribute_not_supported)]
pub(crate) struct AsmAttributeNotSupported {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_expected_comma)]
pub(crate) struct AsmExpectedComma {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_expected_string_literal)]
pub(crate) struct AsmExpectedStringLiteral {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_underscore_input)]
pub(crate) struct AsmUnderscoreInput {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_sym_no_path)]
pub(crate) struct AsmSymNoPath {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_expected_other)]
pub(crate) struct AsmExpectedOther {
#[primary_span]
#[label(builtin_macros_asm_expected_other)]
pub(crate) span: Span,
pub(crate) is_inline_asm: bool,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_duplicate_arg)]
pub(crate) struct AsmDuplicateArg {
@ -925,16 +879,6 @@ pub(crate) struct AsmUnsupportedOption {
pub(crate) macro_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_unsupported_operand)]
pub(crate) struct AsmUnsupportedOperand<'a> {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) symbol: &'a str,
pub(crate) macro_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_unsupported_clobber_abi)]
pub(crate) struct AsmUnsupportedClobberAbi {
@ -957,13 +901,6 @@ pub(crate) struct TestRunnerNargs {
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_expected_register_class_or_explicit_register)]
pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_expected_comma_in_list)]
pub(crate) struct ExpectedCommaInList {
@ -1027,3 +964,12 @@ pub(crate) struct NonGenericPointee {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_expected_other)]
pub(crate) struct AsmExpectedOther {
#[primary_span]
#[label(builtin_macros_expected_other)]
pub(crate) span: Span,
pub(crate) is_inline_asm: bool,
}

View file

@ -155,20 +155,6 @@ pub(crate) struct Regions {
impl Regions {
/// Returns true if none of this structure's tables contain any regions.
pub(crate) fn has_no_regions(&self) -> bool {
// Every region has a span, so if there are no spans then there are no regions.
self.all_cov_spans().next().is_none()
}
pub(crate) fn all_cov_spans(&self) -> impl Iterator<Item = &CoverageSpan> {
macro_rules! iter_cov_spans {
( $( $regions:expr ),* $(,)? ) => {
std::iter::empty()
$(
.chain( $regions.iter().map(|region| &region.cov_span) )
)*
}
}
let Self {
code_regions,
expansion_regions,
@ -177,13 +163,11 @@ impl Regions {
mcdc_decision_regions,
} = self;
iter_cov_spans!(
code_regions,
expansion_regions,
branch_regions,
mcdc_branch_regions,
mcdc_decision_regions,
)
code_regions.is_empty()
&& expansion_regions.is_empty()
&& branch_regions.is_empty()
&& mcdc_branch_regions.is_empty()
&& mcdc_decision_regions.is_empty()
}
}

View file

@ -11,7 +11,6 @@ use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods as _, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::{
BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping,
MappingKind, Op,
@ -105,16 +104,6 @@ fn fill_region_tables<'tcx>(
ids_info: &'tcx CoverageIdsInfo,
covfun: &mut CovfunRecord<'tcx>,
) {
// If this function is unused, replace all counters with zero.
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
let term = if covfun.is_used {
ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
} else {
CovTerm::Zero
};
ffi::Counter::from_term(term)
};
// Currently a function's mappings must all be in the same file, so use the
// first mapping's span to determine the file.
let source_map = tcx.sess.source_map();
@ -126,12 +115,6 @@ fn fill_region_tables<'tcx>(
let local_file_id = covfun.virtual_file_mapping.push_file(&source_file);
// If this testing flag is set, add an extra unused entry to the local
// file table, to help test the code for detecting unused file IDs.
if tcx.sess.coverage_inject_unused_local_file() {
covfun.virtual_file_mapping.push_file(&source_file);
}
// In rare cases, _all_ of a function's spans are discarded, and coverage
// codegen needs to handle that gracefully to avoid #133606.
// It's hard for tests to trigger this organically, so instead we set
@ -152,6 +135,16 @@ fn fill_region_tables<'tcx>(
// For each counter/region pair in this function+file, convert it to a
// form suitable for FFI.
for &Mapping { ref kind, span } in &fn_cov_info.mappings {
// If this function is unused, replace all counters with zero.
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
let term = if covfun.is_used {
ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
} else {
CovTerm::Zero
};
ffi::Counter::from_term(term)
};
let Some(coords) = make_coords(span) else { continue };
let cov_span = coords.make_coverage_span(local_file_id);
@ -184,19 +177,6 @@ fn fill_region_tables<'tcx>(
}
}
/// LLVM requires all local file IDs to have at least one mapping region.
/// If that's not the case, skip this function, to avoid an assertion failure
/// (or worse) in LLVM.
fn check_local_file_table(covfun: &CovfunRecord<'_>) -> bool {
let mut local_file_id_seen =
IndexVec::<u32, _>::from_elem_n(false, covfun.virtual_file_mapping.local_file_table.len());
for cov_span in covfun.regions.all_cov_spans() {
local_file_id_seen[cov_span.file_id] = true;
}
local_file_id_seen.into_iter().all(|seen| seen)
}
/// Generates the contents of the covfun record for this function, which
/// contains the function's coverage mapping data. The record is then stored
/// as a global variable in the `__llvm_covfun` section.
@ -205,10 +185,6 @@ pub(crate) fn generate_covfun_record<'tcx>(
global_file_table: &GlobalFileTable,
covfun: &CovfunRecord<'tcx>,
) {
if !check_local_file_table(covfun) {
return;
}
let &CovfunRecord {
mangled_function_name,
source_hash,

View file

@ -39,10 +39,7 @@ impl Coords {
/// or other expansions), and if it does happen then skipping a span or function is
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option<Coords> {
if span.is_empty() {
debug_assert!(false, "can't make coords from empty span: {span:?}");
return None;
}
let span = ensure_non_empty_span(source_map, span)?;
let lo = span.lo();
let hi = span.hi();
@ -73,6 +70,29 @@ pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span)
})
}
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
if !span.is_empty() {
return Some(span);
}
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
source_map
.span_to_source(span, |src, start, end| try {
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
// but in this case we have specifically checked that the character
// we're skipping over is one of two specific ASCII characters, so
// adjusting by exactly 1 byte is correct.
if src.as_bytes().get(end).copied() == Some(b'{') {
Some(span.with_hi(span.hi() + BytePos(1)))
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
Some(span.with_lo(span.lo() - BytePos(1)))
} else {
None
}
})
.ok()?
}
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
/// it will immediately exit with a fatal error. To prevent that from happening,
/// discard regions that are improperly ordered, or might be interpreted in a

View file

@ -63,14 +63,33 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>(
}
}
enum PassMode {
Direct,
Indirect,
}
enum SlotSize {
Bytes8 = 8,
Bytes4 = 4,
}
enum AllowHigherAlign {
No,
Yes,
}
fn emit_ptr_va_arg<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
list: OperandRef<'tcx, &'ll Value>,
target_ty: Ty<'tcx>,
indirect: bool,
slot_size: Align,
allow_higher_align: bool,
pass_mode: PassMode,
slot_size: SlotSize,
allow_higher_align: AllowHigherAlign,
) -> &'ll Value {
let indirect = matches!(pass_mode, PassMode::Indirect);
let allow_higher_align = matches!(allow_higher_align, AllowHigherAlign::Yes);
let slot_size = Align::from_bytes(slot_size as u64).unwrap();
let layout = bx.cx.layout_of(target_ty);
let (llty, size, align) = if indirect {
(
@ -179,8 +198,14 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
// On Stack block
bx.switch_to_block(on_stack);
let stack_value =
emit_ptr_va_arg(bx, list, target_ty, false, Align::from_bytes(8).unwrap(), true);
let stack_value = emit_ptr_va_arg(
bx,
list,
target_ty,
PassMode::Direct,
SlotSize::Bytes8,
AllowHigherAlign::Yes,
);
bx.br(end);
bx.switch_to_block(end);
@ -386,29 +411,43 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
// Determine the va_arg implementation to use. The LLVM va_arg instruction
// is lacking in some instances, so we should only use it as a fallback.
let target = &bx.cx.tcx.sess.target;
let arch = &bx.cx.tcx.sess.target.arch;
match &**arch {
// Windows x86
"x86" if target.is_like_windows => {
emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), false)
}
// Generic x86
"x86" => emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true),
// Windows AArch64
"aarch64" | "arm64ec" if target.is_like_windows => {
emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false)
}
// macOS / iOS AArch64
"aarch64" if target.is_like_darwin => {
emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true)
match &*target.arch {
"x86" => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes4,
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
),
"aarch64" | "arm64ec" if target.is_like_windows || target.is_like_darwin => {
emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes8,
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
)
}
"aarch64" => emit_aapcs_va_arg(bx, addr, target_ty),
"s390x" => emit_s390x_va_arg(bx, addr, target_ty),
// Windows x86_64
"x86_64" if target.is_like_windows => {
let target_ty_size = bx.cx.size_of(target_ty).bytes();
let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false)
emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes8,
AllowHigherAlign::No,
)
}
"xtensa" => emit_xtensa_va_arg(bx, addr, target_ty),
// For all other architecture/OS combinations fall back to using

View file

@ -77,7 +77,7 @@ fn dyn_trait_in_self<'tcx>(
ty: Ty<'tcx>,
) -> Option<ty::ExistentialTraitRef<'tcx>> {
for arg in ty.peel_refs().walk() {
if let GenericArgKind::Type(ty) = arg.unpack()
if let GenericArgKind::Type(ty) = arg.kind()
&& let ty::Dynamic(data, _, _) = ty.kind()
{
// FIXME(arbitrary_self_types): This is likely broken for receivers which

View file

@ -281,7 +281,7 @@ fn build_error_for_const_call<'tcx>(
let mut sugg = None;
if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
match (args[0].unpack(), args[1].unpack()) {
match (args[0].kind(), args[1].kind()) {
(GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
if self_ty == rhs_ty
&& self_ty.is_ref()

View file

@ -310,7 +310,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
let ty = tcx.ty_ordering_enum(None);
let layout =
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap();
Self::from_scalar(Scalar::from_i8(c as i8), layout)
Self::from_scalar(Scalar::Int(c.into()), layout)
}
pub fn from_pair(a: Self, b: Self, cx: &(impl HasTypingEnv<'tcx> + HasTyCtxt<'tcx>)) -> Self {

View file

@ -125,7 +125,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
) -> Result<(), PrintError> {
print_prefix(self)?;
let args =
args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_)));
if args.clone().next().is_some() {
self.generic_delimiters(|cx| cx.comma_sep(args))
} else {

View file

@ -5,8 +5,8 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
fluent-bundle = "0.15.2"
fluent-syntax = "0.11"
fluent-bundle = "0.16"
fluent-syntax = "0.12"
icu_list = "1.2"
icu_locid = "1.2"
icu_provider_adapters = "1.2"

View file

@ -1325,7 +1325,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
));
self.note("consider using `--verbose` to print the full type name to the console");
}
Box::into_inner(self.diag.take().unwrap())
*self.diag.take().unwrap()
}
/// This method allows us to access the path of the file where "long types" are written to.

View file

@ -12,7 +12,6 @@
#![feature(array_windows)]
#![feature(assert_matches)]
#![feature(associated_type_defaults)]
#![feature(box_into_inner)]
#![feature(box_patterns)]
#![feature(default_field_values)]
#![feature(error_reporter)]

View file

@ -349,7 +349,7 @@ impl MutVisitor for PlaceholderExpander {
fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
match expr.kind {
ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(),
_ => noop_filter_map_expr(self, expr),
_ => walk_filter_map_expr(self, expr),
}
}

View file

@ -371,6 +371,8 @@ declare_features! (
(unstable, arbitrary_self_types, "1.23.0", Some(44874)),
/// Allows inherent and trait methods with arbitrary self types that are raw pointers.
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
/// Allows #[cfg(...)] on inline assembly templates and operands.
(unstable, asm_cfg, "CURRENT_RUSTC_VERSION", Some(140364)),
/// Enables experimental inline assembly support for additional architectures.
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
/// Enables experimental register support in inline assembly.

View file

@ -9,8 +9,8 @@ proc-macro = true
[dependencies]
# tidy-alphabetical-start
annotate-snippets = "0.11"
fluent-bundle = "0.15.2"
fluent-syntax = "0.11"
fluent-bundle = "0.16"
fluent-syntax = "0.12"
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }

View file

@ -1575,7 +1575,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
let mut params_used = DenseBitSet::new_empty(generics.own_params.len());
for leaf in ty.walk() {
if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
if let GenericArgKind::Type(leaf_ty) = leaf.kind()
&& let ty::Param(param) = leaf_ty.kind()
{
debug!("found use of ty param {:?}", param);
@ -1700,7 +1700,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG
let mut label_match = |ty: Ty<'_>, span| {
for arg in ty.walk() {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Alias(
ty::Opaque,
ty::AliasTy { def_id: captured_def_id, .. },

View file

@ -1231,7 +1231,7 @@ fn check_region_late_boundedness<'tcx>(
for (id_arg, arg) in
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args)
{
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
if let ty::GenericArgKind::Lifetime(r) = arg.kind()
&& let ty::ReVar(vid) = r.kind()
&& let r = infcx
.inner
@ -1256,7 +1256,7 @@ fn check_region_late_boundedness<'tcx>(
for (id_arg, arg) in
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args)
{
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
if let ty::GenericArgKind::Lifetime(r) = arg.kind()
&& let ty::ReVar(vid) = r.kind()
&& let r = infcx
.inner

View file

@ -427,7 +427,7 @@ fn report_mismatched_rpitit_captures<'tcx>(
};
trait_captured_args
.sort_by_cached_key(|arg| !matches!(arg.unpack(), ty::GenericArgKind::Lifetime(_)));
.sort_by_cached_key(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
let suggestion = format!("use<{}>", trait_captured_args.iter().join(", "));
tcx.emit_node_span_lint(

View file

@ -819,7 +819,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
match t.kind() {
ty::Alias(ty::Projection, p) if p.def_id == self.gat => {
for (idx, arg) in p.args.iter().enumerate() {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(lt) if !lt.is_bound() => {
self.regions.insert((lt, idx));
}
@ -1517,7 +1517,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
} else {
// If we've got a generic const parameter we still want to check its
// type is correct in case both it and the param type are fully concrete.
let GenericArgKind::Const(ct) = default.unpack() else {
let GenericArgKind::Const(ct) = default.kind() else {
continue;
};

View file

@ -31,7 +31,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics};
use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
use rustc_middle::hir::nested_filter;
use rustc_middle::query::Providers;
use rustc_middle::ty::util::{Discr, IntTypeExt};
@ -40,7 +40,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::{ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations};
use tracing::{debug, instrument};
use crate::errors;
@ -625,6 +625,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
(input_tys, output_ty)
}
fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec<DynCompatibilityViolation> {
hir_ty_lowering_dyn_compatibility_violations(self.tcx, trait_def_id)
}
}
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.

View file

@ -151,7 +151,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>(
let mut mapping = FxIndexMap::default();
let generics = tcx.generics_of(assoc_item_def_id);
for (param, var) in std::iter::zip(&generics.own_params, gat_vars) {
let existing = match var.unpack() {
let existing = match var.kind() {
ty::GenericArgKind::Lifetime(re) => {
if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() {
mapping.insert(bv.var, tcx.mk_param_from_def(param))

View file

@ -11,7 +11,7 @@ use rustc_middle::ty::{
};
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
use rustc_trait_selection::traits;
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};
@ -97,8 +97,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// to avoid ICEs.
for (clause, span) in user_written_bounds {
if let Some(trait_pred) = clause.as_trait_clause() {
let violations =
hir_ty_lowering_dyn_compatibility_violations(tcx, trait_pred.def_id());
let violations = self.dyn_compatibility_violations(trait_pred.def_id());
if !violations.is_empty() {
let reported = report_dyn_incompatibility(
tcx,
@ -216,7 +215,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be instantiated with a
// `trait_object_dummy_self`, so check for that.
let references_self = match pred.skip_binder().term.unpack() {
let references_self = match pred.skip_binder().term.kind() {
ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
// FIXME(associated_const_equality): We should walk the const instead of not doing anything
ty::TermKind::Const(_) => false,

View file

@ -33,7 +33,7 @@ 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_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
@ -200,6 +200,10 @@ pub trait HirTyLowerer<'tcx> {
{
self
}
/// Performs minimalistic dyn compat checks outside of bodies, but full within bodies.
/// Outside of bodies we could end up in cycles, so we delay most checks to later phases.
fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec<DynCompatibilityViolation>;
}
/// The "qualified self" of an associated item path.
@ -607,7 +611,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if !infer_args && has_default {
// No type parameter provided, but a default exists.
if let Some(prev) =
preceding_args.iter().find_map(|arg| match arg.unpack() {
preceding_args.iter().find_map(|arg| match arg.kind() {
GenericArgKind::Type(ty) => ty.error_reported().err(),
_ => None,
})

View file

@ -119,7 +119,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
) {
for arg in ty.walk() {
let leaf_ty = match arg.unpack() {
let leaf_ty = match arg.kind() {
GenericArgKind::Type(ty) => ty,
// No predicates from lifetimes or constants, except potentially
@ -299,7 +299,7 @@ fn check_explicit_predicates<'tcx>(
// binding) and thus infer an outlives requirement that `X:
// 'b`.
if let Some(self_ty) = ignored_self_ty
&& let GenericArgKind::Type(ty) = outlives_predicate.0.unpack()
&& let GenericArgKind::Type(ty) = outlives_predicate.0.kind()
&& ty.walk().any(|arg| arg == self_ty.into())
{
debug!("skipping self ty = {ty:?}");

View file

@ -69,8 +69,8 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
.map(|(&def_id, set)| {
let predicates =
&*tcx.arena.alloc_from_iter(set.as_ref().skip_binder().iter().filter_map(
|(ty::OutlivesPredicate(kind1, region2), &span)| {
match kind1.unpack() {
|(ty::OutlivesPredicate(arg1, region2), &span)| {
match arg1.kind() {
GenericArgKind::Type(ty1) => Some((
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty1, *region2))
.upcast(tcx),

View file

@ -14,7 +14,7 @@ pub(crate) type RequiredPredicates<'tcx> =
/// outlives_component and add it to `required_predicates`
pub(crate) fn insert_outlives_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
kind: GenericArg<'tcx>,
arg: GenericArg<'tcx>,
outlived_region: Region<'tcx>,
span: Span,
required_predicates: &mut RequiredPredicates<'tcx>,
@ -25,7 +25,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
return;
}
match kind.unpack() {
match arg.kind() {
GenericArgKind::Type(ty) => {
// `T: 'outlived_region` for some type `T`
// But T could be a lot of things:
@ -135,7 +135,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
if !is_free_region(r) {
return;
}
required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span);
required_predicates.entry(ty::OutlivesPredicate(arg, outlived_region)).or_insert(span);
}
GenericArgKind::Const(_) => {

View file

@ -200,8 +200,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
// Trait are always invariant so we can take advantage of that.
let variance_i = self.invariant(variance);
for k in args {
match k.unpack() {
for arg in args {
match arg.kind() {
GenericArgKind::Lifetime(lt) => {
self.add_constraints_from_region(current, lt, variance_i)
}
@ -294,7 +294,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
for projection in data.projection_bounds() {
match projection.skip_binder().term.unpack() {
match projection.skip_binder().term.kind() {
ty::TermKind::Ty(ty) => {
self.add_constraints_from_ty(current, ty, self.invariant);
}
@ -373,7 +373,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
(None, Some(self.tcx().variances_of(def_id)))
};
for (i, k) in args.iter().enumerate() {
for (i, arg) in args.iter().enumerate() {
let variance_decl = if let Some(InferredIndex(start)) = local {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
@ -389,7 +389,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
"add_constraints_from_args: variance_decl={:?} variance_i={:?}",
variance_decl, variance_i
);
match k.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(lt) => {
self.add_constraints_from_region(current, lt, variance_i)
}

View file

@ -80,12 +80,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| {
predicate_args.iter().find_map(|arg| {
arg.walk().find_map(|arg| {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Param(param_ty) = *ty.kind()
&& matches(ty::ParamTerm::Ty(param_ty))
{
Some(arg)
} else if let ty::GenericArgKind::Const(ct) = arg.unpack()
} else if let ty::GenericArgKind::Const(ct) = arg.kind()
&& let ty::ConstKind::Param(param_ct) = ct.kind()
&& matches(ty::ParamTerm::Const(param_ct))
{
@ -357,7 +357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
error: &mut traits::FulfillmentError<'tcx>,
def_id: DefId,
param: ty::GenericArg<'tcx>,
arg: ty::GenericArg<'tcx>,
qpath: &hir::QPath<'tcx>,
) -> bool {
match qpath {
@ -365,7 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for segment in path.segments.iter().rev() {
if let Res::Def(kind, def_id) = segment.res
&& !matches!(kind, DefKind::Mod | DefKind::ForeignMod)
&& self.point_at_generic_if_possible(error, def_id, param, segment)
&& self.point_at_generic_if_possible(error, def_id, arg, segment)
{
return true;
}
@ -373,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Handle `Self` param specifically, since it's separated in
// the path representation
if let Some(self_ty) = self_ty
&& let ty::GenericArgKind::Type(ty) = param.unpack()
&& let ty::GenericArgKind::Type(ty) = arg.kind()
&& ty == self.tcx.types.self_param
{
error.obligation.cause.span = self_ty
@ -384,12 +384,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
hir::QPath::TypeRelative(self_ty, segment) => {
if self.point_at_generic_if_possible(error, def_id, param, segment) {
if self.point_at_generic_if_possible(error, def_id, arg, segment) {
return true;
}
// Handle `Self` param specifically, since it's separated in
// the path representation
if let ty::GenericArgKind::Type(ty) = param.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& ty == self.tcx.types.self_param
{
error.obligation.cause.span = self_ty
@ -424,10 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the args list does not, then we should chop off all of the lifetimes,
// since they're all elided.
let segment_args = segment.args().args;
if matches!(own_args[0].unpack(), ty::GenericArgKind::Lifetime(_))
if matches!(own_args[0].kind(), ty::GenericArgKind::Lifetime(_))
&& segment_args.first().is_some_and(|arg| arg.is_ty_or_const())
&& let Some(offset) = own_args.iter().position(|arg| {
matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_))
matches!(arg.kind(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_))
})
&& let Some(new_index) = index.checked_sub(offset)
{
@ -750,7 +750,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ok(expr);
}
let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else {
let ty::GenericArgKind::Type(in_ty) = in_ty.kind() else {
return Err(expr);
};
@ -1045,7 +1045,7 @@ fn find_param_in_ty<'tcx>(
if arg == param_to_point_at {
return true;
}
if let ty::GenericArgKind::Type(ty) = arg.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind()
{
// This logic may seem a bit strange, but typically when

View file

@ -1556,25 +1556,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
SuggestionText::Reorder => Some("reorder these arguments".to_string()),
SuggestionText::DidYouMean => Some("did you mean".to_string()),
};
if let Some(suggestion_text) = suggestion_text {
if let Some(suggestion_text) = suggestion_text
&& !full_call_span.in_external_macro(self.sess().source_map())
{
let source_map = self.sess().source_map();
let (mut suggestion, suggestion_span) = if let Some(call_span) =
full_call_span.find_ancestor_inside_same_ctxt(error_span)
{
("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) {
// Span of the braces, e.g. `(a, b, c)`.
args_span
} else {
(
format!(
"{}(",
source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
fn_def_id.map_or("".to_string(), |fn_def_id| {
tcx.item_name(fn_def_id).to_string()
})
})
),
error_span,
)
// The arg span of a function call that wasn't even given braces
// like what might happen with delegation reuse.
// e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
full_call_span.shrink_to_hi()
};
let mut suggestion = "(".to_owned();
let mut needs_comma = false;
for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
if needs_comma {

View file

@ -14,7 +14,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, HirId, ItemLocalMap};
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
use rustc_infer::infer;
use rustc_infer::traits::Obligation;
use rustc_infer::traits::{DynCompatibilityViolation, Obligation};
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
@ -388,6 +388,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
};
(input_tys, output_ty)
}
fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec<DynCompatibilityViolation> {
self.tcx.dyn_compatibility_violations(trait_def_id).to_vec()
}
}
/// The `ty` representation of a user-provided type. Depending on the use-site

View file

@ -2226,7 +2226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let infer_args = self.tcx.mk_args_from_iter(args.into_iter().map(|arg| {
if !arg.is_suggestable(self.tcx, true) {
has_unsuggestable_args = true;
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(_) => self
.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
.into(),
@ -2843,7 +2843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let [first] = ***args else {
return;
};
let ty::GenericArgKind::Type(ty) = first.unpack() else {
let ty::GenericArgKind::Type(ty) = first.kind() else {
return;
};
let Ok(pick) = self.lookup_probe_for_diagnostic(

View file

@ -347,7 +347,7 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> {
fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
TypeTrace {
cause: cause.clone(),
values: match (a.unpack(), b.unpack()) {
values: match (a.kind(), b.kind()) {
(GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
ValuePairs::Regions(ExpectedFound::new(a, b))
}

View file

@ -61,15 +61,15 @@ where
value
} else {
let delegate = FnMutDelegate {
regions: &mut |br: ty::BoundRegion| match var_values[br.var].unpack() {
regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() {
GenericArgKind::Lifetime(l) => l,
r => bug!("{:?} is a region but value is {:?}", br, r),
},
types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].unpack() {
types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() {
GenericArgKind::Type(ty) => ty,
r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
},
consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].unpack() {
consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() {
GenericArgKind::Const(ct) => ct,
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
},

View file

@ -245,7 +245,7 @@ impl<'tcx> InferCtxt<'tcx> {
let result_value = query_response.instantiate_projected(self.tcx, &result_args, |v| {
v.var_values[BoundVar::new(index)]
});
match (original_value.unpack(), result_value.unpack()) {
match (original_value.kind(), result_value.kind()) {
(GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
if re1.is_erased() && re2.is_erased() =>
{
@ -402,7 +402,7 @@ impl<'tcx> InferCtxt<'tcx> {
// [(?A, Vec<?0>), ('static, '?1), (?B, ?0)]
for (original_value, result_value) in iter::zip(&original_values.var_values, result_values)
{
match result_value.unpack() {
match result_value.kind() {
GenericArgKind::Type(result_value) => {
// e.g., here `result_value` might be `?0` in the example above...
if let ty::Bound(debruijn, b) = *result_value.kind() {
@ -533,7 +533,7 @@ impl<'tcx> InferCtxt<'tcx> {
for (index, value1) in variables1.var_values.iter().enumerate() {
let value2 = variables2(BoundVar::new(index));
match (value1.unpack(), value2.unpack()) {
match (value1.kind(), value2.kind()) {
(GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
obligations.extend(
self.at(cause, param_env)

View file

@ -90,7 +90,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
}
fn is_changed_arg(&self, arg: ty::GenericArg<'tcx>) -> bool {
match arg.unpack() {
match arg.kind() {
ty::GenericArgKind::Lifetime(_) => {
// Lifetimes should not change affect trait selection.
false

View file

@ -1372,7 +1372,7 @@ impl<'tcx> TyOrConstInferVar {
/// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and
/// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`).
pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option<Self> {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Type(ty) => Self::maybe_from_ty(ty),
GenericArgKind::Const(ct) => Self::maybe_from_const(ct),
GenericArgKind::Lifetime(_) => None,
@ -1383,7 +1383,7 @@ impl<'tcx> TyOrConstInferVar {
/// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and
/// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`).
pub fn maybe_from_term(term: Term<'tcx>) -> Option<Self> {
match term.unpack() {
match term.kind() {
TermKind::Ty(ty) => Self::maybe_from_ty(ty),
TermKind::Const(ct) => Self::maybe_from_const(ct),
}

View file

@ -87,7 +87,7 @@ impl<'tcx> InferCtxt<'tcx> {
ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
cause: &ObligationCause<'tcx>,
) {
match arg.unpack() {
match arg.kind() {
ty::GenericArgKind::Lifetime(r1) => {
self.register_region_outlives_constraint(ty::OutlivesPredicate(r1, r2), cause);
}
@ -503,8 +503,8 @@ where
opt_variances: Option<&[ty::Variance]>,
) {
let constraint = origin.to_constraint_category();
for (index, k) in args.iter().enumerate() {
match k.unpack() {
for (index, arg) in args.iter().enumerate() {
match arg.kind() {
GenericArgKind::Lifetime(lt) => {
let variance = if let Some(variances) = opt_variances {
variances[index]

View file

@ -329,7 +329,7 @@ struct Generalizer<'me, 'tcx> {
impl<'tcx> Generalizer<'_, 'tcx> {
/// Create an error that corresponds to the term kind in `root_term`
fn cyclic_term_error(&self) -> TypeError<'tcx> {
match self.root_term.unpack() {
match self.root_term.kind() {
ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty),
ty::TermKind::Const(ct) => TypeError::CyclicConst(ct),
}

View file

@ -776,8 +776,7 @@ fn test_unstable_options_tracking_hash() {
CoverageOptions {
level: CoverageLevel::Mcdc,
no_mir_spans: true,
discard_all_spans_in_codegen: true,
inject_unused_local_file: true,
discard_all_spans_in_codegen: true
}
);
tracked!(crate_attr, vec!["abc".to_string()]);

View file

@ -807,6 +807,11 @@ lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're
lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver
.note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler
lint_undefined_transmute = pointers cannot be transmuted to integers during const eval
.note = at compile-time, pointers do not have an integer value
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior
.help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html
lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
.label = argument has type `{$arg_ty}`
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value

View file

@ -18,7 +18,7 @@ use tracing::debug;
use crate::context::{EarlyContext, LintContext, LintStore};
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
mod diagnostics;
pub(super) mod diagnostics;
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
@ -40,7 +40,7 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
self.context.opt_span_lint(lint_id.lint, span, |diag| {
diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag);
diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag);
});
}
}

View file

@ -18,7 +18,7 @@ use crate::lints::{self, ElidedNamedLifetime};
mod check_cfg;
pub(super) fn decorate_lint(
pub fn decorate_builtin_lint(
sess: &Session,
tcx: Option<TyCtxt<'_>>,
diagnostic: BuiltinLintDiag,

View file

@ -461,7 +461,7 @@ fn extract_def_id_from_arg<'tcx>(
generics: &'tcx ty::Generics,
arg: ty::GenericArg<'tcx>,
) -> DefId {
match arg.unpack() {
match arg.kind() {
ty::GenericArgKind::Lifetime(re) => match re.kind() {
ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
ty::ReBound(

View file

@ -75,6 +75,7 @@ mod reference_casting;
mod shadowed_into_iter;
mod static_mut_refs;
mod traits;
mod transmute;
mod types;
mod unit_bindings;
mod unqualified_local_imports;
@ -118,6 +119,7 @@ use shadowed_into_iter::ShadowedIntoIter;
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
use static_mut_refs::*;
use traits::*;
use transmute::CheckTransmutes;
use types::*;
use unit_bindings::*;
use unqualified_local_imports::*;
@ -126,6 +128,7 @@ use unused::*;
#[rustfmt::skip]
pub use builtin::{MissingDoc, SoftLints};
pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
pub use early::diagnostics::decorate_builtin_lint;
pub use early::{EarlyCheckNode, check_ast_node};
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
pub use levels::LintLevelsBuilder;
@ -245,6 +248,7 @@ late_lint_methods!(
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
UnqualifiedLocalImports: UnqualifiedLocalImports,
CheckTransmutes: CheckTransmutes,
]
]
);

View file

@ -0,0 +1,278 @@
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{self as hir};
use rustc_macros::LintDiagnostic;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::sym;
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
/// transmute in const functions and associated constants.
///
/// ### Example
///
/// ```rust
/// const fn foo(ptr: *const u8) -> usize {
/// unsafe {
/// std::mem::transmute::<*const u8, usize>(ptr)
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Transmuting pointers to integers in a `const` context is undefined behavior.
/// Any attempt to use the resulting integer will abort const-evaluation.
///
/// But sometimes the compiler might not emit an error for pointer to integer transmutes
/// inside const functions and associated consts because they are evaluated only when referenced.
/// Therefore, this lint serves as an extra layer of defense to prevent any undefined behavior
/// from compiling without any warnings or errors.
///
/// See [std::mem::transmute] in the reference for more details.
///
/// [std::mem::transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html
pub PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
Warn,
"detects pointer to integer transmutes in const functions and associated constants",
}
declare_lint! {
/// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives.
///
/// ### Example
///
/// ```rust
/// fn bytes_at_home(x: [u8; 4]) -> u32 {
/// unsafe { std::mem::transmute(x) }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Using an explicit method is preferable over calls to
/// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as
/// they more clearly communicate the intent, are easier to review, and
/// are less likely to accidentally result in unsoundness.
pub UNNECESSARY_TRANSMUTES,
Warn,
"detects transmutes that can also be achieved by other operations"
}
pub(crate) struct CheckTransmutes;
impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
let hir::ExprKind::Call(callee, [arg]) = expr.kind else {
return;
};
let hir::ExprKind::Path(qpath) = callee.kind else {
return;
};
let Res::Def(DefKind::Fn, def_id) = cx.qpath_res(&qpath, callee.hir_id) else {
return;
};
if !cx.tcx.is_intrinsic(def_id, sym::transmute) {
return;
};
let body_owner_def_id = cx.tcx.hir_enclosing_body_owner(expr.hir_id);
let const_context = cx.tcx.hir_body_const_context(body_owner_def_id);
let args = cx.typeck_results().node_args(callee.hir_id);
let src = args.type_at(0);
let dst = args.type_at(1);
check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
}
}
/// Check for transmutes that exhibit undefined behavior.
/// For example, transmuting pointers to integers in a const context.
///
/// Why do we consider const functions and associated constants only?
///
/// Generally, undefined behavior in const items are handled by the evaluator.
/// But, const functions and associated constants are evaluated only when referenced.
/// This can result in undefined behavior in a library going unnoticed until
/// the function or constant is actually used.
///
/// Therefore, we only consider const functions and associated constants here and leave
/// other const items to be handled by the evaluator.
fn check_ptr_transmute_in_const<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
body_owner_def_id: LocalDefId,
const_context: Option<hir::ConstContext>,
src: Ty<'tcx>,
dst: Ty<'tcx>,
) {
if matches!(const_context, Some(hir::ConstContext::ConstFn))
|| matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst)
{
if src.is_raw_ptr() && dst.is_integral() {
cx.tcx.emit_node_span_lint(
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
expr.hir_id,
expr.span,
UndefinedTransmuteLint,
);
}
}
}
/// Check for transmutes that overlap with stdlib methods.
/// For example, transmuting `[u8; 4]` to `u32`.
///
/// We chose not to lint u8 -> bool transmutes, see #140431.
fn check_unnecessary_transmute<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
callee: &'tcx hir::Expr<'tcx>,
arg: &'tcx hir::Expr<'tcx>,
const_context: Option<hir::ConstContext>,
src: Ty<'tcx>,
dst: Ty<'tcx>,
) {
let callee_span = callee.span.find_ancestor_inside(expr.span).unwrap_or(callee.span);
let (sugg, help) = match (src.kind(), dst.kind()) {
// dont check the length; transmute does that for us.
// [u8; _] => primitive
(ty::Array(t, _), ty::Uint(_) | ty::Float(_) | ty::Int(_))
if *t.kind() == ty::Uint(ty::UintTy::U8) =>
{
(
Some(vec![(callee_span, format!("{dst}::from_ne_bytes"))]),
Some(
"there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order",
),
)
}
// primitive => [u8; _]
(ty::Uint(_) | ty::Float(_) | ty::Int(_), ty::Array(t, _))
if *t.kind() == ty::Uint(ty::UintTy::U8) =>
{
(
Some(vec![(callee_span, format!("{src}::to_ne_bytes"))]),
Some(
"there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order",
),
)
}
// char → u32
(ty::Char, ty::Uint(ty::UintTy::U32)) => {
(Some(vec![(callee_span, "u32::from".to_string())]), None)
}
// char (→ u32) → i32
(ty::Char, ty::Int(ty::IntTy::I32)) => (
Some(vec![
(callee_span, "u32::from".to_string()),
(expr.span.shrink_to_hi(), ".cast_signed()".to_string()),
]),
None,
),
// u32 → char
(ty::Uint(ty::UintTy::U32), ty::Char) => (
Some(vec![(callee_span, "char::from_u32_unchecked".to_string())]),
Some("consider using `char::from_u32(…).unwrap()`"),
),
// i32 → char
(ty::Int(ty::IntTy::I32), ty::Char) => (
Some(vec![
(callee_span, "char::from_u32_unchecked(i32::cast_unsigned".to_string()),
(expr.span.shrink_to_hi(), ")".to_string()),
]),
Some("consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()`"),
),
// uNN → iNN
(ty::Uint(_), ty::Int(_)) => {
(Some(vec![(callee_span, format!("{src}::cast_signed"))]), None)
}
// iNN → uNN
(ty::Int(_), ty::Uint(_)) => {
(Some(vec![(callee_span, format!("{src}::cast_unsigned"))]), None)
}
// fNN → usize, isize
(ty::Float(_), ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize)) => (
Some(vec![
(callee_span, format!("{src}::to_bits")),
(expr.span.shrink_to_hi(), format!(" as {dst}")),
]),
None,
),
// fNN (→ uNN) → iNN
(ty::Float(_), ty::Int(..)) => (
Some(vec![
(callee_span, format!("{src}::to_bits")),
(expr.span.shrink_to_hi(), ".cast_signed()".to_string()),
]),
None,
),
// fNN → uNN
(ty::Float(_), ty::Uint(..)) => {
(Some(vec![(callee_span, format!("{src}::to_bits"))]), None)
}
// xsize → fNN
(ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize), ty::Float(_)) => (
Some(vec![
(callee_span, format!("{dst}::from_bits")),
(arg.span.shrink_to_hi(), " as _".to_string()),
]),
None,
),
// iNN (→ uNN) → fNN
(ty::Int(_), ty::Float(_)) => (
Some(vec![
(callee_span, format!("{dst}::from_bits({src}::cast_unsigned")),
(expr.span.shrink_to_hi(), ")".to_string()),
]),
None,
),
// uNN → fNN
(ty::Uint(_), ty::Float(_)) => {
(Some(vec![(callee_span, format!("{dst}::from_bits"))]), None)
}
// bool → x8 in const context since `From::from` is not const yet
// FIXME: Consider arg expr's precedence to avoid parentheses.
// FIXME(const_traits): Remove this when `From::from` is constified.
(ty::Bool, ty::Int(..) | ty::Uint(..)) if const_context.is_some() => (
Some(vec![
(callee_span, "".to_string()),
(expr.span.shrink_to_hi(), format!(" as {dst}")),
]),
None,
),
// bool → x8 using `x8::from`
(ty::Bool, ty::Int(..) | ty::Uint(..)) => {
(Some(vec![(callee_span, format!("{dst}::from"))]), None)
}
_ => return,
};
cx.tcx.node_span_lint(UNNECESSARY_TRANSMUTES, expr.hir_id, expr.span, |diag| {
diag.primary_message("unnecessary transmute");
if let Some(sugg) = sugg {
diag.multipart_suggestion("replace this with", sugg, Applicability::MachineApplicable);
}
if let Some(help) = help {
diag.help(help);
}
});
}
#[derive(LintDiagnostic)]
#[diag(lint_undefined_transmute)]
#[note]
#[note(lint_note2)]
#[help]
pub(crate) struct UndefinedTransmuteLint;

View file

@ -79,7 +79,6 @@ declare_lint_pass! {
PRIVATE_BOUNDS,
PRIVATE_INTERFACES,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
REDUNDANT_IMPORTS,
REDUNDANT_LIFETIMES,
@ -118,7 +117,6 @@ declare_lint_pass! {
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
UNNAMEABLE_TEST_ITEMS,
UNNAMEABLE_TYPES,
UNNECESSARY_TRANSMUTES,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNSAFE_ATTR_OUTSIDE_UNSAFE,
@ -4851,64 +4849,6 @@ declare_lint! {
@feature_gate = supertrait_item_shadowing;
}
declare_lint! {
/// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer
/// transmute in const functions and associated constants.
///
/// ### Example
///
/// ```rust
/// const fn foo(ptr: *const u8) -> usize {
/// unsafe {
/// std::mem::transmute::<*const u8, usize>(ptr)
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Transmuting pointers to integers in a `const` context is undefined behavior.
/// Any attempt to use the resulting integer will abort const-evaluation.
///
/// But sometimes the compiler might not emit an error for pointer to integer transmutes
/// inside const functions and associated consts because they are evaluated only when referenced.
/// Therefore, this lint serves as an extra layer of defense to prevent any undefined behavior
/// from compiling without any warnings or errors.
///
/// See [std::mem::transmute] in the reference for more details.
///
/// [std::mem::transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html
pub PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
Warn,
"detects pointer to integer transmutes in const functions and associated constants",
}
declare_lint! {
/// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives.
///
/// ### Example
///
/// ```rust
/// fn bytes_at_home(x: [u8; 4]) -> u32 {
/// unsafe { std::mem::transmute(x) }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Using an explicit method is preferable over calls to
/// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as
/// they more clearly communicate the intent, are easier to review, and
/// are less likely to accidentally result in unsoundness.
pub UNNECESSARY_TRANSMUTES,
Warn,
"detects transmutes that are shadowed by std methods"
}
declare_lint! {
/// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location,
/// that runs a custom `Drop` destructor.

View file

@ -310,7 +310,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// This should only be used for determining the context of a body, a return
/// value of `Some` does not always suggest that the owner of the body is `const`,
/// just that it has to be checked as if it were.
pub fn hir_body_const_context(self, def_id: impl Into<DefId>) -> Option<ConstContext> {
pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext> {
let def_id = def_id.into();
let ccx = match self.hir_body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { inline },

View file

@ -180,27 +180,27 @@ impl<Prov> Scalar<Prov> {
#[inline]
pub fn from_i8(i: i8) -> Self {
Self::from_int(i, Size::from_bits(8))
Self::Int(i.into())
}
#[inline]
pub fn from_i16(i: i16) -> Self {
Self::from_int(i, Size::from_bits(16))
Self::Int(i.into())
}
#[inline]
pub fn from_i32(i: i32) -> Self {
Self::from_int(i, Size::from_bits(32))
Self::Int(i.into())
}
#[inline]
pub fn from_i64(i: i64) -> Self {
Self::from_int(i, Size::from_bits(64))
Self::Int(i.into())
}
#[inline]
pub fn from_i128(i: i128) -> Self {
Self::from_int(i, Size::from_bits(128))
Self::Int(i.into())
}
#[inline]

View file

@ -1624,7 +1624,11 @@ pub fn write_allocations<'tcx>(
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
write!(w, " (static: {}", tcx.def_path_str(did))?;
if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)
&& tcx.hir_body_const_context(body.source.def_id()).is_some()
&& body
.source
.def_id()
.as_local()
.is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some())
{
// Statics may be cyclic and evaluating them too early
// in the MIR pipeline may cause cycle errors even though

View file

@ -422,9 +422,9 @@ macro_rules! from_scalar_int_for_x {
impl From<ScalarInt> for $ty {
#[inline]
fn from(int: ScalarInt) -> Self {
// The `unwrap` cannot fail because to_bits (if it succeeds)
// The `unwrap` cannot fail because to_uint (if it succeeds)
// is guaranteed to return a value that fits into the size.
int.to_bits(Size::from_bytes(size_of::<$ty>()))
int.to_uint(Size::from_bytes(size_of::<$ty>()))
.try_into().unwrap()
}
}
@ -450,6 +450,49 @@ impl From<char> for ScalarInt {
}
}
macro_rules! from_x_for_scalar_int_signed {
($($ty:ty),*) => {
$(
impl From<$ty> for ScalarInt {
#[inline]
fn from(u: $ty) -> Self {
Self {
data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size
size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
}
}
}
)*
}
}
macro_rules! from_scalar_int_for_x_signed {
($($ty:ty),*) => {
$(
impl From<ScalarInt> for $ty {
#[inline]
fn from(int: ScalarInt) -> Self {
// The `unwrap` cannot fail because to_int (if it succeeds)
// is guaranteed to return a value that fits into the size.
int.to_int(Size::from_bytes(size_of::<$ty>()))
.try_into().unwrap()
}
}
)*
}
}
from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128);
from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128);
impl From<std::cmp::Ordering> for ScalarInt {
#[inline]
fn from(c: std::cmp::Ordering) -> Self {
// Here we rely on `Ordering` having the same values in host and target!
ScalarInt::from(c as i8)
}
}
/// Error returned when a conversion from ScalarInt to char fails.
#[derive(Debug)]
pub struct CharTryFromScalarInt;

View file

@ -2781,7 +2781,7 @@ impl<'tcx> TyCtxt<'tcx> {
return false;
}
if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) {
if !matches!(args[0].kind(), ty::GenericArgKind::Type(_)) {
return false;
}
@ -2803,7 +2803,7 @@ impl<'tcx> TyCtxt<'tcx> {
};
for (param, arg) in std::iter::zip(&generics.own_params, own_args) {
match (&param.kind, arg.unpack()) {
match (&param.kind, arg.kind()) {
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}

View file

@ -137,7 +137,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> {
type Kind = GenericArgKind<'tcx>;
fn kind(self) -> Self::Kind {
self.unpack()
self.kind()
}
}
@ -218,7 +218,7 @@ impl<'tcx> From<ty::Const<'tcx>> for GenericArg<'tcx> {
impl<'tcx> From<ty::Term<'tcx>> for GenericArg<'tcx> {
fn from(value: ty::Term<'tcx>) -> Self {
match value.unpack() {
match value.kind() {
ty::TermKind::Ty(t) => t.into(),
ty::TermKind::Const(c) => c.into(),
}
@ -227,7 +227,7 @@ impl<'tcx> From<ty::Term<'tcx>> for GenericArg<'tcx> {
impl<'tcx> GenericArg<'tcx> {
#[inline]
pub fn unpack(self) -> GenericArgKind<'tcx> {
pub fn kind(self) -> GenericArgKind<'tcx> {
let ptr =
unsafe { self.ptr.map_addr(|addr| NonZero::new_unchecked(addr.get() & !TAG_MASK)) };
// SAFETY: use of `Interned::new_unchecked` here is ok because these
@ -251,7 +251,7 @@ impl<'tcx> GenericArg<'tcx> {
#[inline]
pub fn as_region(self) -> Option<ty::Region<'tcx>> {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(re) => Some(re),
_ => None,
}
@ -259,7 +259,7 @@ impl<'tcx> GenericArg<'tcx> {
#[inline]
pub fn as_type(self) -> Option<Ty<'tcx>> {
match self.unpack() {
match self.kind() {
GenericArgKind::Type(ty) => Some(ty),
_ => None,
}
@ -267,7 +267,7 @@ impl<'tcx> GenericArg<'tcx> {
#[inline]
pub fn as_const(self) -> Option<ty::Const<'tcx>> {
match self.unpack() {
match self.kind() {
GenericArgKind::Const(ct) => Some(ct),
_ => None,
}
@ -275,7 +275,7 @@ impl<'tcx> GenericArg<'tcx> {
#[inline]
pub fn as_term(self) -> Option<ty::Term<'tcx>> {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(_) => None,
GenericArgKind::Type(ty) => Some(ty.into()),
GenericArgKind::Const(ct) => Some(ct.into()),
@ -300,7 +300,7 @@ impl<'tcx> GenericArg<'tcx> {
}
pub fn is_non_region_infer(self) -> bool {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(_) => false,
// FIXME: This shouldn't return numerical/float.
GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(),
@ -327,7 +327,7 @@ impl<'a, 'tcx> Lift<TyCtxt<'tcx>> for GenericArg<'a> {
type Lifted = GenericArg<'tcx>;
fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => tcx.lift(lt).map(|lt| lt.into()),
GenericArgKind::Type(ty) => tcx.lift(ty).map(|ty| ty.into()),
GenericArgKind::Const(ct) => tcx.lift(ct).map(|ct| ct.into()),
@ -340,7 +340,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArg<'tcx> {
self,
folder: &mut F,
) -> Result<Self, F::Error> {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into),
GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into),
GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
@ -348,7 +348,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArg<'tcx> {
}
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(),
GenericArgKind::Type(ty) => ty.fold_with(folder).into(),
GenericArgKind::Const(ct) => ct.fold_with(folder).into(),
@ -358,7 +358,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArg<'tcx> {
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for GenericArg<'tcx> {
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.visit_with(visitor),
GenericArgKind::Type(ty) => ty.visit_with(visitor),
GenericArgKind::Const(ct) => ct.visit_with(visitor),
@ -368,7 +368,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for GenericArg<'tcx> {
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for GenericArg<'tcx> {
fn encode(&self, e: &mut E) {
self.unpack().encode(e)
self.kind().encode(e)
}
}
@ -390,7 +390,7 @@ impl<'tcx> GenericArgs<'tcx> {
///
/// If any of the generic arguments are not types.
pub fn into_type_list(&self, tcx: TyCtxt<'tcx>) -> &'tcx List<Ty<'tcx>> {
tcx.mk_type_list_from_iter(self.iter().map(|arg| match arg.unpack() {
tcx.mk_type_list_from_iter(self.iter().map(|arg| match arg.kind() {
GenericArgKind::Type(ty) => ty,
_ => bug!("`into_type_list` called on generic arg with non-types"),
}))
@ -527,7 +527,7 @@ impl<'tcx> GenericArgs<'tcx> {
/// Returns generic arguments that are not lifetimes.
#[inline]
pub fn non_erasable_generics(&self) -> impl DoubleEndedIterator<Item = GenericArgKind<'tcx>> {
self.iter().filter_map(|k| match k.unpack() {
self.iter().filter_map(|arg| match arg.kind() {
ty::GenericArgKind::Lifetime(_) => None,
generic => Some(generic),
})

View file

@ -60,7 +60,7 @@ where
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::GenericArg<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
self.unpack().hash_stable(hcx, hasher);
self.kind().hash_stable(hcx, hasher);
}
}

View file

@ -504,7 +504,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> {
type Kind = TermKind<'tcx>;
fn kind(self) -> Self::Kind {
self.unpack()
self.kind()
}
}
@ -521,7 +521,7 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync
impl Debug for Term<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.unpack() {
match self.kind() {
TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"),
TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"),
}
@ -542,7 +542,7 @@ impl<'tcx> From<Const<'tcx>> for Term<'tcx> {
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Term<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
self.unpack().hash_stable(hcx, hasher);
self.kind().hash_stable(hcx, hasher);
}
}
@ -551,14 +551,14 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Term<'tcx> {
self,
folder: &mut F,
) -> Result<Self, F::Error> {
match self.unpack() {
match self.kind() {
ty::TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into),
ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
}
}
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
match self.unpack() {
match self.kind() {
ty::TermKind::Ty(ty) => ty.fold_with(folder).into(),
ty::TermKind::Const(ct) => ct.fold_with(folder).into(),
}
@ -567,7 +567,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Term<'tcx> {
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Term<'tcx> {
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
match self.unpack() {
match self.kind() {
ty::TermKind::Ty(ty) => ty.visit_with(visitor),
ty::TermKind::Const(ct) => ct.visit_with(visitor),
}
@ -576,7 +576,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Term<'tcx> {
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for Term<'tcx> {
fn encode(&self, e: &mut E) {
self.unpack().encode(e)
self.kind().encode(e)
}
}
@ -589,7 +589,7 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for Term<'tcx> {
impl<'tcx> Term<'tcx> {
#[inline]
pub fn unpack(self) -> TermKind<'tcx> {
pub fn kind(self) -> TermKind<'tcx> {
let ptr =
unsafe { self.ptr.map_addr(|addr| NonZero::new_unchecked(addr.get() & !TAG_MASK)) };
// SAFETY: use of `Interned::new_unchecked` here is ok because these
@ -609,7 +609,7 @@ impl<'tcx> Term<'tcx> {
}
pub fn as_type(&self) -> Option<Ty<'tcx>> {
if let TermKind::Ty(ty) = self.unpack() { Some(ty) } else { None }
if let TermKind::Ty(ty) = self.kind() { Some(ty) } else { None }
}
pub fn expect_type(&self) -> Ty<'tcx> {
@ -617,7 +617,7 @@ impl<'tcx> Term<'tcx> {
}
pub fn as_const(&self) -> Option<Const<'tcx>> {
if let TermKind::Const(c) = self.unpack() { Some(c) } else { None }
if let TermKind::Const(c) = self.kind() { Some(c) } else { None }
}
pub fn expect_const(&self) -> Const<'tcx> {
@ -625,14 +625,14 @@ impl<'tcx> Term<'tcx> {
}
pub fn into_arg(self) -> GenericArg<'tcx> {
match self.unpack() {
match self.kind() {
TermKind::Ty(ty) => ty.into(),
TermKind::Const(c) => c.into(),
}
}
pub fn to_alias_term(self) -> Option<AliasTerm<'tcx>> {
match self.unpack() {
match self.kind() {
TermKind::Ty(ty) => match *ty.kind() {
ty::Alias(_kind, alias_ty) => Some(alias_ty.into()),
_ => None,
@ -645,7 +645,7 @@ impl<'tcx> Term<'tcx> {
}
pub fn is_infer(&self) -> bool {
match self.unpack() {
match self.kind() {
TermKind::Ty(ty) => ty.is_ty_var(),
TermKind::Const(ct) => ct.is_ct_infer(),
}

View file

@ -120,7 +120,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
}
}
match self.map.get(&r.into()).map(|k| k.unpack()) {
match self.map.get(&r.into()).map(|arg| arg.kind()) {
Some(GenericArgKind::Lifetime(r1)) => r1,
Some(u) => panic!("region mapped to unexpected kind: {u:?}"),
None if self.do_not_error => self.tcx.lifetimes.re_static,
@ -162,7 +162,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
ty::Param(param) => {
// Look it up in the generic parameters list.
match self.map.get(&ty.into()).map(|k| k.unpack()) {
match self.map.get(&ty.into()).map(|arg| arg.kind()) {
// Found it in the generic parameters list; replace with the parameter from the
// opaque type.
Some(GenericArgKind::Type(t1)) => t1,
@ -195,7 +195,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
match ct.kind() {
ty::ConstKind::Param(..) => {
// Look it up in the generic parameters list.
match self.map.get(&ct.into()).map(|k| k.unpack()) {
match self.map.get(&ct.into()).map(|arg| arg.kind()) {
// Found it in the generic parameters list, replace with the parameter from the
// opaque type.
Some(GenericArgKind::Const(c1)) => c1,

View file

@ -1239,7 +1239,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name()));
match term.unpack() {
match term.kind() {
TermKind::Ty(ty) => p!(print(ty)),
TermKind::Const(c) => p!(print(c)),
};
@ -3386,7 +3386,7 @@ define_print_and_forward_display! {
}
ty::Term<'tcx> {
match self.unpack() {
match self.kind() {
ty::TermKind::Ty(ty) => p!(print(ty)),
ty::TermKind::Const(c) => p!(print(c)),
}
@ -3401,7 +3401,7 @@ define_print_and_forward_display! {
}
GenericArg<'tcx> {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => p!(print(lt)),
GenericArgKind::Type(ty) => p!(print(ty)),
GenericArgKind::Const(ct) => p!(print(ct)),

View file

@ -169,7 +169,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArg<'tcx> {
a: ty::GenericArg<'tcx>,
b: ty::GenericArg<'tcx>,
) -> RelateResult<'tcx, ty::GenericArg<'tcx>> {
match (a.unpack(), b.unpack()) {
match (a.kind(), b.kind()) {
(ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => {
Ok(relation.relate(a_lt, b_lt)?.into())
}
@ -190,7 +190,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Term<'tcx> {
a: Self,
b: Self,
) -> RelateResult<'tcx, Self> {
Ok(match (a.unpack(), b.unpack()) {
Ok(match (a.kind(), b.kind()) {
(ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(),
(ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(),
_ => return Err(TypeError::Mismatch),

View file

@ -202,7 +202,7 @@ impl<T: fmt::Debug> fmt::Debug for ty::Placeholder<T> {
impl<'tcx> fmt::Debug for GenericArg<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.unpack() {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.fmt(f),
GenericArgKind::Type(ty) => ty.fmt(f),
GenericArgKind::Const(ct) => ct.fmt(f),
@ -326,7 +326,7 @@ impl<'tcx, T: Lift<TyCtxt<'tcx>>> Lift<TyCtxt<'tcx>> for Option<T> {
impl<'a, 'tcx> Lift<TyCtxt<'tcx>> for Term<'a> {
type Lifted = ty::Term<'tcx>;
fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
match self.unpack() {
match self.kind() {
TermKind::Ty(ty) => tcx.lift(ty).map(Into::into),
TermKind::Const(c) => tcx.lift(c).map(Into::into),
}

View file

@ -777,8 +777,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
return false;
}
iter::zip(user_args.args, BoundVar::ZERO..).all(|(kind, cvar)| {
match kind.unpack() {
iter::zip(user_args.args, BoundVar::ZERO..).all(|(arg, cvar)| {
match arg.kind() {
GenericArgKind::Type(ty) => match ty.kind() {
ty::Bound(debruijn, b) => {
// We only allow a `ty::INNERMOST` index in generic parameters.

View file

@ -516,8 +516,8 @@ impl<'tcx> TyCtxt<'tcx> {
let item_args = ty::GenericArgs::identity_for_item(self, def.did());
let result = iter::zip(item_args, impl_args)
.filter(|&(_, k)| {
match k.unpack() {
.filter(|&(_, arg)| {
match arg.kind() {
GenericArgKind::Lifetime(region) => match region.kind() {
ty::ReEarlyParam(ebr) => {
!impl_generics.region_param(ebr, self).pure_wrt_drop
@ -554,7 +554,7 @@ impl<'tcx> TyCtxt<'tcx> {
let mut seen = GrowableBitSet::default();
let mut seen_late = FxHashSet::default();
for arg in args {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(lt) => match (ignore_regions, lt.kind()) {
(CheckRegions::FromFunction, ty::ReBound(di, reg)) => {
if !seen_late.insert((di, reg)) {

View file

@ -131,7 +131,7 @@ impl<'tcx> ConstToPat<'tcx> {
.dcx()
.create_err(ConstPatternDependsOnGenericParameter { span: self.span });
for arg in uv.args {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
if let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Param(param_ty) = ty.kind()
{
let def_id = self.tcx.hir_enclosing_body_owner(self.id);

View file

@ -78,10 +78,4 @@ mir_transform_unconditional_recursion = function cannot return without recursing
mir_transform_unconditional_recursion_call_site_label = recursive call site
mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval
.note = at compile-time, pointers do not have an integer value
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior
.help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html
mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored
mir_transform_unnecessary_transmute = unnecessary transmute

View file

@ -1,77 +0,0 @@
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind};
use rustc_middle::ty::{AssocItem, AssocKind, TyCtxt};
use rustc_session::lint::builtin::PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS;
use rustc_span::sym;
use crate::errors;
/// Check for transmutes that exhibit undefined behavior.
/// For example, transmuting pointers to integers in a const context.
pub(super) struct CheckUndefinedTransmutes;
impl<'tcx> crate::MirLint<'tcx> for CheckUndefinedTransmutes {
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let mut checker = UndefinedTransmutesChecker { body, tcx };
checker.visit_body(body);
}
}
struct UndefinedTransmutesChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
}
impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> {
// This functions checks two things:
// 1. `function` takes a raw pointer as input and returns an integer as output.
// 2. `function` is called from a const function or an associated constant.
//
// Why do we consider const functions and associated constants only?
//
// Generally, undefined behavior in const items are handled by the evaluator.
// But, const functions and associated constants are evaluated only when referenced.
// This can result in undefined behavior in a library going unnoticed until
// the function or constant is actually used.
//
// Therefore, we only consider const functions and associated constants here and leave
// other const items to be handled by the evaluator.
fn is_ptr_to_int_in_const(&self, function: &Operand<'tcx>) -> bool {
let def_id = self.body.source.def_id();
if self.tcx.is_const_fn(def_id)
|| matches!(
self.tcx.opt_associated_item(def_id),
Some(AssocItem { kind: AssocKind::Const { .. }, .. })
)
{
let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
if let [input] = fn_sig.inputs() {
return input.is_raw_ptr() && fn_sig.output().is_integral();
}
}
false
}
}
impl<'tcx> Visitor<'tcx> for UndefinedTransmutesChecker<'_, 'tcx> {
// Check each block's terminator for calls to pointer to integer transmutes
// in const functions or associated constants and emit a lint.
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
if let TerminatorKind::Call { func, .. } = &terminator.kind
&& let Some((func_def_id, _)) = func.const_fn_def()
&& self.tcx.is_intrinsic(func_def_id, sym::transmute)
&& self.is_ptr_to_int_in_const(func)
&& let Some(call_id) = self.body.source.def_id().as_local()
{
let hir_id = self.tcx.local_def_id_to_hir_id(call_id);
let span = self.body.source_info(location).span;
self.tcx.emit_node_span_lint(
PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
hir_id,
span,
errors::UndefinedTransmute,
);
}
}
}

View file

@ -1,136 +0,0 @@
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind};
use rustc_middle::ty::*;
use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES;
use rustc_span::source_map::Spanned;
use rustc_span::{Span, sym};
use crate::errors::UnnecessaryTransmute as Error;
/// Check for transmutes that overlap with stdlib methods.
/// For example, transmuting `[u8; 4]` to `u32`.
/// We chose not to lint u8 -> bool transmutes, see #140431
pub(super) struct CheckUnnecessaryTransmutes;
impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes {
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let mut checker = UnnecessaryTransmuteChecker { body, tcx };
checker.visit_body(body);
}
}
struct UnnecessaryTransmuteChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
}
impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> {
fn is_unnecessary_transmute(
&self,
function: &Operand<'tcx>,
arg: String,
span: Span,
is_in_const: bool,
) -> Option<Error> {
let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
let [input] = fn_sig.inputs() else { return None };
let err = |sugg| Error { span, sugg, help: None };
Some(match (input.kind(), fn_sig.output().kind()) {
// dont check the length; transmute does that for us.
// [u8; _] => primitive
(Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error {
sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()),
help: Some(
"there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order",
),
span,
},
// primitive => [u8; _]
(Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error {
sugg: format!("{input}::to_ne_bytes({arg})"),
help: Some(
"there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order",
),
span,
},
// char → u32
(Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")),
// char (→ u32) → i32
(Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")),
// u32 → char
(Uint(UintTy::U32), Char) => Error {
sugg: format!("char::from_u32_unchecked({arg})"),
help: Some("consider `char::from_u32(…).unwrap()`"),
span,
},
// i32 → char
(Int(IntTy::I32), Char) => Error {
sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"),
help: Some("consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`"),
span,
},
// uNN → iNN
(Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())),
// iNN → uNN
(Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())),
// fNN → xsize
(Float(ty), Uint(UintTy::Usize)) => {
err(format!("{}::to_bits({arg}) as usize", ty.name_str()))
}
(Float(ty), Int(IntTy::Isize)) => {
err(format!("{}::to_bits({arg}) as isize", ty.name_str()))
}
// fNN (→ uNN) → iNN
(Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())),
// fNN → uNN
(Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())),
// xsize → fNN
(Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => {
err(format!("{}::from_bits({arg} as _)", ty.name_str(),))
}
// iNN (→ uNN) → fNN
(Int(int_ty), Float(ty)) => err(format!(
"{}::from_bits({}::cast_unsigned({arg}))",
ty.name_str(),
int_ty.name_str()
)),
// uNN → fNN
(Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())),
// bool → { x8 } in const context since `From::from` is not const yet
// FIXME: is it possible to know when the parentheses arent necessary?
// FIXME(const_traits): Remove this when From::from is constified?
(Bool, Int(..) | Uint(..)) if is_in_const => {
err(format!("({arg}) as {}", fn_sig.output()))
}
// " using `x8::from`
(Bool, Int(..) | Uint(..)) => err(format!("{}::from({arg})", fn_sig.output())),
_ => return None,
})
}
}
impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> {
// Check each block's terminator for calls to pointer to integer transmutes
// in const functions or associated constants and emit a lint.
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
if let TerminatorKind::Call { func, args, .. } = &terminator.kind
&& let [Spanned { span: arg, .. }] = **args
&& let Some((func_def_id, _)) = func.const_fn_def()
&& self.tcx.is_intrinsic(func_def_id, sym::transmute)
&& let span = self.body.source_info(location).span
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg)
&& let def_id = self.body.source.def_id()
&& let Some(lint) = self.is_unnecessary_transmute(
func,
snippet,
span,
self.tcx.hir_body_const_context(def_id.expect_local()).is_some(),
)
&& let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes)
{
self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint);
}
}
}

View file

@ -1,8 +1,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir;
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
use tracing::instrument;
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
@ -84,18 +83,8 @@ pub(super) fn extract_refined_covspans<'tcx>(
// Discard any span that overlaps with a hole.
discard_spans_overlapping_holes(&mut covspans, &holes);
// Discard spans that overlap in unwanted ways.
// Perform more refinement steps after holes have been dealt with.
let mut covspans = remove_unwanted_overlapping_spans(covspans);
// For all empty spans, either enlarge them to be non-empty, or discard them.
let source_map = tcx.sess.source_map();
covspans.retain_mut(|covspan| {
let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
covspan.span = span;
true
});
// Merge covspans that can be merged.
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
@ -241,26 +230,3 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
// - Both have the same start and span A extends further right
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
}
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
if !span.is_empty() {
return Some(span);
}
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
source_map
.span_to_source(span, |src, start, end| try {
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
// but in this case we have specifically checked that the character
// we're skipping over is one of two specific ASCII characters, so
// adjusting by exactly 1 byte is correct.
if src.as_bytes().get(end).copied() == Some(b'{') {
Some(span.with_hi(span.hi() + BytePos(1)))
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
Some(span.with_lo(span.lo() - BytePos(1)))
} else {
None
}
})
.ok()?
}

View file

@ -158,33 +158,6 @@ pub(crate) struct MustNotSuspendReason {
pub reason: String,
}
pub(crate) struct UnnecessaryTransmute {
pub span: Span,
pub sugg: String,
pub help: Option<&'static str>,
}
// Needed for def_path_str
impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute {
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
diag.primary_message(fluent::mir_transform_unnecessary_transmute);
diag.span_suggestion(
self.span,
"replace this with",
self.sugg,
lint::Applicability::MachineApplicable,
);
self.help.map(|help| diag.help(help));
}
}
#[derive(LintDiagnostic)]
#[diag(mir_transform_undefined_transmute)]
#[note]
#[note(mir_transform_note2)]
#[help]
pub(crate) struct UndefinedTransmute;
#[derive(Diagnostic)]
#[diag(mir_transform_force_inline)]
#[note]

View file

@ -638,6 +638,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
place: PlaceRef<'tcx>,
value: VnIndex,
proj: PlaceElem<'tcx>,
from_non_ssa_index: &mut bool,
) -> Option<VnIndex> {
let proj = match proj {
ProjectionElem::Deref => {
@ -682,6 +683,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
ProjectionElem::Index(idx) => {
if let Value::Repeat(inner, _) = self.get(value) {
*from_non_ssa_index |= self.locals[idx].is_none();
return Some(*inner);
}
let idx = self.locals[idx]?;
@ -774,6 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// Invariant: `value` holds the value up-to the `index`th projection excluded.
let mut value = self.locals[place.local]?;
let mut from_non_ssa_index = false;
for (index, proj) in place.projection.iter().enumerate() {
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
&& let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer)
@ -791,7 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
let base = PlaceRef { local: place.local, projection: &place.projection[..index] };
value = self.project(base, value, proj)?;
value = self.project(base, value, proj, &mut from_non_ssa_index)?;
}
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@ -804,6 +807,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
if let Some(new_local) = self.try_as_local(value, location) {
place_ref = PlaceRef { local: new_local, projection: &[] };
} else if from_non_ssa_index {
// If access to non-SSA locals is unavoidable, bail out.
return None;
}
if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() {

View file

@ -123,8 +123,6 @@ declare_passes! {
mod check_const_item_mutation : CheckConstItemMutation;
mod check_null : CheckNull;
mod check_packed_ref : CheckPackedRef;
mod check_undefined_transmutes : CheckUndefinedTransmutes;
mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes;
// This pass is public to allow external drivers to perform MIR cleanup
pub mod cleanup_post_borrowck : CleanupPostBorrowck;
@ -390,8 +388,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
&Lint(check_packed_ref::CheckPackedRef),
&Lint(check_const_item_mutation::CheckConstItemMutation),
&Lint(function_item_references::FunctionItemReferences),
&Lint(check_undefined_transmutes::CheckUndefinedTransmutes),
&Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes),
// What we need to do constant evaluation.
&simplify::SimplifyCfg::Initial,
&Lint(sanity_check::SanityCheck),

View file

@ -817,8 +817,8 @@ where
/// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`.
/// If `kind` is an integer inference variable this will still return a ty infer var.
pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term {
match kind.kind() {
pub(super) fn next_term_infer_of_kind(&mut self, term: I::Term) -> I::Term {
match term.kind() {
ty::TermKind::Ty(_) => self.next_ty_infer().into(),
ty::TermKind::Const(_) => self.next_const_infer().into(),
}

View file

@ -8,6 +8,30 @@ parse_array_brackets_instead_of_braces = this is a block expression, not an arra
parse_array_index_offset_of = array indexing not supported in offset_of
parse_asm_expected_comma = expected token: `,`
.label = expected `,`
parse_asm_expected_other = expected operand, {$is_inline_asm ->
[false] options
*[true] clobber_abi, options
}, or additional template string
parse_asm_expected_register_class_or_explicit_register = expected register class or explicit register
parse_asm_expected_string_literal = expected string literal
.label = not a string literal
parse_asm_non_abi = at least one abi must be provided as an argument to `clobber_abi`
parse_asm_requires_template = requires at least a template string argument
parse_asm_sym_no_path = expected a path for argument to `sym`
parse_asm_underscore_input = _ cannot be used for input operands
parse_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!`
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
parse_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is not allowed
parse_associated_static_item_not_allowed = associated `static` items are not allowed

View file

@ -3525,3 +3525,73 @@ pub(crate) struct MoveSelfModifier {
pub insertion_span: Span,
pub modifier: String,
}
#[derive(Diagnostic)]
#[diag(parse_asm_unsupported_operand)]
pub(crate) struct AsmUnsupportedOperand<'a> {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) symbol: &'a str,
pub(crate) macro_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(parse_asm_underscore_input)]
pub(crate) struct AsmUnderscoreInput {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_sym_no_path)]
pub(crate) struct AsmSymNoPath {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_requires_template)]
pub(crate) struct AsmRequiresTemplate {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_expected_comma)]
pub(crate) struct AsmExpectedComma {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_expected_other)]
pub(crate) struct AsmExpectedOther {
#[primary_span]
#[label(parse_asm_expected_other)]
pub(crate) span: Span,
pub(crate) is_inline_asm: bool,
}
#[derive(Diagnostic)]
#[diag(parse_asm_non_abi)]
pub(crate) struct NonABI {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_expected_string_literal)]
pub(crate) struct AsmExpectedStringLiteral {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_asm_expected_register_class_or_explicit_register)]
pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
#[primary_span]
pub(crate) span: Span,
}

View file

@ -0,0 +1,385 @@
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, AsmMacro};
use rustc_span::{Span, Symbol, kw};
use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos};
use crate::{PResult, Parser, errors, exp, token};
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
/// not validated at all.
pub struct AsmArg {
pub kind: AsmArgKind,
pub attributes: AsmAttrVec,
pub span: Span,
}
pub enum AsmArgKind {
Template(P<ast::Expr>),
Operand(Option<Symbol>, ast::InlineAsmOperand),
Options(Vec<AsmOption>),
ClobberAbi(Vec<(Symbol, Span)>),
}
pub struct AsmOption {
pub symbol: Symbol,
pub span: Span,
// A bitset, with only the bit for this option's symbol set.
pub options: ast::InlineAsmOptions,
// Used when suggesting to remove an option.
pub span_with_comma: Span,
}
/// A parsed list of attributes that is not attached to any item.
/// Used to check whether `asm!` arguments are configured out.
pub struct AsmAttrVec(pub ast::AttrVec);
impl AsmAttrVec {
fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> {
let attrs = p.parse_outer_attributes()?;
p.collect_tokens(None, attrs, ForceCollect::No, |_, attrs| {
Ok((Self(attrs), Trailing::No, UsePreAttrPos::No))
})
}
}
impl ast::HasAttrs for AsmAttrVec {
// Follows `ast::Expr`.
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
fn attrs(&self) -> &[rustc_ast::Attribute] {
&self.0
}
fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) {
f(&mut self.0)
}
}
impl ast::HasTokens for AsmAttrVec {
fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> {
None
}
fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> {
None
}
}
/// Used for better error messages when operand types are used that are not
/// supported by the current macro (e.g. `in` or `out` for `global_asm!`)
///
/// returns
///
/// - `Ok(true)` if the current token matches the keyword, and was expected
/// - `Ok(false)` if the current token does not match the keyword
/// - `Err(_)` if the current token matches the keyword, but was not expected
fn eat_operand_keyword<'a>(
p: &mut Parser<'a>,
exp: ExpKeywordPair,
asm_macro: AsmMacro,
) -> PResult<'a, bool> {
if matches!(asm_macro, AsmMacro::Asm) {
Ok(p.eat_keyword(exp))
} else {
let span = p.token.span;
if p.eat_keyword_noexpect(exp.kw) {
// in gets printed as `r#in` otherwise
let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
span,
symbol,
macro_name: asm_macro.macro_name(),
}))
} else {
Ok(false)
}
}
}
fn parse_asm_operand<'a>(
p: &mut Parser<'a>,
asm_macro: AsmMacro,
) -> PResult<'a, Option<ast::InlineAsmOperand>> {
let dcx = p.dcx();
Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(exp!(Const)) {
let anon_const = p.parse_expr_anon_const()?;
ast::InlineAsmOperand::Const { anon_const }
} else if p.eat_keyword(exp!(Sym)) {
let expr = p.parse_expr()?;
let ast::ExprKind::Path(qself, path) = &expr.kind else {
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
return Err(err);
};
let sym =
ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
ast::InlineAsmOperand::Sym { sym }
} else {
return Ok(None);
}))
}
// Public for rustfmt.
pub fn parse_asm_args<'a>(
p: &mut Parser<'a>,
sp: Span,
asm_macro: AsmMacro,
) -> PResult<'a, Vec<AsmArg>> {
let dcx = p.dcx();
if p.token == token::Eof {
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
}
let mut args = Vec::new();
let attributes = AsmAttrVec::parse(p)?;
let first_template = p.parse_expr()?;
args.push(AsmArg {
span: first_template.span,
kind: AsmArgKind::Template(first_template),
attributes,
});
let mut allow_templates = true;
while p.token != token::Eof {
if !p.eat(exp!(Comma)) {
if allow_templates {
// After a template string, we always expect *only* a comma...
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
} else {
// ...after that delegate to `expect` to also include the other expected tokens.
return Err(p.expect(exp!(Comma)).err().unwrap());
}
}
// Accept trailing commas.
if p.token == token::Eof {
break;
}
let attributes = AsmAttrVec::parse(p)?;
let span_start = p.token.span;
// Parse `clobber_abi`.
if p.eat_keyword(exp!(ClobberAbi)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
span: span_start.to(p.prev_token.span),
attributes,
});
continue;
}
// Parse `options`.
if p.eat_keyword(exp!(Options)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
span: span_start.to(p.prev_token.span),
attributes,
});
continue;
}
// Parse operand names.
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
let (ident, _) = p.token.ident().unwrap();
p.bump();
p.expect(exp!(Eq))?;
allow_templates = false;
Some(ident.name)
} else {
None
};
if let Some(op) = parse_asm_operand(p, asm_macro)? {
allow_templates = false;
args.push(AsmArg {
span: span_start.to(p.prev_token.span),
kind: AsmArgKind::Operand(name, op),
attributes,
});
} else if allow_templates {
let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other
// things it could have been.
match template.kind {
ast::ExprKind::Lit(token_lit)
if matches!(
token_lit.kind,
token::LitKind::Str | token::LitKind::StrRaw(_)
) => {}
ast::ExprKind::MacCall(..) => {}
_ => {
let err = dcx.create_err(errors::AsmExpectedOther {
span: template.span,
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
});
return Err(err);
}
}
args.push(AsmArg {
span: template.span,
kind: AsmArgKind::Template(template),
attributes,
});
} else {
p.unexpected_any()?
}
}
Ok(args)
}
fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
p.expect(exp!(OpenParen))?;
let mut asm_options = Vec::new();
while !p.eat(exp!(CloseParen)) {
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
(exp!(Pure), ast::InlineAsmOptions::PURE),
(exp!(Nomem), ast::InlineAsmOptions::NOMEM),
(exp!(Readonly), ast::InlineAsmOptions::READONLY),
(exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
(exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
(exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
(exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
(exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
(exp!(Raw), ast::InlineAsmOptions::RAW),
];
'blk: {
for (exp, options) in OPTIONS {
// Gives a more accurate list of expected next tokens.
let kw_matched = if asm_macro.is_supported_option(options) {
p.eat_keyword(exp)
} else {
p.eat_keyword_noexpect(exp.kw)
};
if kw_matched {
let span = p.prev_token.span;
let span_with_comma =
if p.token == token::Comma { span.to(p.token.span) } else { span };
asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
break 'blk;
}
}
return p.unexpected_any();
}
// Allow trailing commas.
if p.eat(exp!(CloseParen)) {
break;
}
p.expect(exp!(Comma))?;
}
Ok(asm_options)
}
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
p.expect(exp!(OpenParen))?;
if p.eat(exp!(CloseParen)) {
return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
}
let mut new_abis = Vec::new();
while !p.eat(exp!(CloseParen)) {
match p.parse_str_lit() {
Ok(str_lit) => {
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
}
Err(opt_lit) => {
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
}
};
// Allow trailing commas
if p.eat(exp!(CloseParen)) {
break;
}
p.expect(exp!(Comma))?;
}
Ok(new_abis)
}
fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
p.expect(exp!(OpenParen))?;
let result = match p.token.uninterpolate().kind {
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
ast::InlineAsmRegOrRegClass::Reg(symbol)
}
_ => {
return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
span: p.token.span,
}));
}
};
p.bump();
p.expect(exp!(CloseParen))?;
Ok(result)
}

View file

@ -1,3 +1,4 @@
pub mod asm;
pub mod attr;
mod attr_wrapper;
mod diagnostics;

View file

@ -79,7 +79,7 @@ fn encode_args<'tcx>(
s.push('I');
let def_generics = tcx.generics_of(for_def);
for (n, arg) in args.iter().enumerate() {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(region) => {
s.push_str(&encode_region(region, dict));
}
@ -245,7 +245,7 @@ fn encode_predicate<'tcx>(
let name = encode_ty_name(tcx, projection.def_id);
let _ = write!(s, "u{}{}", name.len(), name);
s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options));
match projection.term.unpack() {
match projection.term.kind() {
TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)),
TermKind::Const(c) => s.push_str(&encode_const(
tcx,

View file

@ -195,11 +195,6 @@ pub struct CoverageOptions {
/// regression tests for #133606, because we don't have an easy way to
/// reproduce it from actual source code.
pub discard_all_spans_in_codegen: bool,
/// `-Zcoverage-options=inject-unused-local-file`: During codegen, add an
/// extra dummy entry to each function's local file table, to exercise the
/// code that checks for local file IDs with no mapping regions.
pub inject_unused_local_file: bool,
}
/// Controls whether branch coverage or MC/DC coverage is enabled.

View file

@ -1413,7 +1413,6 @@ pub mod parse {
"mcdc" => slot.level = CoverageLevel::Mcdc,
"no-mir-spans" => slot.no_mir_spans = true,
"discard-all-spans-in-codegen" => slot.discard_all_spans_in_codegen = true,
"inject-unused-local-file" => slot.inject_unused_local_file = true,
_ => return false,
}
}

View file

@ -371,11 +371,6 @@ impl Session {
self.opts.unstable_opts.coverage_options.discard_all_spans_in_codegen
}
/// True if testing flag `-Zcoverage-options=inject-unused-local-file` was passed.
pub fn coverage_inject_unused_local_file(&self) -> bool {
self.opts.unstable_opts.coverage_options.inject_unused_local_file
}
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
}

View file

@ -100,7 +100,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
stable_mir::ty::ExistentialProjection {
def_id: tables.trait_def(*def_id),
generic_args: args.stable(tables),
term: term.unpack().stable(tables),
term: term.kind().stable(tables),
}
}
}
@ -158,7 +158,7 @@ impl<'tcx> Stable<'tcx> for ty::FieldDef {
impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> {
type T = stable_mir::ty::GenericArgs;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect())
GenericArgs(self.iter().map(|arg| arg.kind().stable(tables)).collect())
}
}
@ -604,8 +604,8 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> {
PredicateKind::NormalizesTo(_pred) => unimplemented!(),
PredicateKind::AliasRelate(a, b, alias_relation_direction) => {
stable_mir::ty::PredicateKind::AliasRelate(
a.unpack().stable(tables),
b.unpack().stable(tables),
a.kind().stable(tables),
b.kind().stable(tables),
alias_relation_direction.stable(tables),
)
}
@ -640,7 +640,7 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
ty.stable(tables),
),
ClauseKind::WellFormed(term) => {
stable_mir::ty::ClauseKind::WellFormed(term.unpack().stable(tables))
stable_mir::ty::ClauseKind::WellFormed(term.kind().stable(tables))
}
ClauseKind::ConstEvaluatable(const_) => {
stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables))
@ -726,7 +726,7 @@ impl<'tcx> Stable<'tcx> for ty::ProjectionPredicate<'tcx> {
let ty::ProjectionPredicate { projection_term, term } = self;
stable_mir::ty::ProjectionPredicate {
projection_term: projection_term.stable(tables),
term: term.unpack().stable(tables),
term: term.kind().stable(tables),
}
}
}

View file

@ -465,6 +465,7 @@ symbols! {
as_ref,
as_str,
asm,
asm_cfg,
asm_const,
asm_experimental_arch,
asm_experimental_reg,

View file

@ -147,7 +147,7 @@ impl<'tcx> AbiHashStable<'tcx> for ty::FnSig<'tcx> {
impl<'tcx> AbiHashStable<'tcx> for ty::GenericArg<'tcx> {
fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) {
self.unpack().abi_hash(tcx, hasher);
self.kind().abi_hash(tcx, hasher);
}
}

View file

@ -386,7 +386,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
print_prefix(self)?;
let args =
args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_)));
if args.clone().next().is_some() {
self.generic_delimiters(|cx| cx.comma_sep(args))

View file

@ -646,7 +646,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
let name = cx.tcx.associated_item(projection.def_id).name();
cx.push("p");
cx.push_ident(name.as_str());
match projection.term.unpack() {
match projection.term.kind() {
ty::TermKind::Ty(ty) => ty.print(cx),
ty::TermKind::Const(c) => c.print(cx),
}?;
@ -912,11 +912,11 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
args: &[GenericArg<'tcx>],
) -> Result<(), PrintError> {
// Don't print any regions if they're all erased.
let print_regions = args.iter().any(|arg| match arg.unpack() {
let print_regions = args.iter().any(|arg| match arg.kind() {
GenericArgKind::Lifetime(r) => !r.is_erased(),
_ => false,
});
let args = args.iter().cloned().filter(|arg| match arg.unpack() {
let args = args.iter().cloned().filter(|arg| match arg.kind() {
GenericArgKind::Lifetime(_) => print_regions,
_ => true,
});
@ -928,7 +928,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
self.push("I");
print_prefix(self)?;
for arg in args {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(lt) => {
lt.print(self)?;
}

View file

@ -738,7 +738,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
value.push_normal(", ");
}
match arg.unpack() {
match arg.kind() {
ty::GenericArgKind::Lifetime(lt) => {
let s = lt.to_string();
value.push_normal(if s.is_empty() { "'_" } else { &s });
@ -1166,7 +1166,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) {
self.push_comma(&mut values.0, &mut values.1, i);
match arg1.unpack() {
match arg1.kind() {
// At one point we'd like to elide all lifetimes here, they are
// irrelevant for all diagnostics that use this output.
//
@ -1509,7 +1509,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
let (is_simple_error, exp_found) = match values {
ValuePairs::Terms(ExpectedFound { expected, found }) => {
match (expected.unpack(), found.unpack()) {
match (expected.kind(), found.kind()) {
(ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
let is_simple_err =
expected.is_simple_text() && found.is_simple_text();
@ -2156,7 +2156,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return None;
}
Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) {
Some(match (exp_found.expected.kind(), exp_found.found.kind()) {
(ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
let (mut exp, mut fnd) = self.cmp(expected, found);
// Use the terminal width as the basis to determine when to compress the printed

View file

@ -215,7 +215,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
// `_` because then we'd end up with `Vec<_, _>`, instead of
// `Vec<_>`.
arg
} else if let GenericArgKind::Type(_) = arg.unpack() {
} else if let GenericArgKind::Type(_) = arg.kind() {
// We don't replace lifetime or const params, only type params.
self.new_infer().into()
} else {
@ -347,7 +347,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
highlight: ty::print::RegionHighlightMode<'tcx>,
) -> InferenceDiagnosticsData {
let tcx = self.tcx;
match term.unpack() {
match term.kind() {
TermKind::Ty(ty) => {
if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
let var_origin = self.infcx.type_var_origin(ty_vid);
@ -568,7 +568,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return arg;
}
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(),
GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
@ -803,7 +803,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
}
impl<'tcx> CostCtxt<'tcx> {
fn arg_cost(self, arg: GenericArg<'tcx>) -> usize {
match arg.unpack() {
match arg.kind() {
GenericArgKind::Lifetime(_) => 0, // erased
GenericArgKind::Type(ty) => self.ty_cost(ty),
GenericArgKind::Const(_) => 3, // some non-zero value
@ -898,7 +898,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
return true;
}
match (arg.unpack(), self.target.unpack()) {
match (arg.kind(), self.target.kind()) {
(GenericArgKind::Type(inner_ty), TermKind::Ty(target_ty)) => {
use ty::{Infer, TyVar};
match (inner_ty.kind(), target_ty.kind()) {
@ -929,7 +929,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
if self.generic_arg_is_target(inner) {
return true;
}
match inner.unpack() {
match inner.kind() {
GenericArgKind::Lifetime(_) => {}
GenericArgKind::Type(ty) => {
if matches!(

View file

@ -664,8 +664,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let Some(found) = exp_found.found.args.get(1) else {
return;
};
let expected = expected.unpack();
let found = found.unpack();
let expected = expected.kind();
let found = found.kind();
// 3. Extract the tuple type from Fn trait and suggest the change.
if let GenericArgKind::Type(expected) = expected
&& let GenericArgKind::Type(found) = found

View file

@ -2446,7 +2446,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
if let ty::Adt(def, args) = self_ty.kind()
&& let [arg] = &args[..]
&& let ty::GenericArgKind::Type(ty) = arg.unpack()
&& let ty::GenericArgKind::Type(ty) = arg.kind()
&& let ty::Adt(inner_def, _) = ty.kind()
&& inner_def == def
{

View file

@ -81,7 +81,7 @@ pub fn check_opaque_type_parameter_valid<'tcx>(
}
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
let arg_is_param = match arg.unpack() {
let arg_is_param = match arg.kind() {
GenericArgKind::Lifetime(lt) => match defining_scope_kind {
DefiningScopeKind::HirTypeck => continue,
DefiningScopeKind::MirBorrowck => {

View file

@ -108,7 +108,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
arg: ty::GenericArg<'tcx>,
span: Span,
) -> ty::GenericArg<'tcx> {
match arg.unpack() {
match arg.kind() {
ty::GenericArgKind::Lifetime(_) => {
self.next_region_var(RegionVariableOrigin::MiscVariable(span)).into()
}

View file

@ -207,7 +207,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
let infcx = self.goal.infcx;
match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.unpack() {
let unconstrained_term = match term.kind() {
ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};

View file

@ -1784,7 +1784,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if !generics.is_own_empty()
&& obligation.predicate.args[generics.parent_count..].iter().any(|&p| {
p.has_non_region_infer()
&& match p.unpack() {
&& match p.kind() {
ty::GenericArgKind::Const(ct) => {
self.infcx.shallow_resolve_const(ct) != ct
}

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