Merge from rustc
This commit is contained in:
commit
15f0fb03bb
330 changed files with 3717 additions and 2941 deletions
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
28
Cargo.lock
28
Cargo.lock
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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())),
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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| ®ion.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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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, .. },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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:?}");
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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(_) => {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()]);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
|
|||
278
compiler/rustc_lint/src/transmute.rs
Normal file
278
compiler/rustc_lint/src/transmute.rs
Normal 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;
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 (¶m.kind, arg.unpack()) {
|
||||
match (¶m.kind, arg.kind()) {
|
||||
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
|
||||
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
|
||||
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
385
compiler/rustc_parse/src/parser/asm.rs
Normal file
385
compiler/rustc_parse/src/parser/asm.rs
Normal 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)
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod asm;
|
||||
pub mod attr;
|
||||
mod attr_wrapper;
|
||||
mod diagnostics;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@ symbols! {
|
|||
as_ref,
|
||||
as_str,
|
||||
asm,
|
||||
asm_cfg,
|
||||
asm_const,
|
||||
asm_experimental_arch,
|
||||
asm_experimental_reg,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue