Rollup merge of #115947 - GuillaumeGomez:custom_code_classes_in_docs-warning, r=notriddle
Custom code classes in docs warning
Fixes https://github.com/rust-lang/rust/issues/115938.
This PR does two things:
1. Unless the `custom_code_classes_in_docs` feature is enabled, it will use the old codeblock tag parser.
2. If there is a codeblock tag that starts with a `.`, it will emit a behaviour change warning.
Hopefully this is the last missing part for this feature until stabilization.
Follow-up of https://github.com/rust-lang/rust/pull/110800.
r? `@notriddle`
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3680136..618a269 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -67,7 +67,7 @@
- name: disable git crlf conversion
run: git config --global core.autocrlf false
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
- name: configure the PR in which the error message will be posted
@@ -393,7 +393,7 @@
- name: dist-x86_64-msvc
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler"
- SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist python x.py dist bootstrap --include-default-paths
+ SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-2019-8core-32gb
- name: dist-i686-msvc
@@ -435,7 +435,7 @@
- name: disable git crlf conversion
run: git config --global core.autocrlf false
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
- name: configure the PR in which the error message will be posted
@@ -555,7 +555,7 @@
- name: disable git crlf conversion
run: git config --global core.autocrlf false
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
- name: configure the PR in which the error message will be posted
@@ -662,7 +662,7 @@
if: "github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'"
steps:
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
- name: publish toolstate
diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml
index 26d2ba6..97ed891 100644
--- a/.github/workflows/dependencies.yml
+++ b/.github/workflows/dependencies.yml
@@ -50,7 +50,7 @@
runs-on: ubuntu-latest
steps:
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
submodules: recursive
- name: install the bootstrap toolchain
@@ -87,7 +87,7 @@
pull-requests: write
steps:
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: download Cargo.lock from update job
uses: actions/download-artifact@v3
diff --git a/.mailmap b/.mailmap
index eb82cf4..21e1adb 100644
--- a/.mailmap
+++ b/.mailmap
@@ -328,7 +328,8 @@
Kyle J Strand <batmanaod@gmail.com> <kyle.j.strand@gmail.com>
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@pieinsurance.com>
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@rms.com>
-Laurențiu Nicola <lnicola@dend.ro>
+Laurențiu Nicola <lnicola@dend.ro> Laurentiu Nicola <lnicola@dend.ro>
+Laurențiu Nicola <lnicola@dend.ro> <lnicola@users.noreply.github.com>
lcnr <rust@lcnr.de> <bastian_kauschke@hotmail.de>
Lee Jeffery <leejeffery@gmail.com> Lee Jeffery <lee@leejeffery.co.uk>
Lee Wondong <wdlee91@gmail.com>
@@ -549,6 +550,8 @@
Tomas Koutsky <tomas@stepnivlk.net>
Torsten Weber <TorstenWeber12@gmail.com>
Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
+Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
+Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
Tshepang Mbambo <tshepang@gmail.com>
Ty Overby <ty@pre-alpha.com>
diff --git a/Cargo.lock b/Cargo.lock
index 386b57e..95ac313 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -644,9 +644,9 @@
[[package]]
name = "compiler_builtins"
-version = "0.1.100"
+version = "0.1.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c0f24437059853f0fa64afc51f338f93647a3de4cf3358ba1bb4171a199775"
+checksum = "01a6d58e9c3408138099a396a98fd0d0e6cfb25d723594d2ae48b5004513fd5b"
dependencies = [
"cc",
"rustc-std-workspace-core",
@@ -662,6 +662,7 @@
"diff",
"getopts",
"glob",
+ "home",
"lazycell",
"libc",
"miow",
@@ -860,12 +861,36 @@
[[package]]
name = "darling"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+dependencies = [
+ "darling_core 0.14.4",
+ "darling_macro 0.14.4",
+]
+
+[[package]]
+name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
- "darling_core",
- "darling_macro",
+ "darling_core 0.20.3",
+ "darling_macro 0.20.3",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 1.0.109",
]
[[package]]
@@ -884,11 +909,22 @@
[[package]]
name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core 0.14.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
- "darling_core",
+ "darling_core 0.20.3",
"quote",
"syn 2.0.29",
]
@@ -920,6 +956,37 @@
]
[[package]]
+name = "derive_builder"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
+dependencies = [
+ "darling 0.14.4",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
+dependencies = [
+ "derive_builder_core",
+ "syn 1.0.109",
+]
+
+[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -938,7 +1005,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
dependencies = [
- "darling",
+ "darling 0.20.3",
"proc-macro2",
"quote",
"syn 2.0.29",
@@ -1598,6 +1665,15 @@
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "html-checker"
version = "0.1.0"
dependencies = [
@@ -2066,9 +2142,9 @@
[[package]]
name = "libc"
-version = "0.2.147"
+version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
dependencies = [
"rustc-std-workspace-core",
]
@@ -2584,6 +2660,8 @@
"anyhow",
"build_helper",
"camino",
+ "clap",
+ "derive_builder",
"env_logger 0.10.0",
"fs_extra",
"glob",
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 745358f..a000e48 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -18,6 +18,20 @@
is_const: bool,
) {
let span = cx.with_def_site_ctxt(span);
+
+ let structural_trait_def = TraitDef {
+ span,
+ path: path_std!(marker::StructuralEq),
+ skip_path_as_bound: true, // crucial!
+ needs_copy_as_bound_if_packed: false,
+ additional_bounds: Vec::new(),
+ supports_unions: true,
+ methods: Vec::new(),
+ associated_types: Vec::new(),
+ is_const: false,
+ };
+ structural_trait_def.expand(cx, mitem, item, push);
+
let trait_def = TraitDef {
span,
path: path_std!(cmp::Eq),
@@ -44,9 +58,6 @@
associated_types: Vec::new(),
is_const,
};
-
- super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push);
-
trait_def.expand_ext(cx, mitem, item, push, true)
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index a71ecc5..a170468 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -72,13 +72,20 @@
BlockOrExpr::new_expr(expr)
}
- super::inject_impl_of_structural_trait(
- cx,
+ let structural_trait_def = TraitDef {
span,
- item,
- path_std!(marker::StructuralPartialEq),
- push,
- );
+ path: path_std!(marker::StructuralPartialEq),
+ skip_path_as_bound: true, // crucial!
+ needs_copy_as_bound_if_packed: false,
+ additional_bounds: Vec::new(),
+ // We really don't support unions, but that's already checked by the impl generated below;
+ // a second check here would lead to redundant error messages.
+ supports_unions: true,
+ methods: Vec::new(),
+ associated_types: Vec::new(),
+ is_const: false,
+ };
+ structural_trait_def.expand(cx, mitem, item, push);
// No need to generate `ne`, the default suffices, and not generating it is
// faster.
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index edc6f9f..7252658 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -711,7 +711,9 @@
.collect();
// Require the current trait.
- bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
+ if !self.skip_path_as_bound {
+ bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
+ }
// Add a `Copy` bound if required.
if is_packed && self.needs_copy_as_bound_if_packed {
@@ -722,15 +724,17 @@
));
}
- let predicate = ast::WhereBoundPredicate {
- span: self.span,
- bound_generic_params: field_ty_param.bound_generic_params,
- bounded_ty: field_ty_param.ty,
- bounds,
- };
+ if !bounds.is_empty() {
+ let predicate = ast::WhereBoundPredicate {
+ span: self.span,
+ bound_generic_params: field_ty_param.bound_generic_params,
+ bounded_ty: field_ty_param.ty,
+ bounds,
+ };
- let predicate = ast::WherePredicate::BoundPredicate(predicate);
- where_clause.predicates.push(predicate);
+ let predicate = ast::WherePredicate::BoundPredicate(predicate);
+ where_clause.predicates.push(predicate);
+ }
}
}
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index d34336e..a6f3252 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -2,9 +2,9 @@
use rustc_ast as ast;
use rustc_ast::ptr::P;
-use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem};
+use rustc_ast::{GenericArg, MetaItem};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
-use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use thin_vec::{thin_vec, ThinVec};
@@ -116,100 +116,6 @@
}))
}
-// Injects `impl<...> Structural for ItemType<...> { }`. In particular,
-// does *not* add `where T: Structural` for parameters `T` in `...`.
-// (That's the main reason we cannot use TraitDef here.)
-fn inject_impl_of_structural_trait(
- cx: &mut ExtCtxt<'_>,
- span: Span,
- item: &Annotatable,
- structural_path: generic::ty::Path,
- push: &mut dyn FnMut(Annotatable),
-) {
- let Annotatable::Item(item) = item else {
- unreachable!();
- };
-
- let generics = match &item.kind {
- ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics,
- // Do not inject `impl Structural for Union`. (`PartialEq` does not
- // support unions, so we will see error downstream.)
- ItemKind::Union(..) => return,
- _ => unreachable!(),
- };
-
- // Create generics param list for where clauses and impl headers
- let mut generics = generics.clone();
-
- let ctxt = span.ctxt();
-
- // Create the type of `self`.
- //
- // in addition, remove defaults from generic params (impls cannot have them).
- let self_params: Vec<_> = generics
- .params
- .iter_mut()
- .map(|param| match &mut param.kind {
- ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
- cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident),
- ),
- ast::GenericParamKind::Type { default } => {
- *default = None;
- ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
- }
- ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
- *default = None;
- ast::GenericArg::Const(
- cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident),
- )
- }
- })
- .collect();
-
- let type_ident = item.ident;
-
- let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics));
- let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params));
-
- // It would be nice to also encode constraint `where Self: Eq` (by adding it
- // onto `generics` cloned above). Unfortunately, that strategy runs afoul of
- // rust-lang/rust#48214. So we perform that additional check in the compiler
- // itself, instead of encoding it here.
-
- // Keep the lint and stability attributes of the original item, to control
- // how the generated implementation is linted.
- let mut attrs = ast::AttrVec::new();
- attrs.extend(
- item.attrs
- .iter()
- .filter(|a| {
- [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
- .contains(&a.name_or_empty())
- })
- .cloned(),
- );
- // Mark as `automatically_derived` to avoid some silly lints.
- attrs.push(cx.attr_word(sym::automatically_derived, span));
-
- let newitem = cx.item(
- span,
- Ident::empty(),
- attrs,
- ItemKind::Impl(Box::new(Impl {
- unsafety: ast::Unsafe::No,
- polarity: ast::ImplPolarity::Positive,
- defaultness: ast::Defaultness::Final,
- constness: ast::Const::No,
- generics,
- of_trait: Some(trait_ref),
- self_ty: self_type,
- items: ThinVec::new(),
- })),
- );
-
- push(Annotatable::Item(newitem));
-}
-
fn assert_ty_bounds(
cx: &mut ExtCtxt<'_>,
stmts: &mut ThinVec<ast::Stmt>,
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 54f82dc..9b5a6b8 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -250,7 +250,10 @@
}
fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
- if !crate::constant::check_constants(fx) {
+ if let Err(err) =
+ fx.mir.post_mono_checks(fx.tcx, ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c)))
+ {
+ err.emit_err(fx.tcx);
fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]);
fx.bcx.switch_to_block(fx.block_map[START_BLOCK]);
// compilation should have been aborted
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 8c67760..0246868 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -2,9 +2,8 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::mir::interpret::{
- read_target_uint, AllocId, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
-};
+use rustc_middle::mir::interpret::{read_target_uint, AllocId, GlobalAlloc, Scalar};
+use rustc_middle::mir::ConstValue;
use cranelift_module::*;
@@ -33,16 +32,6 @@
}
}
-pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
- let mut all_constants_ok = true;
- for constant in &fx.mir.required_consts {
- if eval_mir_constant(fx, constant).is_none() {
- all_constants_ok = false;
- }
- }
- all_constants_ok
-}
-
pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) {
let mut constants_cx = ConstantCx::new();
constants_cx.todo.push(TodoItem::Static(def_id));
@@ -76,30 +65,20 @@
pub(crate) fn eval_mir_constant<'tcx>(
fx: &FunctionCx<'_, '_, 'tcx>,
constant: &Constant<'tcx>,
-) -> Option<(ConstValue<'tcx>, Ty<'tcx>)> {
+) -> (ConstValue<'tcx>, Ty<'tcx>) {
let cv = fx.monomorphize(constant.literal);
+ // This cannot fail because we checked all required_consts in advance.
let val = cv
.eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span))
- .map_err(|err| match err {
- ErrorHandled::Reported(_) => {
- fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
- }
- ErrorHandled::TooGeneric => {
- span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err);
- }
- })
- .ok();
- val.map(|val| (val, cv.ty()))
+ .expect("erroneous constant not captured by required_consts");
+ (val, cv.ty())
}
pub(crate) fn codegen_constant_operand<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
constant: &Constant<'tcx>,
) -> CValue<'tcx> {
- let (const_val, ty) = eval_mir_constant(fx, constant).unwrap_or_else(|| {
- span_bug!(constant.span, "erroneous constant not captured by required_consts")
- });
-
+ let (const_val, ty) = eval_mir_constant(fx, constant);
codegen_const_value(fx, const_val, ty)
}
@@ -459,7 +438,7 @@
operand: &Operand<'tcx>,
) -> Option<ConstValue<'tcx>> {
match operand {
- Operand::Constant(const_) => Some(eval_mir_constant(fx, const_).unwrap().0),
+ Operand::Constant(const_) => Some(eval_mir_constant(fx, const_).0),
// FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored
// inside a temporary before being passed to the intrinsic requiring the const argument.
// This code tries to find a single constant defining definition of the referenced local.
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index 518e3da..eba9094 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -242,8 +242,7 @@
}
}
InlineAsmOperand::Const { ref value } => {
- let (const_value, ty) = crate::constant::eval_mir_constant(fx, value)
- .unwrap_or_else(|| span_bug!(span, "asm const cannot be resolved"));
+ let (const_value, ty) = crate::constant::eval_mir_constant(fx, value);
let value = rustc_codegen_ssa::common::asm_const_to_str(
fx.tcx,
span,
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 9ce13ff..d0a0785 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -19,8 +19,6 @@
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
-codegen_ssa_erroneous_constant = erroneous constant encountered
-
codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
codegen_ssa_expected_coverage_symbol = expected `coverage(off)` or `coverage(on)`
@@ -174,8 +172,6 @@
codegen_ssa_option_gcc_only = option `-Z gcc-ld` is used even though linker flavor is not gcc
-codegen_ssa_polymorphic_constant_too_generic = codegen encountered polymorphic constant: TooGeneric
-
codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status}
.note = {$output}
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index 5a68075..641ac3e 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -1,7 +1,7 @@
#![allow(non_camel_case_types)]
use rustc_hir::LangItem;
-use rustc_middle::mir::interpret::ConstValue;
+use rustc_middle::mir;
use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
use rustc_span::Span;
@@ -194,10 +194,10 @@
pub fn asm_const_to_str<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
- const_value: ConstValue<'tcx>,
+ const_value: mir::ConstValue<'tcx>,
ty_and_layout: TyAndLayout<'tcx>,
) -> String {
- let ConstValue::Scalar(scalar) = const_value else {
+ let mir::ConstValue::Scalar(scalar) = const_value else {
span_bug!(sp, "expected Scalar for promoted asm const, but got {:#?}", const_value)
};
let value = scalar.assert_bits(ty_and_layout.size);
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index fa49095..bfd572a 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -596,20 +596,6 @@
}
#[derive(Diagnostic)]
-#[diag(codegen_ssa_erroneous_constant)]
-pub struct ErroneousConstant {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_ssa_polymorphic_constant_too_generic)]
-pub struct PolymorphicConstantTooGeneric {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(Diagnostic)]
#[diag(codegen_ssa_shuffle_indices_evaluation)]
pub struct ShuffleIndicesEvaluation {
#[primary_span]
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 464278f..b738a49 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1088,9 +1088,7 @@
InlineAsmOperandRef::InOut { reg, late, in_value, out_place }
}
mir::InlineAsmOperand::Const { ref value } => {
- let const_value = self
- .eval_mir_constant(value)
- .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved"));
+ let const_value = self.eval_mir_constant(value);
let string = common::asm_const_to_str(
bx.tcx(),
span,
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 4d7bd60..7fec404 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -2,7 +2,7 @@
use crate::mir::operand::OperandRef;
use crate::traits::*;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
+use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, Ty};
use rustc_target::abi::Abi;
@@ -14,34 +14,16 @@
&self,
bx: &mut Bx,
constant: &mir::Constant<'tcx>,
- ) -> Result<OperandRef<'tcx, Bx::Value>, ErrorHandled> {
- let val = self.eval_mir_constant(constant)?;
+ ) -> OperandRef<'tcx, Bx::Value> {
+ let val = self.eval_mir_constant(constant);
let ty = self.monomorphize(constant.ty());
- Ok(OperandRef::from_const(bx, val, ty))
+ OperandRef::from_const(bx, val, ty)
}
- pub fn eval_mir_constant(
- &self,
- constant: &mir::Constant<'tcx>,
- ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+ pub fn eval_mir_constant(&self, constant: &mir::Constant<'tcx>) -> mir::ConstValue<'tcx> {
self.monomorphize(constant.literal)
.eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span))
- .map_err(|err| {
- match err {
- ErrorHandled::Reported(_) => {
- self.cx
- .tcx()
- .sess
- .emit_err(errors::ErroneousConstant { span: constant.span });
- }
- ErrorHandled::TooGeneric => {
- self.cx.tcx().sess.diagnostic().emit_bug(
- errors::PolymorphicConstantTooGeneric { span: constant.span },
- );
- }
- }
- err
- })
+ .expect("erroneous constant not captured by required_consts")
}
/// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index ac705a5..d156011 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -579,23 +579,12 @@
if let Some(dbg_var) = dbg_var {
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
- if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
- self.set_debug_loc(bx, var.source_info);
- let base = Self::spill_operand_to_stack(
- operand,
- Some(var.name.to_string()),
- bx,
- );
+ let operand = self.eval_mir_constant_to_operand(bx, &c);
+ self.set_debug_loc(bx, var.source_info);
+ let base =
+ Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx);
- bx.dbg_var_addr(
- dbg_var,
- dbg_loc,
- base.llval,
- Size::ZERO,
- &[],
- fragment,
- );
- }
+ bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], fragment);
}
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 8445de6..1e905a7 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -3,7 +3,6 @@
use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::traversal;
use rustc_middle::mir::UnwindTerminateReason;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
@@ -212,23 +211,14 @@
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx);
- // Evaluate all required consts; codegen later assumes that CTFE will never fail.
- let mut all_consts_ok = true;
- for const_ in &mir.required_consts {
- if let Err(err) = fx.eval_mir_constant(const_) {
- all_consts_ok = false;
- match err {
- // errored or at least linted
- ErrorHandled::Reported(_) => {}
- ErrorHandled::TooGeneric => {
- span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err)
- }
- }
- }
- }
- if !all_consts_ok {
- // We leave the IR in some half-built state here, and rely on this code not even being
- // submitted to LLVM once an error was raised.
+ // Rust post-monomorphization checks; we later rely on them.
+ if let Err(err) =
+ mir.post_mono_checks(cx.tcx(), ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c)))
+ {
+ err.emit_err(cx.tcx());
+ // This IR shouldn't ever be emitted, but let's try to guard against any of this code
+ // ever running.
+ start_bx.abort();
return;
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 1926bb8..926a0be 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -6,8 +6,8 @@
use crate::traits::*;
use crate::MemFlags;
-use rustc_middle::mir;
-use rustc_middle::mir::interpret::{alloc_range, ConstValue, Pointer, Scalar};
+use rustc_middle::mir::interpret::{alloc_range, Pointer, Scalar};
+use rustc_middle::mir::{self, ConstValue};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, Align, Size};
@@ -86,7 +86,7 @@
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
- val: ConstValue<'tcx>,
+ val: mir::ConstValue<'tcx>,
ty: Ty<'tcx>,
) -> Self {
let layout = bx.layout_of(ty);
@@ -575,12 +575,7 @@
self.codegen_consume(bx, place.as_ref())
}
- mir::Operand::Constant(ref constant) => {
- // This cannot fail because we checked all required_consts in advance.
- self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|_err| {
- span_bug!(constant.span, "erroneous constant not captured by required_consts")
- })
- }
+ mir::Operand::Constant(ref constant) => self.eval_mir_constant_to_operand(bx, constant),
}
}
}
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 020402f..d23e2a9 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -83,9 +83,6 @@
const_eval_dyn_star_call_vtable_mismatch =
`dyn*` call on a pointer whose vtable does not match its type
-const_eval_erroneous_constant =
- erroneous constant used
-
const_eval_error = {$error_kind ->
[static] could not evaluate static initializer
[const] evaluation of constant value failed
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 2d20d55..bf1e0a3 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -4,7 +4,7 @@
use rustc_middle::mir::AssertKind;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{layout::LayoutError, ConstInt};
-use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_span::{ErrorGuaranteed, Span, Symbol, DUMMY_SP};
use super::InterpCx;
use crate::errors::{self, FrameNote, ReportErrorExt};
@@ -134,11 +134,11 @@
// Don't emit a new diagnostic for these errors, they are already reported elsewhere or
// should remain silent.
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
- ErrorHandled::TooGeneric
+ ErrorHandled::TooGeneric(span.unwrap_or(DUMMY_SP))
}
- err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar),
+ err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span.unwrap_or(DUMMY_SP)),
err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
- ErrorHandled::Reported(guar.into())
+ ErrorHandled::Reported(guar.into(), span.unwrap_or(DUMMY_SP))
}
// Report remaining errors.
_ => {
@@ -152,7 +152,7 @@
// Use *our* span to label the interp error
err.span_label(our_span, msg);
- ErrorHandled::Reported(err.emit().into())
+ ErrorHandled::Reported(err.emit().into(), span)
}
}
}
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 454baf2..de78b4e 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -4,9 +4,9 @@
use either::{Left, Right};
use rustc_hir::def::DefKind;
-use rustc_middle::mir;
use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
use rustc_middle::mir::pretty::write_allocation_bytes;
+use rustc_middle::mir::{self, ConstAlloc, ConstValue};
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -18,9 +18,8 @@
use crate::errors;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
- intern_const_alloc_recursive, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, Immediate,
- InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
- StackPopCleanup,
+ intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
+ InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup,
};
// Returns a pointer to where the result lives
@@ -212,7 +211,7 @@
key.param_env = key.param_env.with_user_facing();
match tcx.eval_to_const_value_raw(key) {
// try again with reveal all as requested
- Err(ErrorHandled::TooGeneric) => {}
+ Err(ErrorHandled::TooGeneric(_)) => {}
// deduplicate calls
other => return other,
}
@@ -259,7 +258,7 @@
key.param_env = key.param_env.with_user_facing();
match tcx.eval_to_allocation_raw(key) {
// try again with reveal all as requested
- Err(ErrorHandled::TooGeneric) => {}
+ Err(ErrorHandled::TooGeneric(_)) => {}
// deduplicate calls
other => return other,
}
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 5327fa5..886d797 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,7 +1,7 @@
// Not in interpret to make sure we do not use private implementation details
use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, Scalar};
+use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar};
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -22,7 +22,7 @@
pub(crate) fn const_caller_location(
tcx: TyCtxt<'_>,
(file, line, col): (Symbol, u32, u32),
-) -> ConstValue<'_> {
+) -> mir::ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
@@ -30,7 +30,7 @@
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
bug!("intern_const_alloc_recursive should not error in this case")
}
- ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
+ mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
}
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
@@ -87,7 +87,7 @@
#[instrument(skip(tcx), level = "debug")]
pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
tcx: TyCtxt<'tcx>,
- val: ConstValue<'tcx>,
+ val: mir::ConstValue<'tcx>,
ty: Ty<'tcx>,
) -> Option<mir::DestructuredConstant<'tcx>> {
let param_env = ty::ParamEnv::reveal_all();
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 1675b82..2fba745 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -4,9 +4,10 @@
use crate::const_eval::CanAccessStatics;
use crate::interpret::MPlaceTy;
use crate::interpret::{
- intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
- MemoryKind, PlaceTy, Projectable, Scalar,
+ intern_const_alloc_recursive, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy,
+ Projectable, Scalar,
};
+use rustc_middle::mir;
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
@@ -206,7 +207,7 @@
tcx: TyCtxt<'tcx>,
param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
valtree: ty::ValTree<'tcx>,
-) -> ConstValue<'tcx> {
+) -> mir::ConstValue<'tcx> {
// Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
// (those for constants with type bool, int, uint, float or char).
// For all other types we create an `MPlace` and fill that by walking
@@ -219,10 +220,10 @@
match ty.kind() {
ty::FnDef(..) => {
assert!(valtree.unwrap_branch().is_empty());
- ConstValue::ZeroSized
+ mir::ConstValue::ZeroSized
}
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree {
- ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)),
+ ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)),
ty::ValTree::Branch(_) => bug!(
"ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
),
@@ -237,7 +238,7 @@
let layout = tcx.layout_of(param_env_ty).unwrap();
if layout.is_zst() {
// Fast path to avoid some allocations.
- return ConstValue::ZeroSized;
+ return mir::ConstValue::ZeroSized;
}
if layout.abi.is_scalar()
&& (matches!(ty.kind(), ty::Tuple(_))
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index c3c6cbe..b1599dd 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -239,13 +239,6 @@
pub item_span: Span,
}
-#[derive(Diagnostic)]
-#[diag(const_eval_erroneous_constant)]
-pub(crate) struct ErroneousConstUsed {
- #[primary_span]
- pub span: Span,
-}
-
#[derive(Subdiagnostic)]
#[note(const_eval_non_const_impl)]
pub(crate) struct NonConstImplNote {
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 966ce66..cb14e16 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -7,7 +7,7 @@
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ErrorHandled, InterpError, InvalidMetaKind, ReportedErrorInfo};
+use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
@@ -21,10 +21,10 @@
use super::{
AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
- MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer, PointerArithmetic,
+ MemPlaceMeta, Memory, MemoryKind, OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic,
Projectable, Provenance, Scalar, StackPopJump,
};
-use crate::errors::{self, ErroneousConstUsed};
+use crate::errors;
use crate::util;
use crate::{fluent_generated as fluent, ReportErrorExt};
@@ -556,7 +556,7 @@
>(
&self,
value: T,
- ) -> Result<T, InterpError<'tcx>> {
+ ) -> Result<T, ErrorHandled> {
self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
}
@@ -566,7 +566,7 @@
&self,
frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>,
value: T,
- ) -> Result<T, InterpError<'tcx>> {
+ ) -> Result<T, ErrorHandled> {
frame
.instance
.try_subst_mir_and_normalize_erasing_regions(
@@ -574,7 +574,7 @@
self.param_env,
ty::EarlyBinder::bind(value),
)
- .map_err(|_| err_inval!(TooGeneric))
+ .map_err(|_| ErrorHandled::TooGeneric(self.cur_span()))
}
/// The `args` are assumed to already be in our interpreter "universe" (param_env).
@@ -750,11 +750,12 @@
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
if M::POST_MONO_CHECKS {
- for ct in &body.required_consts {
- let span = ct.span;
- let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?;
- self.eval_mir_constant(&ct, Some(span), None)?;
- }
+ // `ctfe_query` does some error message decoration that we want to be in effect here.
+ self.ctfe_query(None, |tcx| {
+ body.post_mono_checks(*tcx, self.param_env, |c| {
+ self.subst_from_current_frame_and_normalize_erasing_regions(c)
+ })
+ })?;
}
// done
@@ -1059,28 +1060,19 @@
&self,
span: Option<Span>,
query: impl FnOnce(TyCtxtAt<'tcx>) -> Result<T, ErrorHandled>,
- ) -> InterpResult<'tcx, T> {
+ ) -> Result<T, ErrorHandled> {
// Use a precise span for better cycle errors.
query(self.tcx.at(span.unwrap_or_else(|| self.cur_span()))).map_err(|err| {
- match err {
- ErrorHandled::Reported(err) => {
- if !err.is_tainted_by_errors() && let Some(span) = span {
- // To make it easier to figure out where this error comes from, also add a note at the current location.
- self.tcx.sess.emit_note(ErroneousConstUsed { span });
- }
- err_inval!(AlreadyReported(err))
- }
- ErrorHandled::TooGeneric => err_inval!(TooGeneric),
- }
- .into()
+ err.emit_note(*self.tcx);
+ err
})
}
pub fn eval_global(
&self,
- gid: GlobalId<'tcx>,
- span: Option<Span>,
+ instance: ty::Instance<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+ let gid = GlobalId { instance, promoted: None };
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
// and thus don't care about the parameter environment. While we could just use
// `self.param_env`, that would mean we invoke the query to evaluate the static
@@ -1091,10 +1083,20 @@
} else {
self.param_env
};
- let val = self.ctfe_query(span, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
+ let val = self.ctfe_query(None, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
self.raw_const_to_mplace(val)
}
+ pub fn eval_mir_constant(
+ &self,
+ val: &mir::ConstantKind<'tcx>,
+ span: Option<Span>,
+ layout: Option<TyAndLayout<'tcx>>,
+ ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
+ let const_val = self.ctfe_query(span, |tcx| val.eval(*tcx, self.param_env, span))?;
+ self.const_val_to_op(const_val, val.ty(), layout)
+ }
+
#[must_use]
pub fn dump_place(
&self,
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 3b58f66..f08f143 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -5,10 +5,8 @@
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
self,
- interpret::{
- Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
- },
- BinOp, NonDivergingIntrinsic,
+ interpret::{Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar},
+ BinOp, ConstValue, NonDivergingIntrinsic,
};
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 3d42fda..788b50d 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -8,15 +8,13 @@
use rustc_hir::def::Namespace;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
-use rustc_middle::ty::{ConstInt, Ty, ValTree};
+use rustc_middle::ty::{ConstInt, Ty};
use rustc_middle::{mir, ty};
-use rustc_span::Span;
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
use super::{
- alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
- InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer,
- Projectable, Provenance, Scalar,
+ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
+ MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable, Provenance, Scalar,
};
/// An `Immediate` represents a single immediate self-contained Rust value.
@@ -701,57 +699,9 @@
Ok(op)
}
- fn eval_ty_constant(
- &self,
- val: ty::Const<'tcx>,
- span: Option<Span>,
- ) -> InterpResult<'tcx, ValTree<'tcx>> {
- Ok(match val.kind() {
- ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
- throw_inval!(TooGeneric)
- }
- // FIXME(generic_const_exprs): `ConstKind::Expr` should be able to be evaluated
- ty::ConstKind::Expr(_) => throw_inval!(TooGeneric),
- ty::ConstKind::Error(reported) => {
- throw_inval!(AlreadyReported(reported.into()))
- }
- ty::ConstKind::Unevaluated(uv) => {
- let instance = self.resolve(uv.def, uv.args)?;
- let cid = GlobalId { instance, promoted: None };
- self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))?
- .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
- }
- ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
- span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}")
- }
- ty::ConstKind::Value(valtree) => valtree,
- })
- }
-
- pub fn eval_mir_constant(
- &self,
- val: &mir::ConstantKind<'tcx>,
- span: Option<Span>,
- layout: Option<TyAndLayout<'tcx>>,
- ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
- match *val {
- mir::ConstantKind::Ty(ct) => {
- let ty = ct.ty();
- let valtree = self.eval_ty_constant(ct, span)?;
- let const_val = self.tcx.valtree_to_const_val((ty, valtree));
- self.const_val_to_op(const_val, ty, layout)
- }
- mir::ConstantKind::Val(val, ty) => self.const_val_to_op(val, ty, layout),
- mir::ConstantKind::Unevaluated(uv, _) => {
- let instance = self.resolve(uv.def, uv.args)?;
- Ok(self.eval_global(GlobalId { instance, promoted: uv.promoted }, span)?.into())
- }
- }
- }
-
pub(crate) fn const_val_to_op(
&self,
- val_val: ConstValue<'tcx>,
+ val_val: mir::ConstValue<'tcx>,
ty: Ty<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
@@ -764,15 +714,15 @@
};
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let op = match val_val {
- ConstValue::Indirect { alloc_id, offset } => {
+ mir::ConstValue::Indirect { alloc_id, offset } => {
// We rely on mutability being set correctly in that allocation to prevent writes
// where none should happen.
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
}
- ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
- ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
- ConstValue::Slice { data, start, end } => {
+ mir::ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
+ mir::ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
+ mir::ConstValue::Slice { data, start, end } => {
// We rely on mutability being set correctly in `data` to prevent writes
// where none should happen.
let ptr = Pointer::new(
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 0f66df5..fb9aa9d 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -9,16 +9,15 @@
use rustc_ast::Mutability;
use rustc_index::IndexSlice;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::PointerArithmetic;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::Ty;
use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
use super::{
- alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
- ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
- Pointer, Projectable, Provenance, Readable, Scalar,
+ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ImmTy,
+ Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, Pointer,
+ PointerArithmetic, Projectable, Provenance, Readable, Scalar,
};
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@@ -1037,7 +1036,7 @@
pub fn raw_const_to_mplace(
&self,
- raw: ConstAlloc<'tcx>,
+ raw: mir::ConstAlloc<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
// This must be an allocation in `tcx`
let _ = self.tcx.global_alloc(raw.alloc_id);
diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
index 85ef2de..4075481 100644
--- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
@@ -51,7 +51,7 @@
// Traverse the graph, collecting a number of things:
//
// * Preorder mapping (to it, and back to the actual ordering)
- // * Postorder mapping (used exclusively for rank_partial_cmp on the final product)
+ // * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product)
// * Parents for each vertex in the preorder tree
//
// These are all done here rather than through one of the 'standard'
@@ -342,8 +342,8 @@
/// relationship, the dominator will always precede the dominated. (The relative ordering
/// of two unrelated nodes will also be consistent, but otherwise the order has no
/// meaning.) This method cannot be used to determine if either Node dominates the other.
- pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> {
- self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs])
+ pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering {
+ self.post_order_rank[rhs].cmp(&self.post_order_rank[lhs])
}
/// Returns true if `a` dominates `b`.
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index bee5a89..7d037dd 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -47,6 +47,8 @@
#[macro_use]
extern crate rustc_macros;
+use std::fmt;
+
pub use rustc_index::static_assert_size;
#[inline(never)]
@@ -126,6 +128,23 @@
}
}
+/// Turns a closure that takes an `&mut Formatter` into something that can be display-formatted.
+pub fn make_display(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
+ struct Printer<F> {
+ f: F,
+ }
+ impl<F> fmt::Display for Printer<F>
+ where
+ F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
+ {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (self.f)(fmt)
+ }
+ }
+
+ Printer { f }
+}
+
// See comments in src/librustc_middle/lib.rs
#[doc(hidden)]
pub fn __noop_fix_for_27438() {}
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 5d3b2f4..203e529 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -169,6 +169,7 @@
.map(|line| {
// Ensure the source file is present before we try
// to load a string from it.
+ // FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks
source_map.ensure_source_file_source_present(&file);
(
format!("{}", source_map.filename_for_diagnostics(&file.name)),
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 58be74f..d322cbe 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -8,7 +8,7 @@
//! The output types are defined in `rustc_session::config::ErrorOutputType`.
use rustc_span::source_map::SourceMap;
-use rustc_span::{FileLines, SourceFile, Span};
+use rustc_span::{FileLines, FileName, SourceFile, Span};
use crate::snippet::{
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
@@ -635,6 +635,7 @@
short_message: bool,
teach: bool,
ui_testing: bool,
+ ignored_directories_in_source_blocks: Vec<String>,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
@@ -664,6 +665,7 @@
short_message: false,
teach: false,
ui_testing: false,
+ ignored_directories_in_source_blocks: Vec::new(),
diagnostic_width: None,
macro_backtrace: false,
track_diagnostics: false,
@@ -1193,7 +1195,7 @@
let will_be_emitted = |span: Span| {
!span.is_dummy() && {
let file = sm.lookup_source_file(span.hi());
- sm.ensure_source_file_source_present(&file)
+ should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file)
}
};
@@ -1388,7 +1390,11 @@
// Print out the annotate source lines that correspond with the error
for annotated_file in annotated_files {
// we can't annotate anything if the source is unavailable.
- if !sm.ensure_source_file_source_present(&annotated_file.file) {
+ if !should_show_source_code(
+ &self.ignored_directories_in_source_blocks,
+ sm,
+ &annotated_file.file,
+ ) {
if !self.short_message {
// We'll just print an unannotated message.
for (annotation_id, line) in annotated_file.lines.iter().enumerate() {
@@ -2737,3 +2743,18 @@
// bug, but be defensive against that here.
&& found != suggested
}
+
+pub(crate) fn should_show_source_code(
+ ignored_directories: &[String],
+ sm: &SourceMap,
+ file: &SourceFile,
+) -> bool {
+ if !sm.ensure_source_file_source_present(file) {
+ return false;
+ }
+
+ let FileName::Real(name) = &file.name else { return true };
+ name.local_path()
+ .map(|path| ignored_directories.iter().all(|dir| !path.starts_with(dir)))
+ .unwrap_or(true)
+}
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 38667c5..0cb75c7 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -12,7 +12,7 @@
use rustc_span::source_map::{FilePathMapping, SourceMap};
use termcolor::{ColorSpec, WriteColor};
-use crate::emitter::{Emitter, HumanReadableErrorType};
+use crate::emitter::{should_show_source_code, Emitter, HumanReadableErrorType};
use crate::registry::Registry;
use crate::translation::{to_fluent_args, Translate};
use crate::DiagnosticId;
@@ -45,6 +45,7 @@
fallback_bundle: LazyFallbackBundle,
pretty: bool,
ui_testing: bool,
+ ignored_directories_in_source_blocks: Vec<String>,
json_rendered: HumanReadableErrorType,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
@@ -73,6 +74,7 @@
fallback_bundle,
pretty,
ui_testing: false,
+ ignored_directories_in_source_blocks: Vec::new(),
json_rendered,
diagnostic_width,
macro_backtrace,
@@ -127,6 +129,7 @@
fallback_bundle,
pretty,
ui_testing: false,
+ ignored_directories_in_source_blocks: Vec::new(),
json_rendered,
diagnostic_width,
macro_backtrace,
@@ -138,6 +141,10 @@
pub fn ui_testing(self, ui_testing: bool) -> Self {
Self { ui_testing, ..self }
}
+
+ pub fn ignored_directories_in_source_blocks(self, value: Vec<String>) -> Self {
+ Self { ignored_directories_in_source_blocks: value, ..self }
+ }
}
impl Translate for JsonEmitter {
@@ -381,6 +388,7 @@
.track_diagnostics(je.track_diagnostics)
.terminal_url(je.terminal_url)
.ui_testing(je.ui_testing)
+ .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone())
.emit_diagnostic(diag);
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
let output = String::from_utf8(output).unwrap();
@@ -558,7 +566,11 @@
.span_to_lines(span)
.map(|lines| {
// We can't get any lines if the source is unavailable.
- if !je.sm.ensure_source_file_source_present(&lines.file) {
+ if !should_show_source_code(
+ &je.ignored_directories_in_source_blocks,
+ &je.sm,
+ &lines.file,
+ ) {
return vec![];
}
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 1d38117..1382626 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1201,25 +1201,35 @@
);
return;
}
- for (span, _trivial, non_exhaustive) in field_infos {
- if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
- tcx.struct_span_lint_hir(
- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
- tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
- span,
- "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
- |lint| {
- let note = if non_exhaustive {
- "is marked with `#[non_exhaustive]`"
- } else {
- "contains private fields"
- };
- let field_ty = tcx.def_path_str_with_args(def_id, args);
- lint
- .note(format!("this {descr} contains `{field_ty}`, which {note}, \
- and makes it not a breaking change to become non-zero-sized in the future."))
- },
- )
+ let mut prev_non_exhaustive_1zst = false;
+ for (span, _trivial, non_exhaustive_1zst) in field_infos {
+ if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst {
+ // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts.
+ // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst.
+ if non_trivial_count > 0 || prev_non_exhaustive_1zst {
+ tcx.struct_span_lint_hir(
+ REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+ tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+ span,
+ "zero-sized fields in `repr(transparent)` cannot \
+ contain external non-exhaustive types",
+ |lint| {
+ let note = if non_exhaustive {
+ "is marked with `#[non_exhaustive]`"
+ } else {
+ "contains private fields"
+ };
+ let field_ty = tcx.def_path_str_with_args(def_id, args);
+ lint.note(format!(
+ "this {descr} contains `{field_ty}`, which {note}, \
+ and makes it not a breaking change to become \
+ non-zero-sized in the future."
+ ))
+ },
+ )
+ } else {
+ prev_non_exhaustive_1zst = true;
+ }
}
}
}
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 4be3ea8..921a5f5 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -16,6 +16,21 @@
*[other] , perhaps you need to restrict type parameter `{$action_or_ty}` with it
}
+hir_typeck_cannot_cast_to_bool = cannot cast `{$expr_ty}` as `bool`
+ .suggestion = compare with zero instead
+ .help = compare with zero instead
+ .label = unsupported cast
+
+hir_typeck_cast_enum_drop = cannot cast enum `{$expr_ty}` into integer `{$cast_ty}` because it implements `Drop`
+
+hir_typeck_cast_unknown_pointer = cannot cast {$to ->
+ [true] to
+ *[false] from
+ } a pointer of an unknown kind
+ .label_to = needs more type information
+ .note = the type information given here is insufficient to check whether the pointer cast is valid
+ .label_from = the type information given here is insufficient to check whether the pointer cast is valid
+
hir_typeck_const_select_must_be_const = this argument must be a `const fn`
.help = consult the documentation on `const_eval_select` for more information
@@ -29,6 +44,8 @@
hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
+hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
+
hir_typeck_expected_default_return_type = expected `()` because of default return type
hir_typeck_expected_return_type = expected `{$expected}` because of return type
@@ -57,6 +74,13 @@
hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
+hir_typeck_int_to_fat = cannot cast `{$expr_ty}` to a pointer that {$known_wide ->
+ [true] is
+ *[false] may be
+ } wide
+hir_typeck_int_to_fat_label = creating a `{$cast_ty}` requires both an address and {$metadata}
+hir_typeck_int_to_fat_label_nightly = consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts`
+
hir_typeck_invalid_callee = expected function, found {$ty}
hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
@@ -69,6 +93,16 @@
hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect
.suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
+hir_typeck_lossy_provenance_int2ptr =
+ strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`
+ .suggestion = use `.with_addr()` to adjust a valid pointer in the same allocation, to this address
+ .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead
+
+hir_typeck_lossy_provenance_ptr2int =
+ under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`
+ .suggestion = use `.addr()` to obtain the address of a pointer
+ .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead
+
hir_typeck_method_call_on_unknown_raw_pointee =
cannot call a method on a raw pointer with an unknown pointee type
@@ -113,8 +147,18 @@
hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
+hir_typeck_trivial_cast = trivial {$numeric ->
+ [true] numeric cast
+ *[false] cast
+ }: `{$expr_ty}` as `{$cast_ty}`
+ .help = cast can be replaced by coercion; this might require a temporary variable
+
hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field
+
+hir_typeck_use_is_empty =
+ consider using the `is_empty` method on `{$expr_ty}` to determine if it contains anything
+
hir_typeck_yield_expr_outside_of_generator =
yield expression outside of generator literal
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index e8d4e6b..fa77970 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -30,11 +30,10 @@
use super::FnCtxt;
+use crate::errors;
use crate::type_error_struct;
use hir::ExprKind;
-use rustc_errors::{
- struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::mir::Mutability;
@@ -321,33 +320,15 @@
.emit();
}
CastError::CastToBool => {
- let mut err = struct_span_err!(
- fcx.tcx.sess,
- self.span,
- E0054,
- "cannot cast `{}` as `bool`",
- self.expr_ty
- );
-
- if self.expr_ty.is_numeric() {
- match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) {
- Ok(snippet) => {
- err.span_suggestion(
- self.span,
- "compare with zero instead",
- format!("{snippet} != 0"),
- Applicability::MachineApplicable,
- );
- }
- Err(_) => {
- err.span_help(self.span, "compare with zero instead");
- }
- }
+ let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
+ let help = if self.expr_ty.is_numeric() {
+ errors::CannotCastToBoolHelp::Numeric(
+ self.expr_span.shrink_to_hi().with_hi(self.span.hi()),
+ )
} else {
- err.span_label(self.span, "unsupported cast");
- }
-
- err.emit();
+ errors::CannotCastToBoolHelp::Unsupported(self.span)
+ };
+ fcx.tcx.sess.emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help });
}
CastError::CastToChar => {
let mut err = type_error_struct!(
@@ -536,33 +517,20 @@
.emit();
}
CastError::IntToFatCast(known_metadata) => {
- let mut err = struct_span_err!(
- fcx.tcx.sess,
- self.cast_span,
- E0606,
- "cannot cast `{}` to a pointer that {} wide",
- fcx.ty_to_string(self.expr_ty),
- if known_metadata.is_some() { "is" } else { "may be" }
- );
-
- err.span_label(
- self.cast_span,
- format!(
- "creating a `{}` requires both an address and {}",
- self.cast_ty,
- known_metadata.unwrap_or("type-specific metadata"),
- ),
- );
-
- if fcx.tcx.sess.is_nightly_build() {
- err.span_label(
- self.expr_span,
- "consider casting this expression to `*const ()`, \
- then using `core::ptr::from_raw_parts`",
- );
- }
-
- err.emit();
+ let expr_if_nightly = fcx.tcx.sess.is_nightly_build().then_some(self.expr_span);
+ let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
+ let expr_ty = fcx.ty_to_string(self.expr_ty);
+ let metadata = known_metadata.unwrap_or("type-specific metadata");
+ let known_wide = known_metadata.is_some();
+ let span = self.cast_span;
+ fcx.tcx.sess.emit_err(errors::IntToWide {
+ span,
+ metadata,
+ expr_ty,
+ cast_ty,
+ expr_if_nightly,
+ known_wide,
+ });
}
CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => {
let unknown_cast_to = match e {
@@ -570,27 +538,16 @@
CastError::UnknownExprPtrKind => false,
_ => bug!(),
};
- let mut err = struct_span_err!(
- fcx.tcx.sess,
- if unknown_cast_to { self.cast_span } else { self.span },
- E0641,
- "cannot cast {} a pointer of an unknown kind",
- if unknown_cast_to { "to" } else { "from" }
- );
- if unknown_cast_to {
- err.span_label(self.cast_span, "needs more type information");
- err.note(
- "the type information given here is insufficient to check whether \
- the pointer cast is valid",
- );
+ let (span, sub) = if unknown_cast_to {
+ (self.cast_span, errors::CastUnknownPointerSub::To(self.cast_span))
} else {
- err.span_label(
- self.span,
- "the type information given here is insufficient to check whether \
- the pointer cast is valid",
- );
- }
- err.emit();
+ (self.cast_span, errors::CastUnknownPointerSub::From(self.span))
+ };
+ fcx.tcx.sess.emit_err(errors::CastUnknownPointer {
+ span,
+ to: unknown_cast_to,
+ sub,
+ });
}
CastError::ForeignNonExhaustiveAdt => {
make_invalid_casting_error(
@@ -674,31 +631,18 @@
}
fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
- let t_cast = self.cast_ty;
- let t_expr = self.expr_ty;
- let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() {
- ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS)
+ let (numeric, lint) = if self.cast_ty.is_numeric() && self.expr_ty.is_numeric() {
+ (true, lint::builtin::TRIVIAL_NUMERIC_CASTS)
} else {
- ("", lint::builtin::TRIVIAL_CASTS)
+ (false, lint::builtin::TRIVIAL_CASTS)
};
- fcx.tcx.struct_span_lint_hir(
+ let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
+ let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
+ fcx.tcx.emit_spanned_lint(
lint,
self.expr.hir_id,
self.span,
- DelayDm(|| {
- format!(
- "trivial {}cast: `{}` as `{}`",
- adjective,
- fcx.ty_to_string(t_expr),
- fcx.ty_to_string(t_cast)
- )
- }),
- |lint| {
- lint.help(
- "cast can be replaced by coercion; this might \
- require a temporary variable",
- )
- },
+ errors::TrivialCast { numeric, expr_ty, cast_ty },
);
}
@@ -991,93 +935,67 @@
if let ty::Adt(d, _) = self.expr_ty.kind()
&& d.has_dtor(fcx.tcx)
{
- fcx.tcx.struct_span_lint_hir(
+ let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
+ let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
+
+ fcx.tcx.emit_spanned_lint(
lint::builtin::CENUM_IMPL_DROP_CAST,
self.expr.hir_id,
self.span,
- DelayDm(|| format!(
- "cannot cast enum `{}` into integer `{}` because it implements `Drop`",
- self.expr_ty, self.cast_ty
- )),
- |lint| {
- lint
- },
+ errors::CastEnumDrop {
+ expr_ty,
+ cast_ty,
+ }
);
}
}
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
- fcx.tcx.struct_span_lint_hir(
+ let expr_prec = self.expr.precedence().order();
+ let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX;
+
+ let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
+ let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
+ let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
+ let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
+ let expr_span = self.expr_span.shrink_to_lo();
+ let sugg = match (needs_parens, needs_cast) {
+ (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast {
+ expr_span,
+ cast_span,
+ cast_ty,
+ },
+ (true, false) => {
+ errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span }
+ }
+ (false, true) => {
+ errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty }
+ }
+ (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span },
+ };
+
+ let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg };
+ fcx.tcx.emit_spanned_lint(
lint::builtin::LOSSY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
- DelayDm(|| format!(
- "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
- self.expr_ty, self.cast_ty
- )),
- |lint| {
- let msg = "use `.addr()` to obtain the address of a pointer";
-
- let expr_prec = self.expr.precedence().order();
- let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX;
-
- let scalar_cast = match t_c {
- ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(),
- _ => format!(" as {}", self.cast_ty),
- };
-
- let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
-
- if needs_parens {
- let suggestions = vec![
- (self.expr_span.shrink_to_lo(), String::from("(")),
- (cast_span, format!(").addr(){scalar_cast}")),
- ];
-
- lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
- } else {
- lint.span_suggestion(
- cast_span,
- msg,
- format!(".addr(){scalar_cast}"),
- Applicability::MaybeIncorrect,
- );
- }
-
- lint.help(
- "if you can't comply with strict provenance and need to expose the pointer \
- provenance you can use `.expose_addr()` instead"
- );
-
- lint
- },
+ lint,
);
}
fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
- fcx.tcx.struct_span_lint_hir(
+ let sugg = errors::LossyProvenanceInt2PtrSuggestion {
+ lo: self.expr_span.shrink_to_lo(),
+ hi: self.expr_span.shrink_to_hi().to(self.cast_span),
+ };
+ let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
+ let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
+ let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg };
+ fcx.tcx.emit_spanned_lint(
lint::builtin::FUZZY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
- DelayDm(|| format!(
- "strict provenance disallows casting integer `{}` to pointer `{}`",
- self.expr_ty, self.cast_ty
- )),
- |lint| {
- let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
- let suggestions = vec![
- (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")),
- (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")),
- ];
-
- lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
- lint.help(
- "if you can't comply with strict provenance and don't have a pointer with \
- the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
- );
-
- lint
- },
+ lint,
);
}
@@ -1093,26 +1011,19 @@
if let Some((deref_ty, _)) = derefed {
// Give a note about what the expr derefs to.
if deref_ty != self.expr_ty.peel_refs() {
- err.span_note(
- self.expr_span,
- format!(
- "this expression `Deref`s to `{}` which implements `is_empty`",
- fcx.ty_to_string(deref_ty)
- ),
- );
+ err.subdiagnostic(errors::DerefImplsIsEmpty {
+ span: self.expr_span,
+ deref_ty: fcx.ty_to_string(deref_ty),
+ });
}
// Create a multipart suggestion: add `!` and `.is_empty()` in
// place of the cast.
- let suggestion = vec![
- (self.expr_span.shrink_to_lo(), "!".to_string()),
- (self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
- ];
-
- err.multipart_suggestion_verbose(format!(
- "consider using the `is_empty` method on `{}` to determine if it contains anything",
- fcx.ty_to_string(self.expr_ty),
- ), suggestion, Applicability::MaybeIncorrect);
+ err.subdiagnostic(errors::UseIsEmpty {
+ lo: self.expr_span.shrink_to_lo(),
+ hi: self.span.with_lo(self.expr_span.hi()),
+ expr_ty: fcx.ty_to_string(self.expr_ty),
+ });
}
}
}
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index c1d7056..7152585 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -6,7 +6,7 @@
AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, MultiSpan,
SubdiagnosticMessage,
};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::{
edition::{Edition, LATEST_STABLE_EDITION},
@@ -269,6 +269,69 @@
pub found_ty: Ty<'tcx>,
}
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_lossy_provenance_int2ptr)]
+#[help]
+pub struct LossyProvenanceInt2Ptr<'tcx> {
+ pub expr_ty: Ty<'tcx>,
+ pub cast_ty: Ty<'tcx>,
+ #[subdiagnostic]
+ pub sugg: LossyProvenanceInt2PtrSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
+pub struct LossyProvenanceInt2PtrSuggestion {
+ #[suggestion_part(code = "(...).with_addr(")]
+ pub lo: Span,
+ #[suggestion_part(code = ")")]
+ pub hi: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_lossy_provenance_ptr2int)]
+#[help]
+pub struct LossyProvenancePtr2Int<'tcx> {
+ pub expr_ty: Ty<'tcx>,
+ pub cast_ty: Ty<'tcx>,
+ #[subdiagnostic]
+ pub sugg: LossyProvenancePtr2IntSuggestion<'tcx>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum LossyProvenancePtr2IntSuggestion<'tcx> {
+ #[multipart_suggestion(hir_typeck_suggestion, applicability = "maybe-incorrect")]
+ NeedsParensCast {
+ #[suggestion_part(code = "(")]
+ expr_span: Span,
+ #[suggestion_part(code = ").addr() as {cast_ty}")]
+ cast_span: Span,
+ cast_ty: Ty<'tcx>,
+ },
+ #[multipart_suggestion(hir_typeck_suggestion, applicability = "maybe-incorrect")]
+ NeedsParens {
+ #[suggestion_part(code = "(")]
+ expr_span: Span,
+ #[suggestion_part(code = ").addr()")]
+ cast_span: Span,
+ },
+ #[suggestion(
+ hir_typeck_suggestion,
+ code = ".addr() as {cast_ty}",
+ applicability = "maybe-incorrect"
+ )]
+ NeedsCast {
+ #[primary_span]
+ cast_span: Span,
+ cast_ty: Ty<'tcx>,
+ },
+ #[suggestion(hir_typeck_suggestion, code = ".addr()", applicability = "maybe-incorrect")]
+ Other {
+ #[primary_span]
+ cast_span: Span,
+ },
+}
+
#[derive(Subdiagnostic)]
pub enum HelpUseLatestEdition {
#[help(hir_typeck_help_set_edition_cargo)]
@@ -298,6 +361,20 @@
pub ty: String,
}
+#[derive(Diagnostic)]
+#[diag(hir_typeck_int_to_fat, code = "E0606")]
+pub struct IntToWide<'tcx> {
+ #[primary_span]
+ #[label(hir_typeck_int_to_fat_label)]
+ pub span: Span,
+ pub metadata: &'tcx str,
+ pub expr_ty: String,
+ pub cast_ty: Ty<'tcx>,
+ #[label(hir_typeck_int_to_fat_label_nightly)]
+ pub expr_if_nightly: Option<Span>,
+ pub known_wide: bool,
+}
+
#[derive(Subdiagnostic)]
pub enum OptionResultRefMismatch {
#[suggestion(
@@ -396,6 +473,20 @@
pub span: Span,
}
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+ hir_typeck_use_is_empty,
+ applicability = "maybe-incorrect",
+ style = "verbose"
+)]
+pub struct UseIsEmpty {
+ #[suggestion_part(code = "!")]
+ pub lo: Span,
+ #[suggestion_part(code = ".is_empty()")]
+ pub hi: Span,
+ pub expr_ty: String,
+}
+
#[derive(Diagnostic)]
#[diag(hir_typeck_arg_mismatch_indeterminate)]
pub struct ArgMismatchIndeterminate {
@@ -442,6 +533,15 @@
pub span: Span,
}
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_trivial_cast)]
+#[help]
+pub struct TrivialCast<'tcx> {
+ pub numeric: bool,
+ pub expr_ty: Ty<'tcx>,
+ pub cast_ty: Ty<'tcx>,
+}
+
#[derive(Diagnostic)]
#[diag(hir_typeck_no_associated_item, code = "E0599")]
pub struct NoAssociatedItem {
@@ -465,6 +565,74 @@
}
#[derive(Diagnostic)]
+#[diag(hir_typeck_cannot_cast_to_bool, code = "E0054")]
+pub struct CannotCastToBool<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub expr_ty: Ty<'tcx>,
+ #[subdiagnostic]
+ pub help: CannotCastToBoolHelp,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_cast_enum_drop)]
+pub struct CastEnumDrop<'tcx> {
+ pub expr_ty: Ty<'tcx>,
+ pub cast_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_cast_unknown_pointer, code = "E0641")]
+pub struct CastUnknownPointer {
+ #[primary_span]
+ pub span: Span,
+ pub to: bool,
+ #[subdiagnostic]
+ pub sub: CastUnknownPointerSub,
+}
+
+pub enum CastUnknownPointerSub {
+ To(Span),
+ From(Span),
+}
+
+impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, f: F)
+ where
+ F: Fn(
+ &mut Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ CastUnknownPointerSub::To(span) => {
+ let msg = f(diag, crate::fluent_generated::hir_typeck_label_to.into());
+ diag.span_label(span, msg);
+ let msg = f(diag, crate::fluent_generated::hir_typeck_note.into());
+ diag.note(msg);
+ }
+ CastUnknownPointerSub::From(span) => {
+ let msg = f(diag, crate::fluent_generated::hir_typeck_label_from.into());
+ diag.span_label(span, msg);
+ }
+ }
+ }
+}
+
+#[derive(Subdiagnostic)]
+pub enum CannotCastToBoolHelp {
+ #[suggestion(
+ hir_typeck_suggestion,
+ applicability = "machine-applicable",
+ code = " != 0",
+ style = "verbose"
+ )]
+ Numeric(#[primary_span] Span),
+ #[label(hir_typeck_label)]
+ Unsupported(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
#[diag(hir_typeck_ctor_is_private, code = "E0603")]
pub struct CtorIsPrivate {
#[primary_span]
@@ -473,6 +641,14 @@
}
#[derive(Subdiagnostic)]
+#[note(hir_typeck_deref_is_empty)]
+pub struct DerefImplsIsEmpty {
+ #[primary_span]
+ pub span: Span,
+ pub deref_ty: String,
+}
+
+#[derive(Subdiagnostic)]
#[multipart_suggestion(
hir_typeck_convert_using_method,
applicability = "machine-applicable",
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 2acb43c..43d4496 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -33,27 +33,17 @@
};
let generics = self.tcx.generics_of(def_id);
- let predicate_args = match unsubstituted_pred.kind().skip_binder() {
- ty::ClauseKind::Trait(pred) => pred.trait_ref.args.to_vec(),
- ty::ClauseKind::Projection(pred) => pred.projection_ty.args.to_vec(),
- ty::ClauseKind::ConstArgHasType(arg, ty) => {
- vec![ty.into(), arg.into()]
- }
- ty::ClauseKind::ConstEvaluatable(e) => vec![e.into()],
- _ => return false,
- };
+ let (predicate_args, predicate_self_type_to_point_at) =
+ match unsubstituted_pred.kind().skip_binder() {
+ ty::ClauseKind::Trait(pred) => {
+ (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into()))
+ }
+ ty::ClauseKind::Projection(pred) => (pred.projection_ty.args.to_vec(), None),
+ ty::ClauseKind::ConstArgHasType(arg, ty) => (vec![ty.into(), arg.into()], None),
+ ty::ClauseKind::ConstEvaluatable(e) => (vec![e.into()], None),
+ _ => return false,
+ };
- let direct_param = if let ty::ClauseKind::Trait(pred) = unsubstituted_pred.kind().skip_binder()
- && let ty = pred.trait_ref.self_ty()
- && let ty::Param(_param) = ty.kind()
- && let Some(arg) = predicate_args.get(0)
- && let ty::GenericArgKind::Type(arg_ty) = arg.unpack()
- && arg_ty == ty
- {
- Some(*arg)
- } else {
- None
- };
let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| {
predicate_args.iter().find_map(|arg| {
arg.walk().find_map(|arg| {
@@ -112,18 +102,21 @@
let qpath =
if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None };
- (Some(*expr), qpath)
+ (Some(&expr.kind), qpath)
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)),
_ => return false,
};
if let Some(qpath) = qpath {
- if let Some(param) = direct_param {
- if self.point_at_path_if_possible(error, def_id, param, &qpath) {
- return true;
- }
+ // Prefer pointing at the turbofished arg that corresponds to the
+ // self type of the failing predicate over anything else.
+ if let Some(param) = predicate_self_type_to_point_at
+ && self.point_at_path_if_possible(error, def_id, param, &qpath)
+ {
+ return true;
}
+
if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Call(callee, args),
hir_id: call_hir_id,
@@ -166,11 +159,16 @@
}
}
- match expr.map(|e| e.kind) {
+ match expr {
Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => {
- if let Some(param) = direct_param
+ if let Some(param) = predicate_self_type_to_point_at
&& self.point_at_generic_if_possible(error, def_id, param, segment)
{
+ // HACK: This is not correct, since `predicate_self_type_to_point_at` might
+ // not actually correspond to the receiver of the method call. But we
+ // re-adjust the cause code here in order to prefer pointing at one of
+ // the method's turbofish segments but still use `FunctionArgumentObligation`
+ // elsewhere. Hopefully this doesn't break something.
error.obligation.cause.map_code(|parent_code| {
ObligationCauseCode::FunctionArgumentObligation {
arg_hir_id: receiver.hir_id,
@@ -180,6 +178,7 @@
});
return true;
}
+
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
.into_iter()
.flatten()
@@ -237,7 +236,7 @@
}
for param in [
- direct_param,
+ predicate_self_type_to_point_at,
param_to_point_at,
fallback_param_to_point_at,
self_param_to_point_at,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 4237b44..4a245d3 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -325,13 +325,16 @@
fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
// FIXME: normalization and escaping regions
let ty = if !ty.has_escaping_bound_vars() {
- if let ty::Alias(
- ty::AliasKind::Projection | ty::AliasKind::Weak,
- ty::AliasTy { args, def_id, .. },
- ) = ty.kind()
+ // NOTE: These obligations are 100% redundant and are implied by
+ // WF obligations that are registered elsewhere, but they have a
+ // better cause code assigned to them in `add_required_obligations_for_hir`.
+ // This means that they should shadow obligations with worse spans.
+ if let ty::Alias(ty::Projection | ty::Weak, ty::AliasTy { args, def_id, .. }) =
+ ty.kind()
{
self.add_required_obligations_for_hir(span, *def_id, args, hir_id);
}
+
self.normalize(span, ty)
} else {
ty
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 3706c96..4cf9d44 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1600,9 +1600,12 @@
if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? {
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, args));
if let Err(e) = ct.error_reported() {
- return Err(ErrorHandled::Reported(e.into()));
+ return Err(ErrorHandled::Reported(
+ e.into(),
+ span.unwrap_or(rustc_span::DUMMY_SP),
+ ));
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
- return Err(ErrorHandled::TooGeneric);
+ return Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP)));
} else {
args = replace_param_and_infer_args_with_placeholder(tcx, args);
}
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 108a10b..82162fd 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -52,6 +52,8 @@
overflow while adding drop-check rules for {$ty}
.note = overflowed on {$overflow_ty}
+middle_erroneous_constant = erroneous constant encountered
+
middle_layout_references_error =
the type has an unknown layout
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index b346cd4..3c55365 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -144,5 +144,12 @@
pub abi: &'static str,
}
+#[derive(Diagnostic)]
+#[diag(middle_erroneous_constant)]
+pub struct ErroneousConstant {
+ #[primary_span]
+ pub span: Span,
+}
+
/// Used by `rustc_const_eval`
pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error;
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
new file mode 100644
index 0000000..9e86eb2
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -0,0 +1,574 @@
+use std::fmt::{self, Debug, Display, Formatter};
+
+use rustc_hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{self as hir};
+use rustc_span::Span;
+use rustc_target::abi::{HasDataLayout, Size};
+
+use crate::mir::interpret::{
+ alloc_range, AllocId, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar,
+};
+use crate::mir::{pretty_print_const_value, Promoted};
+use crate::ty::{self, print::pretty_print_const, List, Ty, TyCtxt};
+use crate::ty::{GenericArgs, GenericArgsRef};
+use crate::ty::{ScalarInt, UserTypeAnnotationIndex};
+
+///////////////////////////////////////////////////////////////////////////
+/// Evaluated Constants
+
+/// Represents the result of const evaluation via the `eval_to_allocation` query.
+/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
+/// Here we indirect via an `AllocId`.
+#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
+pub struct ConstAlloc<'tcx> {
+ /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
+ /// (so you can use `AllocMap::unwrap_memory`).
+ pub alloc_id: AllocId,
+ pub ty: Ty<'tcx>,
+}
+
+/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
+/// array length computations, enum discriminants and the pattern matching logic.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
+#[derive(HashStable, Lift)]
+pub enum ConstValue<'tcx> {
+ /// Used for types with `layout::abi::Scalar` ABI.
+ ///
+ /// Not using the enum `Value` to encode that this must not be `Uninit`.
+ Scalar(Scalar),
+
+ /// Only for ZSTs.
+ ZeroSized,
+
+ /// Used for `&[u8]` and `&str`.
+ ///
+ /// This is worth an optimized representation since Rust has literals of these types.
+ /// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
+ /// measurable performance improvements on stress tests.
+ Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
+
+ /// A value not representable by the other variants; needs to be stored in-memory.
+ ///
+ /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
+ Indirect {
+ /// The backing memory of the value. May contain more memory than needed for just the value
+ /// if this points into some other larger ConstValue.
+ ///
+ /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
+ /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
+ /// back, we can preserve the original `AllocId`.
+ alloc_id: AllocId,
+ /// Offset into `alloc`
+ offset: Size,
+ },
+}
+
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(ConstValue<'_>, 32);
+
+impl<'tcx> ConstValue<'tcx> {
+ #[inline]
+ pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
+ match *self {
+ ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
+ ConstValue::Scalar(val) => Some(val),
+ }
+ }
+
+ pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
+ self.try_to_scalar()?.try_to_int().ok()
+ }
+
+ pub fn try_to_bits(&self, size: Size) -> Option<u128> {
+ self.try_to_scalar_int()?.to_bits(size).ok()
+ }
+
+ pub fn try_to_bool(&self) -> Option<bool> {
+ self.try_to_scalar_int()?.try_into().ok()
+ }
+
+ pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+ self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
+ }
+
+ pub fn try_to_bits_for_ty(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Option<u128> {
+ let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+ self.try_to_bits(size)
+ }
+
+ pub fn from_bool(b: bool) -> Self {
+ ConstValue::Scalar(Scalar::from_bool(b))
+ }
+
+ pub fn from_u64(i: u64) -> Self {
+ ConstValue::Scalar(Scalar::from_u64(i))
+ }
+
+ pub fn from_u128(i: u128) -> Self {
+ ConstValue::Scalar(Scalar::from_u128(i))
+ }
+
+ pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
+ ConstValue::Scalar(Scalar::from_target_usize(i, cx))
+ }
+
+ /// Must only be called on constants of type `&str` or `&[u8]`!
+ pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
+ let (data, start, end) = match self {
+ ConstValue::Scalar(_) | ConstValue::ZeroSized => {
+ bug!("`try_get_slice_bytes` on non-slice constant")
+ }
+ &ConstValue::Slice { data, start, end } => (data, start, end),
+ &ConstValue::Indirect { alloc_id, offset } => {
+ // The reference itself is stored behind an indirection.
+ // Load the reference, and then load the actual slice contents.
+ let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+ let ptr_size = tcx.data_layout.pointer_size;
+ if a.size() < offset + 2 * ptr_size {
+ // (partially) dangling reference
+ return None;
+ }
+ // Read the wide pointer components.
+ let ptr = a
+ .read_scalar(
+ &tcx,
+ alloc_range(offset, ptr_size),
+ /* read_provenance */ true,
+ )
+ .ok()?;
+ let ptr = ptr.to_pointer(&tcx).ok()?;
+ let len = a
+ .read_scalar(
+ &tcx,
+ alloc_range(offset + ptr_size, ptr_size),
+ /* read_provenance */ false,
+ )
+ .ok()?;
+ let len = len.to_target_usize(&tcx).ok()?;
+ let len: usize = len.try_into().ok()?;
+ if len == 0 {
+ return Some(&[]);
+ }
+ // Non-empty slice, must have memory. We know this is a relative pointer.
+ let (inner_alloc_id, offset) = ptr.into_parts();
+ let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
+ (data, offset.bytes_usize(), offset.bytes_usize() + len)
+ }
+ };
+
+ // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
+ Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// Constants
+///
+/// Two constants are equal if they are the same constant. Note that
+/// this does not necessarily mean that they are `==` in Rust. In
+/// particular, one must be wary of `NaN`!
+
+#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub struct Constant<'tcx> {
+ pub span: Span,
+
+ /// Optional user-given type: for something like
+ /// `collect::<Vec<_>>`, this would be present and would
+ /// indicate that `Vec<_>` was explicitly specified.
+ ///
+ /// Needed for NLL to impose user-given type constraints.
+ pub user_ty: Option<UserTypeAnnotationIndex>,
+
+ pub literal: ConstantKind<'tcx>,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum ConstantKind<'tcx> {
+ /// This constant came from the type system.
+ ///
+ /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
+ /// this ensures that we consistently produce "clean" values without data in the padding or
+ /// anything like that.
+ Ty(ty::Const<'tcx>),
+
+ /// An unevaluated mir constant which is not part of the type system.
+ ///
+ /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will
+ /// always flow through a valtree, so all data not captured in the valtree is lost. This variant
+ /// directly uses the evaluated result of the given constant, including e.g. data stored in
+ /// padding.
+ Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
+
+ /// This constant cannot go back into the type system, as it represents
+ /// something the type system cannot handle (e.g. pointers).
+ Val(ConstValue<'tcx>, Ty<'tcx>),
+}
+
+impl<'tcx> Constant<'tcx> {
+ pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
+ match self.literal.try_to_scalar() {
+ Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
+ GlobalAlloc::Static(def_id) => {
+ assert!(!tcx.is_thread_local_static(def_id));
+ Some(def_id)
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+ #[inline]
+ pub fn ty(&self) -> Ty<'tcx> {
+ self.literal.ty()
+ }
+}
+
+impl<'tcx> ConstantKind<'tcx> {
+ #[inline(always)]
+ pub fn ty(&self) -> Ty<'tcx> {
+ match self {
+ ConstantKind::Ty(c) => c.ty(),
+ ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => *ty,
+ }
+ }
+
+ #[inline]
+ pub fn try_to_scalar(self) -> Option<Scalar> {
+ match self {
+ ConstantKind::Ty(c) => match c.kind() {
+ ty::ConstKind::Value(valtree) => match valtree {
+ ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
+ ty::ValTree::Branch(_) => None,
+ },
+ _ => None,
+ },
+ ConstantKind::Val(val, _) => val.try_to_scalar(),
+ ConstantKind::Unevaluated(..) => None,
+ }
+ }
+
+ #[inline]
+ pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
+ self.try_to_scalar()?.try_to_int().ok()
+ }
+
+ #[inline]
+ pub fn try_to_bits(self, size: Size) -> Option<u128> {
+ self.try_to_scalar_int()?.to_bits(size).ok()
+ }
+
+ #[inline]
+ pub fn try_to_bool(self) -> Option<bool> {
+ self.try_to_scalar_int()?.try_into().ok()
+ }
+
+ #[inline]
+ pub fn eval(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Option<Span>,
+ ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+ match self {
+ ConstantKind::Ty(c) => {
+ // We want to consistently have a "clean" value for type system constants (i.e., no
+ // data hidden in the padding), so we always go through a valtree here.
+ let val = c.eval(tcx, param_env, span)?;
+ Ok(tcx.valtree_to_const_val((self.ty(), val)))
+ }
+ ConstantKind::Unevaluated(uneval, _) => {
+ // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
+ tcx.const_eval_resolve(param_env, uneval, span)
+ }
+ ConstantKind::Val(val, _) => Ok(val),
+ }
+ }
+
+ /// Normalizes the constant to a value or an error if possible.
+ #[inline]
+ pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+ match self.eval(tcx, param_env, None) {
+ Ok(val) => Self::Val(val, self.ty()),
+ Err(ErrorHandled::Reported(guar, _span)) => {
+ Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
+ }
+ Err(ErrorHandled::TooGeneric(_span)) => self,
+ }
+ }
+
+ #[inline]
+ pub fn try_eval_scalar(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<Scalar> {
+ self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+ }
+
+ #[inline]
+ pub fn try_eval_scalar_int(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<ScalarInt> {
+ self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+ }
+
+ #[inline]
+ pub fn try_eval_bits(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Option<u128> {
+ let int = self.try_eval_scalar_int(tcx, param_env)?;
+ assert_eq!(self.ty(), ty);
+ let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+ int.to_bits(size).ok()
+ }
+
+ /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
+ #[inline]
+ pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
+ self.try_eval_bits(tcx, param_env, ty)
+ .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
+ }
+
+ #[inline]
+ pub fn try_eval_target_usize(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<u64> {
+ self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
+ }
+
+ #[inline]
+ /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
+ pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
+ self.try_eval_target_usize(tcx, param_env)
+ .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
+ }
+
+ #[inline]
+ pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
+ self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
+ }
+
+ #[inline]
+ pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
+ Self::Val(val, ty)
+ }
+
+ pub fn from_bits(
+ tcx: TyCtxt<'tcx>,
+ bits: u128,
+ param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ ) -> Self {
+ let size = tcx
+ .layout_of(param_env_ty)
+ .unwrap_or_else(|e| {
+ bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
+ })
+ .size;
+ let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
+
+ Self::Val(cv, param_env_ty.value)
+ }
+
+ #[inline]
+ pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
+ let cv = ConstValue::from_bool(v);
+ Self::Val(cv, tcx.types.bool)
+ }
+
+ #[inline]
+ pub fn zero_sized(ty: Ty<'tcx>) -> Self {
+ let cv = ConstValue::ZeroSized;
+ Self::Val(cv, ty)
+ }
+
+ pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
+ let ty = tcx.types.usize;
+ Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
+ }
+
+ #[inline]
+ pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
+ let val = ConstValue::Scalar(s);
+ Self::Val(val, ty)
+ }
+
+ /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
+ /// converted to a constant, everything else becomes `Unevaluated`.
+ #[instrument(skip(tcx), level = "debug", ret)]
+ pub fn from_anon_const(
+ tcx: TyCtxt<'tcx>,
+ def: LocalDefId,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Self {
+ let body_id = match tcx.hir().get_by_def_id(def) {
+ hir::Node::AnonConst(ac) => ac.body,
+ _ => {
+ span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
+ }
+ };
+
+ let expr = &tcx.hir().body(body_id).value;
+ debug!(?expr);
+
+ // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
+ // currently have to be wrapped in curly brackets, so it's necessary to special-case.
+ let expr = match &expr.kind {
+ hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
+ block.expr.as_ref().unwrap()
+ }
+ _ => expr,
+ };
+ debug!("expr.kind: {:?}", expr.kind);
+
+ let ty = tcx.type_of(def).instantiate_identity();
+ debug!(?ty);
+
+ // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
+ // does not provide the parents generics to anonymous constants. We still allow generic const
+ // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
+ // ever try to substitute the generic parameters in their bodies.
+ //
+ // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
+ // cause issues if we were to remove that special-case and try to evaluate the constant instead.
+ use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
+ match expr.kind {
+ ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
+ // Find the name and index of the const parameter by indexing the generics of
+ // the parent item and construct a `ParamConst`.
+ let item_def_id = tcx.parent(def_id);
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ let name = tcx.item_name(def_id);
+ let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
+ debug!(?ty_const);
+
+ return Self::Ty(ty_const);
+ }
+ _ => {}
+ }
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def);
+ let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
+ && let Some(parent_did) = parent_hir_id.as_owner()
+ {
+ GenericArgs::identity_for_item(tcx, parent_did)
+ } else {
+ List::empty()
+ };
+ debug!(?parent_args);
+
+ let did = def.to_def_id();
+ let child_args = GenericArgs::identity_for_item(tcx, did);
+ let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
+ debug!(?args);
+
+ let span = tcx.def_span(def);
+ let uneval = UnevaluatedConst::new(did, args);
+ debug!(?span, ?param_env);
+
+ match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
+ Ok(val) => {
+ debug!("evaluated const value");
+ Self::Val(val, ty)
+ }
+ Err(_) => {
+ debug!("error encountered during evaluation");
+ // Error was handled in `const_eval_resolve`. Here we just create a
+ // new unevaluated const and error hard later in codegen
+ Self::Unevaluated(
+ UnevaluatedConst {
+ def: did,
+ args: GenericArgs::identity_for_item(tcx, did),
+ promoted: None,
+ },
+ ty,
+ )
+ }
+ }
+ }
+
+ pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+ match c.kind() {
+ ty::ConstKind::Value(valtree) => {
+ // Make sure that if `c` is normalized, then the return value is normalized.
+ let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
+ Self::Val(const_val, c.ty())
+ }
+ _ => Self::Ty(c),
+ }
+ }
+}
+
+/// An unevaluated (potentially generic) constant used in MIR.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct UnevaluatedConst<'tcx> {
+ pub def: DefId,
+ pub args: GenericArgsRef<'tcx>,
+ pub promoted: Option<Promoted>,
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+ #[inline]
+ pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
+ assert_eq!(self.promoted, None);
+ ty::UnevaluatedConst { def: self.def, args: self.args }
+ }
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+ #[inline]
+ pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
+ UnevaluatedConst { def, args, promoted: Default::default() }
+ }
+
+ #[inline]
+ pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
+ UnevaluatedConst::new(instance.def_id(), instance.args)
+ }
+}
+
+impl<'tcx> Debug for Constant<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{self}")
+ }
+}
+
+impl<'tcx> Display for Constant<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ match self.ty().kind() {
+ ty::FnDef(..) => {}
+ _ => write!(fmt, "const ")?,
+ }
+ Display::fmt(&self.literal, fmt)
+ }
+}
+
+impl<'tcx> Display for ConstantKind<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ match *self {
+ ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
+ ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
+ // FIXME(valtrees): Correctly print mir constants.
+ ConstantKind::Unevaluated(..) => {
+ fmt.write_str("_")?;
+ Ok(())
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 2194ef8..bc464ac 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,8 +1,9 @@
-use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
+use super::{AllocId, AllocRange, Pointer, Scalar};
-use crate::mir::interpret::ConstValue;
+use crate::error;
+use crate::mir::{ConstAlloc, ConstValue};
use crate::query::TyCtxtAt;
-use crate::ty::{layout, tls, Ty, ValTree};
+use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
use rustc_data_structures::sync::Lock;
use rustc_errors::{
@@ -11,7 +12,7 @@
};
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
-use rustc_span::def_id::DefId;
+use rustc_span::{def_id::DefId, Span, DUMMY_SP};
use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
use std::borrow::Cow;
@@ -21,16 +22,51 @@
pub enum ErrorHandled {
/// Already reported an error for this evaluation, and the compilation is
/// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
- Reported(ReportedErrorInfo),
+ Reported(ReportedErrorInfo, Span),
/// Don't emit an error, the evaluation failed because the MIR was generic
/// and the args didn't fully monomorphize it.
- TooGeneric,
+ TooGeneric(Span),
}
impl From<ErrorGuaranteed> for ErrorHandled {
#[inline]
fn from(error: ErrorGuaranteed) -> ErrorHandled {
- ErrorHandled::Reported(error.into())
+ ErrorHandled::Reported(error.into(), DUMMY_SP)
+ }
+}
+
+impl ErrorHandled {
+ pub fn with_span(self, span: Span) -> Self {
+ match self {
+ ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span),
+ ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span),
+ }
+ }
+
+ pub fn emit_err(&self, tcx: TyCtxt<'_>) -> ErrorGuaranteed {
+ match self {
+ &ErrorHandled::Reported(err, span) => {
+ if !err.is_tainted_by_errors && !span.is_dummy() {
+ tcx.sess.emit_err(error::ErroneousConstant { span });
+ }
+ err.error
+ }
+ &ErrorHandled::TooGeneric(span) => tcx.sess.delay_span_bug(
+ span,
+ "encountered TooGeneric error when monomorphic data was expected",
+ ),
+ }
+ }
+
+ pub fn emit_note(&self, tcx: TyCtxt<'_>) {
+ match self {
+ &ErrorHandled::Reported(err, span) => {
+ if !err.is_tainted_by_errors && !span.is_dummy() {
+ tcx.sess.emit_note(error::ErroneousConstant { span });
+ }
+ }
+ &ErrorHandled::TooGeneric(_) => {}
+ }
}
}
@@ -45,12 +81,6 @@
pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo {
ReportedErrorInfo { is_tainted_by_errors: true, error }
}
-
- /// Returns true if evaluation failed because MIR was tainted by errors.
- #[inline]
- pub fn is_tainted_by_errors(self) -> bool {
- self.is_tainted_by_errors
- }
}
impl From<ErrorGuaranteed> for ReportedErrorInfo {
@@ -162,6 +192,16 @@
}
}
+impl From<ErrorHandled> for InterpErrorInfo<'_> {
+ fn from(err: ErrorHandled) -> Self {
+ InterpError::InvalidProgram(match err {
+ ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r),
+ ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric,
+ })
+ .into()
+ }
+}
+
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
fn from(kind: InterpError<'tcx>) -> Self {
InterpErrorInfo(Box::new(InterpErrorInfoInner {
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 8c00746..d21f82f 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -149,7 +149,7 @@
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
};
-pub use self::value::{ConstAlloc, ConstValue, Scalar};
+pub use self::value::Scalar;
pub use self::allocation::{
alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index fc659ce..fbf6403 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -61,8 +61,10 @@
let cid = GlobalId { instance, promoted: ct.promoted };
self.const_eval_global_id(param_env, cid, span)
}
- Ok(None) => Err(ErrorHandled::TooGeneric),
- Err(err) => Err(ErrorHandled::Reported(err.into())),
+ // For errors during resolution, we deliberately do not point at the usage site of the constant,
+ // since for these errors the place the constant is used shouldn't matter.
+ Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
}
}
@@ -117,8 +119,10 @@
}
})
}
- Ok(None) => Err(ErrorHandled::TooGeneric),
- Err(err) => Err(ErrorHandled::Reported(err.into())),
+ // For errors during resolution, we deliberately do not point at the usage site of the constant,
+ // since for these errors the place the constant is used shouldn't matter.
+ Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
}
}
@@ -143,7 +147,8 @@
// improve caching of queries.
let inputs = self.erase_regions(param_env.and(cid));
if let Some(span) = span {
- self.at(span).eval_to_const_value_raw(inputs)
+ // The query doesn't know where it is being invoked, so we need to fix the span.
+ self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span))
} else {
self.eval_to_const_value_raw(inputs)
}
@@ -162,7 +167,8 @@
let inputs = self.erase_regions(param_env.and(cid));
debug!(?inputs);
if let Some(span) = span {
- self.at(span).eval_to_valtree(inputs)
+ // The query doesn't know where it is being invoked, so we need to fix the span.
+ self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span))
} else {
self.eval_to_valtree(inputs)
}
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index c169733..85394f6 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -9,163 +9,9 @@
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size};
-use crate::{
- mir::interpret::alloc_range,
- ty::{ParamEnv, ScalarInt, Ty, TyCtxt},
-};
+use crate::ty::ScalarInt;
-use super::{
- AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
- ScalarSizeMismatch,
-};
-
-/// Represents the result of const evaluation via the `eval_to_allocation` query.
-#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
-pub struct ConstAlloc<'tcx> {
- /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
- /// (so you can use `AllocMap::unwrap_memory`).
- pub alloc_id: AllocId,
- pub ty: Ty<'tcx>,
-}
-
-/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
-/// array length computations, enum discriminants and the pattern matching logic.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
-#[derive(HashStable, Lift)]
-pub enum ConstValue<'tcx> {
- /// Used for types with `layout::abi::Scalar` ABI.
- ///
- /// Not using the enum `Value` to encode that this must not be `Uninit`.
- Scalar(Scalar),
-
- /// Only for ZSTs.
- ZeroSized,
-
- /// Used for `&[u8]` and `&str`.
- ///
- /// This is worth an optimized representation since Rust has literals of these types.
- /// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
- /// measurable performance improvements on stress tests.
- Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
-
- /// A value not representable by the other variants; needs to be stored in-memory.
- ///
- /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
- Indirect {
- /// The backing memory of the value. May contain more memory than needed for just the value
- /// if this points into some other larger ConstValue.
- ///
- /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
- /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
- /// back, we can preserve the original `AllocId`.
- alloc_id: AllocId,
- /// Offset into `alloc`
- offset: Size,
- },
-}
-
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstValue<'_>, 32);
-
-impl<'tcx> ConstValue<'tcx> {
- #[inline]
- pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
- match *self {
- ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
- ConstValue::Scalar(val) => Some(val),
- }
- }
-
- pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
- self.try_to_scalar()?.try_to_int().ok()
- }
-
- pub fn try_to_bits(&self, size: Size) -> Option<u128> {
- self.try_to_scalar_int()?.to_bits(size).ok()
- }
-
- pub fn try_to_bool(&self) -> Option<bool> {
- self.try_to_scalar_int()?.try_into().ok()
- }
-
- pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
- self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
- }
-
- pub fn try_to_bits_for_ty(
- &self,
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- ) -> Option<u128> {
- let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
- self.try_to_bits(size)
- }
-
- pub fn from_bool(b: bool) -> Self {
- ConstValue::Scalar(Scalar::from_bool(b))
- }
-
- pub fn from_u64(i: u64) -> Self {
- ConstValue::Scalar(Scalar::from_u64(i))
- }
-
- pub fn from_u128(i: u128) -> Self {
- ConstValue::Scalar(Scalar::from_u128(i))
- }
-
- pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
- ConstValue::Scalar(Scalar::from_target_usize(i, cx))
- }
-
- /// Must only be called on constants of type `&str` or `&[u8]`!
- pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
- let (data, start, end) = match self {
- ConstValue::Scalar(_) | ConstValue::ZeroSized => {
- bug!("`try_get_slice_bytes` on non-slice constant")
- }
- &ConstValue::Slice { data, start, end } => (data, start, end),
- &ConstValue::Indirect { alloc_id, offset } => {
- // The reference itself is stored behind an indirection.
- // Load the reference, and then load the actual slice contents.
- let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
- let ptr_size = tcx.data_layout.pointer_size;
- if a.size() < offset + 2 * ptr_size {
- // (partially) dangling reference
- return None;
- }
- // Read the wide pointer components.
- let ptr = a
- .read_scalar(
- &tcx,
- alloc_range(offset, ptr_size),
- /* read_provenance */ true,
- )
- .ok()?;
- let ptr = ptr.to_pointer(&tcx).ok()?;
- let len = a
- .read_scalar(
- &tcx,
- alloc_range(offset + ptr_size, ptr_size),
- /* read_provenance */ false,
- )
- .ok()?;
- let len = len.to_target_usize(&tcx).ok()?;
- let len: usize = len.try_into().ok()?;
- if len == 0 {
- return Some(&[]);
- }
- // Non-empty slice, must have memory. We know this is a relative pointer.
- let (inner_alloc_id, offset) = ptr.into_parts();
- let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
- (data, offset.bytes_usize(), offset.bytes_usize() + len)
- }
- };
-
- // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
- Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
- }
-}
+use super::{AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, ScalarSizeMismatch};
/// A `Scalar` represents an immediate, primitive value existing outside of a
/// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 5e74977..87180b5 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2,27 +2,25 @@
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
-use crate::mir::interpret::{
- AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
-};
+use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::ty::print::with_no_trimmed_paths;
+use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::visit::TypeVisitableExt;
use crate::ty::{self, List, Ty, TyCtxt};
-use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
-use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
+use crate::ty::{AdtDef, InstanceDef, UserTypeAnnotationIndex};
+use crate::ty::{GenericArg, GenericArgsRef};
use rustc_data_structures::captures::Captures;
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
use rustc_hir::{self as hir, HirId};
use rustc_session::Session;
-use rustc_target::abi::{FieldIdx, Size, VariantIdx};
+use rustc_target::abi::{FieldIdx, VariantIdx};
use polonius_engine::Atom;
pub use rustc_ast::Mutability;
@@ -39,7 +37,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
-use std::fmt::{self, Debug, Display, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
use std::ops::{Index, IndexMut};
use std::{iter, mem};
@@ -47,6 +45,7 @@
pub use basic_blocks::BasicBlocks;
mod basic_blocks;
+mod consts;
pub mod coverage;
mod generic_graph;
pub mod generic_graphviz;
@@ -57,11 +56,10 @@
pub mod pretty;
mod query;
pub mod spanview;
+mod statement;
mod syntax;
-pub use syntax::*;
pub mod tcx;
-pub mod terminator;
-pub use terminator::*;
+mod terminator;
pub mod traversal;
mod type_foldable;
@@ -72,6 +70,11 @@
pub use self::pretty::{
create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty, PassWhere,
};
+pub use consts::*;
+use pretty::pretty_print_const_value;
+pub use statement::*;
+pub use syntax::*;
+pub use terminator::*;
/// Types for locals
pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
@@ -565,6 +568,34 @@
pub fn is_custom_mir(&self) -> bool {
self.injection_phase.is_some()
}
+
+ /// *Must* be called once the full substitution for this body is known, to ensure that the body
+ /// is indeed fit for code generation or consumption more generally.
+ ///
+ /// Sadly there's no nice way to represent an "arbitrary normalizer", so we take one for
+ /// constants specifically. (`Option<GenericArgsRef>` could be used for that, but the fact
+ /// that `Instance::args_for_mir_body` is private and instead instance exposes normalization
+ /// functions makes it seem like exposing the generic args is not the intended strategy.)
+ ///
+ /// Also sadly, CTFE doesn't even know whether it runs on MIR that is already polymorphic or still monomorphic,
+ /// so we cannot just immediately ICE on TooGeneric.
+ ///
+ /// Returns Ok(()) if everything went fine, and `Err` if a problem occurred and got reported.
+ pub fn post_mono_checks(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ normalize_const: impl Fn(ConstantKind<'tcx>) -> Result<ConstantKind<'tcx>, ErrorHandled>,
+ ) -> Result<(), ErrorHandled> {
+ // For now, the only thing we have to check is is to ensure that all the constants used in
+ // the body successfully evaluate.
+ for &const_ in &self.required_consts {
+ let c = normalize_const(const_.literal)?;
+ c.eval(tcx, param_env, Some(const_.span))?;
+ }
+
+ Ok(())
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
@@ -1121,20 +1152,6 @@
pub argument_index: Option<u16>,
}
-impl Debug for VarDebugInfo<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
- pre_fmt_projection(&projection[..], fmt)?;
- write!(fmt, "({}: {})", self.name, ty)?;
- post_fmt_projection(&projection[..], fmt)?;
- } else {
- write!(fmt, "{}", self.name)?;
- }
-
- write!(fmt, " => {:?}", self.value)
- }
-}
-
///////////////////////////////////////////////////////////////////////////
// BasicBlock
@@ -1291,576 +1308,6 @@
}
}
-impl<O> AssertKind<O> {
- /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
- pub fn is_optional_overflow_check(&self) -> bool {
- use AssertKind::*;
- use BinOp::*;
- matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
- }
-
- /// Get the message that is printed at runtime when this assertion fails.
- ///
- /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
- /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
- /// instead of printing a static message.
- pub fn description(&self) -> &'static str {
- use AssertKind::*;
- match self {
- Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
- Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
- Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
- Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
- Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
- OverflowNeg(_) => "attempt to negate with overflow",
- Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
- Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
- Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
- DivisionByZero(_) => "attempt to divide by zero",
- RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
- ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
- ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
- ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
- ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
- BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
- bug!("Unexpected AssertKind")
- }
- }
- }
-
- /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
- ///
- /// Needs to be kept in sync with the run-time behavior (which is defined by
- /// `AssertKind::description` and the lang items mentioned in its docs).
- /// Note that we deliberately show more details here than we do at runtime, such as the actual
- /// numbers that overflowed -- it is much easier to do so here than at runtime.
- pub fn fmt_assert_args<W: Write>(&self, f: &mut W) -> fmt::Result
- where
- O: Debug,
- {
- use AssertKind::*;
- match self {
- BoundsCheck { ref len, ref index } => write!(
- f,
- "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
- ),
-
- OverflowNeg(op) => {
- write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
- }
- DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
- RemainderByZero(op) => write!(
- f,
- "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
- ),
- Overflow(BinOp::Add, l, r) => write!(
- f,
- "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
- ),
- Overflow(BinOp::Sub, l, r) => write!(
- f,
- "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
- ),
- Overflow(BinOp::Mul, l, r) => write!(
- f,
- "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
- ),
- Overflow(BinOp::Div, l, r) => write!(
- f,
- "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
- ),
- Overflow(BinOp::Rem, l, r) => write!(
- f,
- "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
- ),
- Overflow(BinOp::Shr, _, r) => {
- write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
- }
- Overflow(BinOp::Shl, _, r) => {
- write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
- }
- MisalignedPointerDereference { required, found } => {
- write!(
- f,
- "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
- )
- }
- _ => write!(f, "\"{}\"", self.description()),
- }
- }
-
- /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
- ///
- /// Needs to be kept in sync with the run-time behavior (which is defined by
- /// `AssertKind::description` and the lang items mentioned in its docs).
- /// Note that we deliberately show more details here than we do at runtime, such as the actual
- /// numbers that overflowed -- it is much easier to do so here than at runtime.
- pub fn diagnostic_message(&self) -> DiagnosticMessage {
- use crate::fluent_generated::*;
- use AssertKind::*;
-
- match self {
- BoundsCheck { .. } => middle_bounds_check,
- Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
- Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
- Overflow(_, _, _) => middle_assert_op_overflow,
- OverflowNeg(_) => middle_assert_overflow_neg,
- DivisionByZero(_) => middle_assert_divide_by_zero,
- RemainderByZero(_) => middle_assert_remainder_by_zero,
- ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
- ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
- ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
- ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
-
- MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
- }
- }
-
- pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
- where
- O: fmt::Debug,
- {
- use AssertKind::*;
-
- macro_rules! add {
- ($name: expr, $value: expr) => {
- adder($name.into(), $value.into_diagnostic_arg());
- };
- }
-
- match self {
- BoundsCheck { len, index } => {
- add!("len", format!("{len:?}"));
- add!("index", format!("{index:?}"));
- }
- Overflow(BinOp::Shl | BinOp::Shr, _, val)
- | DivisionByZero(val)
- | RemainderByZero(val)
- | OverflowNeg(val) => {
- add!("val", format!("{val:#?}"));
- }
- Overflow(binop, left, right) => {
- add!("op", binop.to_hir_binop().as_str());
- add!("left", format!("{left:#?}"));
- add!("right", format!("{right:#?}"));
- }
- ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
- MisalignedPointerDereference { required, found } => {
- add!("required", format!("{required:#?}"));
- add!("found", format!("{found:#?}"));
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Statements
-
-/// A statement in a basic block, including information about its source code.
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
-pub struct Statement<'tcx> {
- pub source_info: SourceInfo,
- pub kind: StatementKind<'tcx>,
-}
-
-impl Statement<'_> {
- /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
- /// invalidating statement indices in `Location`s.
- pub fn make_nop(&mut self) {
- self.kind = StatementKind::Nop
- }
-
- /// Changes a statement to a nop and returns the original statement.
- #[must_use = "If you don't need the statement, use `make_nop` instead"]
- pub fn replace_nop(&mut self) -> Self {
- Statement {
- source_info: self.source_info,
- kind: mem::replace(&mut self.kind, StatementKind::Nop),
- }
- }
-}
-
-impl Debug for Statement<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- use self::StatementKind::*;
- match self.kind {
- Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
- FakeRead(box (ref cause, ref place)) => {
- write!(fmt, "FakeRead({cause:?}, {place:?})")
- }
- Retag(ref kind, ref place) => write!(
- fmt,
- "Retag({}{:?})",
- match kind {
- RetagKind::FnEntry => "[fn entry] ",
- RetagKind::TwoPhase => "[2phase] ",
- RetagKind::Raw => "[raw] ",
- RetagKind::Default => "",
- },
- place,
- ),
- StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
- StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
- SetDiscriminant { ref place, variant_index } => {
- write!(fmt, "discriminant({place:?}) = {variant_index:?}")
- }
- Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
- PlaceMention(ref place) => {
- write!(fmt, "PlaceMention({place:?})")
- }
- AscribeUserType(box (ref place, ref c_ty), ref variance) => {
- write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
- }
- Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
- write!(fmt, "Coverage::{kind:?} for {rgn:?}")
- }
- Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
- Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
- ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
- Nop => write!(fmt, "nop"),
- }
- }
-}
-
-impl<'tcx> StatementKind<'tcx> {
- pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
- match self {
- StatementKind::Assign(x) => Some(x),
- _ => None,
- }
- }
-
- pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
- match self {
- StatementKind::Assign(x) => Some(x),
- _ => None,
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Places
-
-impl<V, T> ProjectionElem<V, T> {
- /// Returns `true` if the target of this projection may refer to a different region of memory
- /// than the base.
- fn is_indirect(&self) -> bool {
- match self {
- Self::Deref => true,
-
- Self::Field(_, _)
- | Self::Index(_)
- | Self::OpaqueCast(_)
- | Self::ConstantIndex { .. }
- | Self::Subslice { .. }
- | Self::Downcast(_, _) => false,
- }
- }
-
- /// Returns `true` if the target of this projection always refers to the same memory region
- /// whatever the state of the program.
- pub fn is_stable_offset(&self) -> bool {
- match self {
- Self::Deref | Self::Index(_) => false,
- Self::Field(_, _)
- | Self::OpaqueCast(_)
- | Self::ConstantIndex { .. }
- | Self::Subslice { .. }
- | Self::Downcast(_, _) => true,
- }
- }
-
- /// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
- pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
- matches!(*self, Self::Downcast(_, x) if x == v)
- }
-
- /// Returns `true` if this is a `Field` projection with the given index.
- pub fn is_field_to(&self, f: FieldIdx) -> bool {
- matches!(*self, Self::Field(x, _) if x == f)
- }
-
- /// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
- pub fn can_use_in_debuginfo(&self) -> bool {
- match self {
- Self::ConstantIndex { from_end: false, .. }
- | Self::Deref
- | Self::Downcast(_, _)
- | Self::Field(_, _) => true,
- Self::ConstantIndex { from_end: true, .. }
- | Self::Index(_)
- | Self::OpaqueCast(_)
- | Self::Subslice { .. } => false,
- }
- }
-}
-
-/// Alias for projections as they appear in `UserTypeProjection`, where we
-/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
-pub type ProjectionKind = ProjectionElem<(), ()>;
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct PlaceRef<'tcx> {
- pub local: Local,
- pub projection: &'tcx [PlaceElem<'tcx>],
-}
-
-// Once we stop implementing `Ord` for `DefId`,
-// this impl will be unnecessary. Until then, we'll
-// leave this impl in place to prevent re-adding a
-// dependency on the `Ord` impl for `DefId`
-impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
-
-impl<'tcx> Place<'tcx> {
- // FIXME change this to a const fn by also making List::empty a const fn.
- pub fn return_place() -> Place<'tcx> {
- Place { local: RETURN_PLACE, projection: List::empty() }
- }
-
- /// Returns `true` if this `Place` contains a `Deref` projection.
- ///
- /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
- /// same region of memory as its base.
- pub fn is_indirect(&self) -> bool {
- self.projection.iter().any(|elem| elem.is_indirect())
- }
-
- /// Returns `true` if this `Place`'s first projection is `Deref`.
- ///
- /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
- /// `Deref` projections can only occur as the first projection. In that case this method
- /// is equivalent to `is_indirect`, but faster.
- pub fn is_indirect_first_projection(&self) -> bool {
- self.as_ref().is_indirect_first_projection()
- }
-
- /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
- /// a single deref of a local.
- #[inline(always)]
- pub fn local_or_deref_local(&self) -> Option<Local> {
- self.as_ref().local_or_deref_local()
- }
-
- /// If this place represents a local variable like `_X` with no
- /// projections, return `Some(_X)`.
- #[inline(always)]
- pub fn as_local(&self) -> Option<Local> {
- self.as_ref().as_local()
- }
-
- #[inline]
- pub fn as_ref(&self) -> PlaceRef<'tcx> {
- PlaceRef { local: self.local, projection: &self.projection }
- }
-
- /// Iterate over the projections in evaluation order, i.e., the first element is the base with
- /// its projection and then subsequently more projections are added.
- /// As a concrete example, given the place a.b.c, this would yield:
- /// - (a, .b)
- /// - (a.b, .c)
- ///
- /// Given a place without projections, the iterator is empty.
- #[inline]
- pub fn iter_projections(
- self,
- ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
- self.as_ref().iter_projections()
- }
-
- /// Generates a new place by appending `more_projections` to the existing ones
- /// and interning the result.
- pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
- if more_projections.is_empty() {
- return self;
- }
-
- self.as_ref().project_deeper(more_projections, tcx)
- }
-}
-
-impl From<Local> for Place<'_> {
- #[inline]
- fn from(local: Local) -> Self {
- Place { local, projection: List::empty() }
- }
-}
-
-impl<'tcx> PlaceRef<'tcx> {
- /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
- /// a single deref of a local.
- pub fn local_or_deref_local(&self) -> Option<Local> {
- match *self {
- PlaceRef { local, projection: [] }
- | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
- _ => None,
- }
- }
-
- /// Returns `true` if this `Place` contains a `Deref` projection.
- ///
- /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
- /// same region of memory as its base.
- pub fn is_indirect(&self) -> bool {
- self.projection.iter().any(|elem| elem.is_indirect())
- }
-
- /// Returns `true` if this `Place`'s first projection is `Deref`.
- ///
- /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
- /// `Deref` projections can only occur as the first projection. In that case this method
- /// is equivalent to `is_indirect`, but faster.
- pub fn is_indirect_first_projection(&self) -> bool {
- // To make sure this is not accidentally used in wrong mir phase
- debug_assert!(
- self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
- );
- self.projection.first() == Some(&PlaceElem::Deref)
- }
-
- /// If this place represents a local variable like `_X` with no
- /// projections, return `Some(_X)`.
- #[inline]
- pub fn as_local(&self) -> Option<Local> {
- match *self {
- PlaceRef { local, projection: [] } => Some(local),
- _ => None,
- }
- }
-
- #[inline]
- pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
- if let &[ref proj_base @ .., elem] = self.projection {
- Some((PlaceRef { local: self.local, projection: proj_base }, elem))
- } else {
- None
- }
- }
-
- /// Iterate over the projections in evaluation order, i.e., the first element is the base with
- /// its projection and then subsequently more projections are added.
- /// As a concrete example, given the place a.b.c, this would yield:
- /// - (a, .b)
- /// - (a.b, .c)
- ///
- /// Given a place without projections, the iterator is empty.
- #[inline]
- pub fn iter_projections(
- self,
- ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
- self.projection.iter().enumerate().map(move |(i, proj)| {
- let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
- (base, *proj)
- })
- }
-
- /// Generates a new place by appending `more_projections` to the existing ones
- /// and interning the result.
- pub fn project_deeper(
- self,
- more_projections: &[PlaceElem<'tcx>],
- tcx: TyCtxt<'tcx>,
- ) -> Place<'tcx> {
- let mut v: Vec<PlaceElem<'tcx>>;
-
- let new_projections = if self.projection.is_empty() {
- more_projections
- } else {
- v = Vec::with_capacity(self.projection.len() + more_projections.len());
- v.extend(self.projection);
- v.extend(more_projections);
- &v
- };
-
- Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
- }
-}
-
-impl From<Local> for PlaceRef<'_> {
- #[inline]
- fn from(local: Local) -> Self {
- PlaceRef { local, projection: &[] }
- }
-}
-
-impl Debug for Place<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- self.as_ref().fmt(fmt)
- }
-}
-
-impl Debug for PlaceRef<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- pre_fmt_projection(self.projection, fmt)?;
- write!(fmt, "{:?}", self.local)?;
- post_fmt_projection(self.projection, fmt)
- }
-}
-
-fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
- for &elem in projection.iter().rev() {
- match elem {
- ProjectionElem::OpaqueCast(_)
- | ProjectionElem::Downcast(_, _)
- | ProjectionElem::Field(_, _) => {
- write!(fmt, "(").unwrap();
- }
- ProjectionElem::Deref => {
- write!(fmt, "(*").unwrap();
- }
- ProjectionElem::Index(_)
- | ProjectionElem::ConstantIndex { .. }
- | ProjectionElem::Subslice { .. } => {}
- }
- }
-
- Ok(())
-}
-
-fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
- for &elem in projection.iter() {
- match elem {
- ProjectionElem::OpaqueCast(ty) => {
- write!(fmt, " as {ty})")?;
- }
- ProjectionElem::Downcast(Some(name), _index) => {
- write!(fmt, " as {name})")?;
- }
- ProjectionElem::Downcast(None, index) => {
- write!(fmt, " as variant#{index:?})")?;
- }
- ProjectionElem::Deref => {
- write!(fmt, ")")?;
- }
- ProjectionElem::Field(field, ty) => {
- with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
- }
- ProjectionElem::Index(ref index) => {
- write!(fmt, "[{index:?}]")?;
- }
- ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
- write!(fmt, "[{offset:?} of {min_length:?}]")?;
- }
- ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
- write!(fmt, "[-{offset:?} of {min_length:?}]")?;
- }
- ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
- write!(fmt, "[{from:?}:]")?;
- }
- ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
- write!(fmt, "[:-{to:?}]")?;
- }
- ProjectionElem::Subslice { from, to, from_end: true } => {
- write!(fmt, "[{from:?}:-{to:?}]")?;
- }
- ProjectionElem::Subslice { from, to, from_end: false } => {
- write!(fmt, "[{from:?}..{to:?}]")?;
- }
- }
- }
-
- Ok(())
-}
-
///////////////////////////////////////////////////////////////////////////
// Scopes
@@ -1939,716 +1386,12 @@
pub safety: Safety,
}
-///////////////////////////////////////////////////////////////////////////
-// Operands
-
-impl<'tcx> Debug for Operand<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- use self::Operand::*;
- match *self {
- Constant(ref a) => write!(fmt, "{a:?}"),
- Copy(ref place) => write!(fmt, "{place:?}"),
- Move(ref place) => write!(fmt, "move {place:?}"),
- }
- }
-}
-
-impl<'tcx> Operand<'tcx> {
- /// Convenience helper to make a constant that refers to the fn
- /// with given `DefId` and args. Since this is used to synthesize
- /// MIR, assumes `user_ty` is None.
- pub fn function_handle(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
- args: impl IntoIterator<Item = GenericArg<'tcx>>,
- span: Span,
- ) -> Self {
- let ty = Ty::new_fn_def(tcx, def_id, args);
- Operand::Constant(Box::new(Constant {
- span,
- user_ty: None,
- literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
- }))
- }
-
- pub fn is_move(&self) -> bool {
- matches!(self, Operand::Move(..))
- }
-
- /// Convenience helper to make a literal-like constant from a given scalar value.
- /// Since this is used to synthesize MIR, assumes `user_ty` is None.
- pub fn const_from_scalar(
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
- val: Scalar,
- span: Span,
- ) -> Operand<'tcx> {
- debug_assert!({
- let param_env_and_ty = ty::ParamEnv::empty().and(ty);
- let type_size = tcx
- .layout_of(param_env_and_ty)
- .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
- .size;
- let scalar_size = match val {
- Scalar::Int(int) => int.size(),
- _ => panic!("Invalid scalar type {val:?}"),
- };
- scalar_size == type_size
- });
- Operand::Constant(Box::new(Constant {
- span,
- user_ty: None,
- literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
- }))
- }
-
- pub fn to_copy(&self) -> Self {
- match *self {
- Operand::Copy(_) | Operand::Constant(_) => self.clone(),
- Operand::Move(place) => Operand::Copy(place),
- }
- }
-
- /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
- /// constant.
- pub fn place(&self) -> Option<Place<'tcx>> {
- match self {
- Operand::Copy(place) | Operand::Move(place) => Some(*place),
- Operand::Constant(_) => None,
- }
- }
-
- /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
- /// place.
- pub fn constant(&self) -> Option<&Constant<'tcx>> {
- match self {
- Operand::Constant(x) => Some(&**x),
- Operand::Copy(_) | Operand::Move(_) => None,
- }
- }
-
- /// Gets the `ty::FnDef` from an operand if it's a constant function item.
- ///
- /// While this is unlikely in general, it's the normal case of what you'll
- /// find as the `func` in a [`TerminatorKind::Call`].
- pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
- let const_ty = self.constant()?.literal.ty();
- if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// Rvalues
-
-impl<'tcx> Rvalue<'tcx> {
- /// Returns true if rvalue can be safely removed when the result is unused.
- #[inline]
- pub fn is_safe_to_remove(&self) -> bool {
- match self {
- // Pointer to int casts may be side-effects due to exposing the provenance.
- // While the model is undecided, we should be conservative. See
- // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
- Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
-
- Rvalue::Use(_)
- | Rvalue::CopyForDeref(_)
- | Rvalue::Repeat(_, _)
- | Rvalue::Ref(_, _, _)
- | Rvalue::ThreadLocalRef(_)
- | Rvalue::AddressOf(_, _)
- | Rvalue::Len(_)
- | Rvalue::Cast(
- CastKind::IntToInt
- | CastKind::FloatToInt
- | CastKind::FloatToFloat
- | CastKind::IntToFloat
- | CastKind::FnPtrToPtr
- | CastKind::PtrToPtr
- | CastKind::PointerCoercion(_)
- | CastKind::PointerFromExposedAddress
- | CastKind::DynStar
- | CastKind::Transmute,
- _,
- _,
- )
- | Rvalue::BinaryOp(_, _)
- | Rvalue::CheckedBinaryOp(_, _)
- | Rvalue::NullaryOp(_, _)
- | Rvalue::UnaryOp(_, _)
- | Rvalue::Discriminant(_)
- | Rvalue::Aggregate(_, _)
- | Rvalue::ShallowInitBox(_, _) => true,
- }
- }
-}
-
-impl BorrowKind {
- pub fn mutability(&self) -> Mutability {
- match *self {
- BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
- BorrowKind::Mut { .. } => Mutability::Mut,
- }
- }
-
- pub fn allows_two_phase_borrow(&self) -> bool {
- match *self {
- BorrowKind::Shared
- | BorrowKind::Shallow
- | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
- false
- }
- BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
- }
- }
-}
-
-impl<'tcx> Debug for Rvalue<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- use self::Rvalue::*;
-
- match *self {
- Use(ref place) => write!(fmt, "{place:?}"),
- Repeat(ref a, b) => {
- write!(fmt, "[{a:?}; ")?;
- pretty_print_const(b, fmt, false)?;
- write!(fmt, "]")
- }
- Len(ref a) => write!(fmt, "Len({a:?})"),
- Cast(ref kind, ref place, ref ty) => {
- with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
- }
- BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
- CheckedBinaryOp(ref op, box (ref a, ref b)) => {
- write!(fmt, "Checked{op:?}({a:?}, {b:?})")
- }
- UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
- Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
- NullaryOp(ref op, ref t) => {
- let t = with_no_trimmed_paths!(format!("{}", t));
- match op {
- NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
- NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
- NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
- }
- }
- ThreadLocalRef(did) => ty::tls::with(|tcx| {
- let muta = tcx.static_mutability(did).unwrap().prefix_str();
- write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
- }),
- Ref(region, borrow_kind, ref place) => {
- let kind_str = match borrow_kind {
- BorrowKind::Shared => "",
- BorrowKind::Shallow => "shallow ",
- BorrowKind::Mut { .. } => "mut ",
- };
-
- // When printing regions, add trailing space if necessary.
- let print_region = ty::tls::with(|tcx| {
- tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
- });
- let region = if print_region {
- let mut region = region.to_string();
- if !region.is_empty() {
- region.push(' ');
- }
- region
- } else {
- // Do not even print 'static
- String::new()
- };
- write!(fmt, "&{region}{kind_str}{place:?}")
- }
-
- CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
-
- AddressOf(mutability, ref place) => {
- let kind_str = match mutability {
- Mutability::Mut => "mut",
- Mutability::Not => "const",
- };
-
- write!(fmt, "&raw {kind_str} {place:?}")
- }
-
- Aggregate(ref kind, ref places) => {
- let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
- let mut tuple_fmt = fmt.debug_tuple(name);
- for place in places {
- tuple_fmt.field(place);
- }
- tuple_fmt.finish()
- };
-
- match **kind {
- AggregateKind::Array(_) => write!(fmt, "{places:?}"),
-
- AggregateKind::Tuple => {
- if places.is_empty() {
- write!(fmt, "()")
- } else {
- fmt_tuple(fmt, "")
- }
- }
-
- AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
- ty::tls::with(|tcx| {
- let variant_def = &tcx.adt_def(adt_did).variant(variant);
- let args = tcx.lift(args).expect("could not lift for printing");
- let name = FmtPrinter::new(tcx, Namespace::ValueNS)
- .print_def_path(variant_def.def_id, args)?
- .into_buffer();
-
- match variant_def.ctor_kind() {
- Some(CtorKind::Const) => fmt.write_str(&name),
- Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
- None => {
- let mut struct_fmt = fmt.debug_struct(&name);
- for (field, place) in iter::zip(&variant_def.fields, places) {
- struct_fmt.field(field.name.as_str(), place);
- }
- struct_fmt.finish()
- }
- }
- })
- }
-
- AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
- let name = if tcx.sess.opts.unstable_opts.span_free_formats {
- let args = tcx.lift(args).unwrap();
- format!("[closure@{}]", tcx.def_path_str_with_args(def_id, args),)
- } else {
- let span = tcx.def_span(def_id);
- format!(
- "[closure@{}]",
- tcx.sess.source_map().span_to_diagnostic_string(span)
- )
- };
- let mut struct_fmt = fmt.debug_struct(&name);
-
- // FIXME(project-rfc-2229#48): This should be a list of capture names/places
- if let Some(def_id) = def_id.as_local()
- && let Some(upvars) = tcx.upvars_mentioned(def_id)
- {
- for (&var_id, place) in iter::zip(upvars.keys(), places) {
- let var_name = tcx.hir().name(var_id);
- struct_fmt.field(var_name.as_str(), place);
- }
- } else {
- for (index, place) in places.iter().enumerate() {
- struct_fmt.field(&format!("{index}"), place);
- }
- }
-
- struct_fmt.finish()
- }),
-
- AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
- let name = format!("[generator@{:?}]", tcx.def_span(def_id));
- let mut struct_fmt = fmt.debug_struct(&name);
-
- // FIXME(project-rfc-2229#48): This should be a list of capture names/places
- if let Some(def_id) = def_id.as_local()
- && let Some(upvars) = tcx.upvars_mentioned(def_id)
- {
- for (&var_id, place) in iter::zip(upvars.keys(), places) {
- let var_name = tcx.hir().name(var_id);
- struct_fmt.field(var_name.as_str(), place);
- }
- } else {
- for (index, place) in places.iter().enumerate() {
- struct_fmt.field(&format!("{index}"), place);
- }
- }
-
- struct_fmt.finish()
- }),
- }
- }
-
- ShallowInitBox(ref place, ref ty) => {
- with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// Constants
-///
-/// Two constants are equal if they are the same constant. Note that
-/// this does not necessarily mean that they are `==` in Rust. In
-/// particular, one must be wary of `NaN`!
-
-#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct Constant<'tcx> {
- pub span: Span,
-
- /// Optional user-given type: for something like
- /// `collect::<Vec<_>>`, this would be present and would
- /// indicate that `Vec<_>` was explicitly specified.
- ///
- /// Needed for NLL to impose user-given type constraints.
- pub user_ty: Option<UserTypeAnnotationIndex>,
-
- pub literal: ConstantKind<'tcx>,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub enum ConstantKind<'tcx> {
- /// This constant came from the type system.
- ///
- /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
- /// this ensures that we consistently produce "clean" values without data in the padding or
- /// anything like that.
- Ty(ty::Const<'tcx>),
-
- /// An unevaluated mir constant which is not part of the type system.
- Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
-
- /// This constant cannot go back into the type system, as it represents
- /// something the type system cannot handle (e.g. pointers).
- Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
-}
-
-impl<'tcx> Constant<'tcx> {
- pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
- match self.literal.try_to_scalar() {
- Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
- GlobalAlloc::Static(def_id) => {
- assert!(!tcx.is_thread_local_static(def_id));
- Some(def_id)
- }
- _ => None,
- },
- _ => None,
- }
- }
- #[inline]
- pub fn ty(&self) -> Ty<'tcx> {
- self.literal.ty()
- }
-}
-
-impl<'tcx> ConstantKind<'tcx> {
- #[inline(always)]
- pub fn ty(&self) -> Ty<'tcx> {
- match self {
- ConstantKind::Ty(c) => c.ty(),
- ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => *ty,
- }
- }
-
- #[inline]
- pub fn try_to_scalar(self) -> Option<Scalar> {
- match self {
- ConstantKind::Ty(c) => match c.kind() {
- ty::ConstKind::Value(valtree) => match valtree {
- ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
- ty::ValTree::Branch(_) => None,
- },
- _ => None,
- },
- ConstantKind::Val(val, _) => val.try_to_scalar(),
- ConstantKind::Unevaluated(..) => None,
- }
- }
-
- #[inline]
- pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
- self.try_to_scalar()?.try_to_int().ok()
- }
-
- #[inline]
- pub fn try_to_bits(self, size: Size) -> Option<u128> {
- self.try_to_scalar_int()?.to_bits(size).ok()
- }
-
- #[inline]
- pub fn try_to_bool(self) -> Option<bool> {
- self.try_to_scalar_int()?.try_into().ok()
- }
-
- #[inline]
- pub fn eval(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- span: Option<Span>,
- ) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
- match self {
- ConstantKind::Ty(c) => {
- // We want to consistently have a "clean" value for type system constants (i.e., no
- // data hidden in the padding), so we always go through a valtree here.
- let val = c.eval(tcx, param_env, span)?;
- Ok(tcx.valtree_to_const_val((self.ty(), val)))
- }
- ConstantKind::Unevaluated(uneval, _) => {
- // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
- tcx.const_eval_resolve(param_env, uneval, span)
- }
- ConstantKind::Val(val, _) => Ok(val),
- }
- }
-
- /// Normalizes the constant to a value or an error if possible.
- #[inline]
- pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
- match self.eval(tcx, param_env, None) {
- Ok(val) => Self::Val(val, self.ty()),
- Err(ErrorHandled::Reported(guar)) => {
- Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
- }
- Err(ErrorHandled::TooGeneric) => self,
- }
- }
-
- #[inline]
- pub fn try_eval_scalar(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Option<Scalar> {
- self.eval(tcx, param_env, None).ok()?.try_to_scalar()
- }
-
- #[inline]
- pub fn try_eval_scalar_int(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Option<ScalarInt> {
- self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
- }
-
- #[inline]
- pub fn try_eval_bits(
- &self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- ) -> Option<u128> {
- let int = self.try_eval_scalar_int(tcx, param_env)?;
- assert_eq!(self.ty(), ty);
- let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
- int.to_bits(size).ok()
- }
-
- /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
- #[inline]
- pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
- self.try_eval_bits(tcx, param_env, ty)
- .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
- }
-
- #[inline]
- pub fn try_eval_target_usize(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Option<u64> {
- self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
- }
-
- #[inline]
- /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
- pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
- self.try_eval_target_usize(tcx, param_env)
- .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
- }
-
- #[inline]
- pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
- self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
- }
-
- #[inline]
- pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
- Self::Val(val, ty)
- }
-
- pub fn from_bits(
- tcx: TyCtxt<'tcx>,
- bits: u128,
- param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
- ) -> Self {
- let size = tcx
- .layout_of(param_env_ty)
- .unwrap_or_else(|e| {
- bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
- })
- .size;
- let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
-
- Self::Val(cv, param_env_ty.value)
- }
-
- #[inline]
- pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
- let cv = ConstValue::from_bool(v);
- Self::Val(cv, tcx.types.bool)
- }
-
- #[inline]
- pub fn zero_sized(ty: Ty<'tcx>) -> Self {
- let cv = ConstValue::ZeroSized;
- Self::Val(cv, ty)
- }
-
- pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
- let ty = tcx.types.usize;
- Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
- }
-
- #[inline]
- pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
- let val = ConstValue::Scalar(s);
- Self::Val(val, ty)
- }
-
- /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
- /// converted to a constant, everything else becomes `Unevaluated`.
- #[instrument(skip(tcx), level = "debug", ret)]
- pub fn from_anon_const(
- tcx: TyCtxt<'tcx>,
- def: LocalDefId,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Self {
- let body_id = match tcx.hir().get_by_def_id(def) {
- hir::Node::AnonConst(ac) => ac.body,
- _ => {
- span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
- }
- };
-
- let expr = &tcx.hir().body(body_id).value;
- debug!(?expr);
-
- // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
- // currently have to be wrapped in curly brackets, so it's necessary to special-case.
- let expr = match &expr.kind {
- hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
- block.expr.as_ref().unwrap()
- }
- _ => expr,
- };
- debug!("expr.kind: {:?}", expr.kind);
-
- let ty = tcx.type_of(def).instantiate_identity();
- debug!(?ty);
-
- // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
- // does not provide the parents generics to anonymous constants. We still allow generic const
- // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
- // ever try to substitute the generic parameters in their bodies.
- //
- // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
- // cause issues if we were to remove that special-case and try to evaluate the constant instead.
- use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
- match expr.kind {
- ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
- // Find the name and index of the const parameter by indexing the generics of
- // the parent item and construct a `ParamConst`.
- let item_def_id = tcx.parent(def_id);
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
- let name = tcx.item_name(def_id);
- let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
- debug!(?ty_const);
-
- return Self::Ty(ty_const);
- }
- _ => {}
- }
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def);
- let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
- && let Some(parent_did) = parent_hir_id.as_owner()
- {
- GenericArgs::identity_for_item(tcx, parent_did)
- } else {
- List::empty()
- };
- debug!(?parent_args);
-
- let did = def.to_def_id();
- let child_args = GenericArgs::identity_for_item(tcx, did);
- let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
- debug!(?args);
-
- let span = tcx.def_span(def);
- let uneval = UnevaluatedConst::new(did, args);
- debug!(?span, ?param_env);
-
- match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
- Ok(val) => {
- debug!("evaluated const value");
- Self::Val(val, ty)
- }
- Err(_) => {
- debug!("error encountered during evaluation");
- // Error was handled in `const_eval_resolve`. Here we just create a
- // new unevaluated const and error hard later in codegen
- Self::Unevaluated(
- UnevaluatedConst {
- def: did,
- args: GenericArgs::identity_for_item(tcx, did),
- promoted: None,
- },
- ty,
- )
- }
- }
- }
-
- pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
- match c.kind() {
- ty::ConstKind::Value(valtree) => {
- // Make sure that if `c` is normalized, then the return value is normalized.
- let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
- Self::Val(const_val, c.ty())
- }
- _ => Self::Ty(c),
- }
- }
-}
-
-/// An unevaluated (potentially generic) constant used in MIR.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
-#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
-pub struct UnevaluatedConst<'tcx> {
- pub def: DefId,
- pub args: GenericArgsRef<'tcx>,
- pub promoted: Option<Promoted>,
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
- #[inline]
- pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
- assert_eq!(self.promoted, None);
- ty::UnevaluatedConst { def: self.def, args: self.args }
- }
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
- #[inline]
- pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
- UnevaluatedConst { def, args, promoted: Default::default() }
- }
-
- #[inline]
- pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
- UnevaluatedConst::new(instance.def_id(), instance.args)
- }
-}
-
/// A collection of projections into user types.
///
/// They are projections because a binding can occur a part of a
/// parent pattern that has been ascribed a type.
///
-/// Its a collection because there can be multiple type ascriptions on
+/// It's a collection because there can be multiple type ascriptions on
/// the path from the root of the pattern down to the binding itself.
///
/// An example:
@@ -2802,206 +1545,6 @@
pub struct Promoted {}
}
-impl<'tcx> Debug for Constant<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- write!(fmt, "{self}")
- }
-}
-
-impl<'tcx> Display for Constant<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- match self.ty().kind() {
- ty::FnDef(..) => {}
- _ => write!(fmt, "const ")?,
- }
- Display::fmt(&self.literal, fmt)
- }
-}
-
-impl<'tcx> Display for ConstantKind<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
- ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
- // FIXME(valtrees): Correctly print mir constants.
- ConstantKind::Unevaluated(..) => {
- fmt.write_str("_")?;
- Ok(())
- }
- }
- }
-}
-
-fn pretty_print_const<'tcx>(
- c: ty::Const<'tcx>,
- fmt: &mut Formatter<'_>,
- print_types: bool,
-) -> fmt::Result {
- use crate::ty::print::PrettyPrinter;
- ty::tls::with(|tcx| {
- let literal = tcx.lift(c).unwrap();
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.pretty_print_const(literal, print_types)?;
- fmt.write_str(&cx.into_buffer())?;
- Ok(())
- })
-}
-
-fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
- write!(fmt, "b\"{}\"", byte_str.escape_ascii())
-}
-
-fn comma_sep<'tcx>(
- fmt: &mut Formatter<'_>,
- elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
-) -> fmt::Result {
- let mut first = true;
- for (ct, ty) in elems {
- if !first {
- fmt.write_str(", ")?;
- }
- pretty_print_const_value(ct, ty, fmt)?;
- first = false;
- }
- Ok(())
-}
-
-// FIXME: Move that into `mir/pretty.rs`.
-fn pretty_print_const_value<'tcx>(
- ct: ConstValue<'tcx>,
- ty: Ty<'tcx>,
- fmt: &mut Formatter<'_>,
-) -> fmt::Result {
- use crate::ty::print::PrettyPrinter;
-
- ty::tls::with(|tcx| {
- let ct = tcx.lift(ct).unwrap();
- let ty = tcx.lift(ty).unwrap();
-
- if tcx.sess.verbose() {
- fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
- return Ok(());
- }
-
- let u8_type = tcx.types.u8;
- match (ct, ty.kind()) {
- // Byte/string slices, printed as (byte) string literals.
- (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
- if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
- fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
- return Ok(());
- }
- }
- (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
- if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
- pretty_print_byte_str(fmt, data)?;
- return Ok(());
- }
- }
- (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
- let n = n.try_to_target_usize(tcx).unwrap();
- let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
- // cast is ok because we already checked for pointer size (32 or 64 bit) above
- let range = AllocRange { start: offset, size: Size::from_bytes(n) };
- let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
- fmt.write_str("*")?;
- pretty_print_byte_str(fmt, byte_str)?;
- return Ok(());
- }
- // Aggregates, printed as array/tuple/struct/variant construction syntax.
- //
- // NB: the `has_non_region_param` check ensures that we can use
- // the `destructure_const` query with an empty `ty::ParamEnv` without
- // introducing ICEs (e.g. via `layout_of`) from missing bounds.
- // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
- // to be able to destructure the tuple into `(0u8, *mut T)`
- (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
- let ct = tcx.lift(ct).unwrap();
- let ty = tcx.lift(ty).unwrap();
- if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
- let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
- match *ty.kind() {
- ty::Array(..) => {
- fmt.write_str("[")?;
- comma_sep(fmt, fields)?;
- fmt.write_str("]")?;
- }
- ty::Tuple(..) => {
- fmt.write_str("(")?;
- comma_sep(fmt, fields)?;
- if contents.fields.len() == 1 {
- fmt.write_str(",")?;
- }
- fmt.write_str(")")?;
- }
- ty::Adt(def, _) if def.variants().is_empty() => {
- fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
- }
- ty::Adt(def, args) => {
- let variant_idx = contents
- .variant
- .expect("destructed mir constant of adt without variant idx");
- let variant_def = &def.variant(variant_idx);
- let args = tcx.lift(args).unwrap();
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.print_value_path(variant_def.def_id, args)?;
- fmt.write_str(&cx.into_buffer())?;
-
- match variant_def.ctor_kind() {
- Some(CtorKind::Const) => {}
- Some(CtorKind::Fn) => {
- fmt.write_str("(")?;
- comma_sep(fmt, fields)?;
- fmt.write_str(")")?;
- }
- None => {
- fmt.write_str(" {{ ")?;
- let mut first = true;
- for (field_def, (ct, ty)) in
- iter::zip(&variant_def.fields, fields)
- {
- if !first {
- fmt.write_str(", ")?;
- }
- write!(fmt, "{}: ", field_def.name)?;
- pretty_print_const_value(ct, ty, fmt)?;
- first = false;
- }
- fmt.write_str(" }}")?;
- }
- }
- }
- _ => unreachable!(),
- }
- return Ok(());
- }
- }
- (ConstValue::Scalar(scalar), _) => {
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let ty = tcx.lift(ty).unwrap();
- cx = cx.pretty_print_const_scalar(scalar, ty)?;
- fmt.write_str(&cx.into_buffer())?;
- return Ok(());
- }
- (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.print_value_path(*d, s)?;
- fmt.write_str(&cx.into_buffer())?;
- return Ok(());
- }
- // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
- // their fields instead of just dumping the memory.
- _ => {}
- }
- // Fall back to debug pretty printing for invalid constants.
- write!(fmt, "{ct:?}: {ty}")
- })
-}
-
/// `Location` represents the position of the start of the statement; or, if
/// `statement_index` equals the number of statements, then the start of the
/// terminator.
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index c4fae27..632f159 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1,19 +1,19 @@
use std::collections::BTreeSet;
-use std::fmt::Display;
-use std::fmt::Write as _;
+use std::fmt::{self, Debug, Display, Write as _};
use std::fs;
-use std::io::{self, Write};
+use std::io::{self, Write as _};
use std::path::{Path, PathBuf};
use super::graphviz::write_mir_fn_graphviz;
use super::spanview::write_mir_fn_spanview;
use either::Either;
+use rustc_ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_index::Idx;
use rustc_middle::mir::interpret::{
- alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, ConstValue,
- GlobalAlloc, Pointer, Provenance,
+ alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc,
+ Pointer, Provenance,
};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
@@ -79,7 +79,7 @@
body: &Body<'tcx>,
extra_data: F,
) where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
if !dump_enabled(tcx, pass_name, body.source.def_id()) {
return;
@@ -116,7 +116,7 @@
body: &Body<'tcx>,
mut extra_data: F,
) where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
@@ -260,11 +260,14 @@
)
}
+///////////////////////////////////////////////////////////////////////////
+// Whole MIR bodies
+
/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'tcx>(
tcx: TyCtxt<'tcx>,
single: Option<DefId>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()> {
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -278,7 +281,7 @@
writeln!(w)?;
}
- let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
+ let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
for body in tcx.promoted_mir(def_id) {
@@ -309,10 +312,10 @@
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
extra_data: &mut F,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()>
where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
write_mir_intro(tcx, body, w)?;
for block in body.basic_blocks.indices() {
@@ -330,218 +333,12 @@
Ok(())
}
-/// Write out a human-readable textual representation for the given basic block.
-pub fn write_basic_block<'tcx, F>(
- tcx: TyCtxt<'tcx>,
- block: BasicBlock,
- body: &Body<'tcx>,
- extra_data: &mut F,
- w: &mut dyn Write,
-) -> io::Result<()>
-where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
-{
- let data = &body[block];
-
- // Basic block label at the top.
- let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
- writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
-
- // List of statements in the middle.
- let mut current_location = Location { block, statement_index: 0 };
- for statement in &data.statements {
- extra_data(PassWhere::BeforeLocation(current_location), w)?;
- let indented_body = format!("{INDENT}{INDENT}{statement:?};");
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- writeln!(
- w,
- "{:A$} // {}{}",
- indented_body,
- if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
- comment(tcx, statement.source_info),
- A = ALIGN,
- )?;
- } else {
- writeln!(w, "{indented_body}")?;
- }
-
- write_extra(tcx, w, |visitor| {
- visitor.visit_statement(statement, current_location);
- })?;
-
- extra_data(PassWhere::AfterLocation(current_location), w)?;
-
- current_location.statement_index += 1;
- }
-
- // Terminator at the bottom.
- extra_data(PassWhere::BeforeLocation(current_location), w)?;
- let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- writeln!(
- w,
- "{:A$} // {}{}",
- indented_terminator,
- if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
- comment(tcx, data.terminator().source_info),
- A = ALIGN,
- )?;
- } else {
- writeln!(w, "{indented_terminator}")?;
- }
-
- write_extra(tcx, w, |visitor| {
- visitor.visit_terminator(data.terminator(), current_location);
- })?;
-
- extra_data(PassWhere::AfterLocation(current_location), w)?;
- extra_data(PassWhere::AfterTerminator(block), w)?;
-
- writeln!(w, "{INDENT}}}")
-}
-
-/// After we print the main statement, we sometimes dump extra
-/// information. There's often a lot of little things "nuzzled up" in
-/// a statement.
-fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
-where
- F: FnMut(&mut ExtraComments<'tcx>),
-{
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- let mut extra_comments = ExtraComments { tcx, comments: vec![] };
- visit_op(&mut extra_comments);
- for comment in extra_comments.comments {
- writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
- }
- }
- Ok(())
-}
-
-struct ExtraComments<'tcx> {
- tcx: TyCtxt<'tcx>,
- comments: Vec<String>,
-}
-
-impl<'tcx> ExtraComments<'tcx> {
- fn push(&mut self, lines: &str) {
- for line in lines.split('\n') {
- self.comments.push(line.to_string());
- }
- }
-}
-
-fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
- match *ty.kind() {
- ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
- // Unit type
- ty::Tuple(g_args) if g_args.is_empty() => false,
- ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
- ty::Array(ty, _) => use_verbose(ty, fn_def),
- ty::FnDef(..) => fn_def,
- _ => true,
- }
-}
-
-impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
- fn visit_constant(&mut self, constant: &Constant<'tcx>, _location: Location) {
- let Constant { span, user_ty, literal } = constant;
- if use_verbose(literal.ty(), true) {
- self.push("mir::Constant");
- self.push(&format!(
- "+ span: {}",
- self.tcx.sess.source_map().span_to_embeddable_string(*span)
- ));
- if let Some(user_ty) = user_ty {
- self.push(&format!("+ user_ty: {user_ty:?}"));
- }
-
- // FIXME: this is a poor version of `pretty_print_const_value`.
- let fmt_val = |val: &ConstValue<'tcx>| match val {
- ConstValue::ZeroSized => "<ZST>".to_string(),
- ConstValue::Scalar(s) => format!("Scalar({s:?})"),
- ConstValue::Slice { .. } => "Slice(..)".to_string(),
- ConstValue::Indirect { .. } => "ByRef(..)".to_string(),
- };
-
- let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
- ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({leaf:?})"),
- ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(),
- };
-
- let val = match literal {
- ConstantKind::Ty(ct) => match ct.kind() {
- ty::ConstKind::Param(p) => format!("Param({p})"),
- ty::ConstKind::Unevaluated(uv) => {
- format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
- }
- ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)),
- ty::ConstKind::Error(_) => "Error".to_string(),
- // These variants shouldn't exist in the MIR.
- ty::ConstKind::Placeholder(_)
- | ty::ConstKind::Infer(_)
- | ty::ConstKind::Expr(_)
- | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
- },
- ConstantKind::Unevaluated(uv, _) => {
- format!(
- "Unevaluated({}, {:?}, {:?})",
- self.tcx.def_path_str(uv.def),
- uv.args,
- uv.promoted,
- )
- }
- // To keep the diffs small, we render this like we render `ty::Const::Value`.
- //
- // This changes once `ty::Const::Value` is represented using valtrees.
- ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)),
- };
-
- // This reflects what `Const` looked liked before `val` was renamed
- // as `kind`. We print it like this to avoid having to update
- // expected output in a lot of tests.
- self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val));
- }
- }
-
- fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
- self.super_rvalue(rvalue, location);
- if let Rvalue::Aggregate(kind, _) = rvalue {
- match **kind {
- AggregateKind::Closure(def_id, args) => {
- self.push("closure");
- self.push(&format!("+ def_id: {def_id:?}"));
- self.push(&format!("+ args: {args:#?}"));
- }
-
- AggregateKind::Generator(def_id, args, movability) => {
- self.push("generator");
- self.push(&format!("+ def_id: {def_id:?}"));
- self.push(&format!("+ args: {args:#?}"));
- self.push(&format!("+ movability: {movability:?}"));
- }
-
- AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
- self.push("adt");
- self.push(&format!("+ user_ty: {user_ty:?}"));
- }
-
- _ => {}
- }
- }
- }
-}
-
-fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
- let location = tcx.sess.source_map().span_to_embeddable_string(span);
- format!("scope {} at {}", scope.index(), location,)
-}
-
/// Prints local variables in a scope tree.
fn write_scope_tree(
tcx: TyCtxt<'_>,
body: &Body<'_>,
scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
parent: SourceScope,
depth: usize,
) -> io::Result<()> {
@@ -656,12 +453,26 @@
Ok(())
}
+impl Debug for VarDebugInfo<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
+ pre_fmt_projection(&projection[..], fmt)?;
+ write!(fmt, "({}: {})", self.name, ty)?;
+ post_fmt_projection(&projection[..], fmt)?;
+ } else {
+ write!(fmt, "{}", self.name)?;
+ }
+
+ write!(fmt, " => {:?}", self.value)
+ }
+}
+
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
pub fn write_mir_intro<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'_>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()> {
write_mir_sig(tcx, body, w)?;
writeln!(w, "{{")?;
@@ -685,12 +496,794 @@
Ok(())
}
+fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
+ use rustc_hir::def::DefKind;
+
+ trace!("write_mir_sig: {:?}", body.source.instance);
+ let def_id = body.source.def_id();
+ let kind = tcx.def_kind(def_id);
+ let is_function = match kind {
+ DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
+ _ => tcx.is_closure(def_id),
+ };
+ match (kind, body.source.promoted) {
+ (_, Some(i)) => write!(w, "{i:?} in ")?,
+ (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
+ (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
+ (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
+ (_, _) if is_function => write!(w, "fn ")?,
+ (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
+ _ => bug!("Unexpected def kind {:?}", kind),
+ }
+
+ ty::print::with_forced_impl_filename_line! {
+ // see notes on #41697 elsewhere
+ write!(w, "{}", tcx.def_path_str(def_id))?
+ }
+
+ if body.source.promoted.is_none() && is_function {
+ write!(w, "(")?;
+
+ // fn argument types.
+ for (i, arg) in body.args_iter().enumerate() {
+ if i != 0 {
+ write!(w, ", ")?;
+ }
+ write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+ }
+
+ write!(w, ") -> {}", body.return_ty())?;
+ } else {
+ assert_eq!(body.arg_count, 0);
+ write!(w, ": {} =", body.return_ty())?;
+ }
+
+ if let Some(yield_ty) = body.yield_ty() {
+ writeln!(w)?;
+ writeln!(w, "yields {yield_ty}")?;
+ }
+
+ write!(w, " ")?;
+ // Next thing that gets printed is the opening {
+
+ Ok(())
+}
+
+fn write_user_type_annotations(
+ tcx: TyCtxt<'_>,
+ body: &Body<'_>,
+ w: &mut dyn io::Write,
+) -> io::Result<()> {
+ if !body.user_type_annotations.is_empty() {
+ writeln!(w, "| User Type Annotations")?;
+ }
+ for (index, annotation) in body.user_type_annotations.iter_enumerated() {
+ writeln!(
+ w,
+ "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
+ index.index(),
+ annotation.user_ty,
+ tcx.sess.source_map().span_to_embeddable_string(annotation.span),
+ with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
+ )?;
+ }
+ if !body.user_type_annotations.is_empty() {
+ writeln!(w, "|")?;
+ }
+ Ok(())
+}
+
+pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
+ if let Some(i) = single {
+ vec![i]
+ } else {
+ tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Basic blocks and their parts (statements, terminators, ...)
+
+/// Write out a human-readable textual representation for the given basic block.
+pub fn write_basic_block<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ block: BasicBlock,
+ body: &Body<'tcx>,
+ extra_data: &mut F,
+ w: &mut dyn io::Write,
+) -> io::Result<()>
+where
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
+{
+ let data = &body[block];
+
+ // Basic block label at the top.
+ let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
+ writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
+
+ // List of statements in the middle.
+ let mut current_location = Location { block, statement_index: 0 };
+ for statement in &data.statements {
+ extra_data(PassWhere::BeforeLocation(current_location), w)?;
+ let indented_body = format!("{INDENT}{INDENT}{statement:?};");
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{:A$} // {}{}",
+ indented_body,
+ if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
+ comment(tcx, statement.source_info),
+ A = ALIGN,
+ )?;
+ } else {
+ writeln!(w, "{indented_body}")?;
+ }
+
+ write_extra(tcx, w, |visitor| {
+ visitor.visit_statement(statement, current_location);
+ })?;
+
+ extra_data(PassWhere::AfterLocation(current_location), w)?;
+
+ current_location.statement_index += 1;
+ }
+
+ // Terminator at the bottom.
+ extra_data(PassWhere::BeforeLocation(current_location), w)?;
+ let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{:A$} // {}{}",
+ indented_terminator,
+ if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
+ comment(tcx, data.terminator().source_info),
+ A = ALIGN,
+ )?;
+ } else {
+ writeln!(w, "{indented_terminator}")?;
+ }
+
+ write_extra(tcx, w, |visitor| {
+ visitor.visit_terminator(data.terminator(), current_location);
+ })?;
+
+ extra_data(PassWhere::AfterLocation(current_location), w)?;
+ extra_data(PassWhere::AfterTerminator(block), w)?;
+
+ writeln!(w, "{INDENT}}}")
+}
+
+impl Debug for Statement<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ use self::StatementKind::*;
+ match self.kind {
+ Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
+ FakeRead(box (ref cause, ref place)) => {
+ write!(fmt, "FakeRead({cause:?}, {place:?})")
+ }
+ Retag(ref kind, ref place) => write!(
+ fmt,
+ "Retag({}{:?})",
+ match kind {
+ RetagKind::FnEntry => "[fn entry] ",
+ RetagKind::TwoPhase => "[2phase] ",
+ RetagKind::Raw => "[raw] ",
+ RetagKind::Default => "",
+ },
+ place,
+ ),
+ StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
+ StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
+ SetDiscriminant { ref place, variant_index } => {
+ write!(fmt, "discriminant({place:?}) = {variant_index:?}")
+ }
+ Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
+ PlaceMention(ref place) => {
+ write!(fmt, "PlaceMention({place:?})")
+ }
+ AscribeUserType(box (ref place, ref c_ty), ref variance) => {
+ write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
+ }
+ Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
+ write!(fmt, "Coverage::{kind:?} for {rgn:?}")
+ }
+ Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
+ Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
+ ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
+ Nop => write!(fmt, "nop"),
+ }
+ }
+}
+
+impl<'tcx> Debug for TerminatorKind<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ self.fmt_head(fmt)?;
+ let successor_count = self.successors().count();
+ let labels = self.fmt_successor_labels();
+ assert_eq!(successor_count, labels.len());
+
+ // `Cleanup` is already included in successors
+ let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
+ let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
+ write!(fmt, "unwind ")?;
+ match self.unwind() {
+ // Not needed or included in successors
+ None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
+ Some(UnwindAction::Continue) => write!(fmt, "continue"),
+ Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
+ Some(UnwindAction::Terminate(reason)) => {
+ write!(fmt, "terminate({})", reason.as_short_str())
+ }
+ }
+ };
+
+ match (successor_count, show_unwind) {
+ (0, false) => Ok(()),
+ (0, true) => {
+ write!(fmt, " -> ")?;
+ fmt_unwind(fmt)
+ }
+ (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
+ _ => {
+ write!(fmt, " -> [")?;
+ for (i, target) in self.successors().enumerate() {
+ if i > 0 {
+ write!(fmt, ", ")?;
+ }
+ write!(fmt, "{}: {:?}", labels[i], target)?;
+ }
+ if show_unwind {
+ write!(fmt, ", ")?;
+ fmt_unwind(fmt)?;
+ }
+ write!(fmt, "]")
+ }
+ }
+ }
+}
+
+impl<'tcx> TerminatorKind<'tcx> {
+ /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
+ /// successor basic block, if any. The only information not included is the list of possible
+ /// successors, which may be rendered differently between the text and the graphviz format.
+ pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
+ use self::TerminatorKind::*;
+ match self {
+ Goto { .. } => write!(fmt, "goto"),
+ SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
+ Return => write!(fmt, "return"),
+ GeneratorDrop => write!(fmt, "generator_drop"),
+ UnwindResume => write!(fmt, "resume"),
+ UnwindTerminate(reason) => {
+ write!(fmt, "abort({})", reason.as_short_str())
+ }
+ Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
+ Unreachable => write!(fmt, "unreachable"),
+ Drop { place, .. } => write!(fmt, "drop({place:?})"),
+ Call { func, args, destination, .. } => {
+ write!(fmt, "{destination:?} = ")?;
+ write!(fmt, "{func:?}(")?;
+ for (index, arg) in args.iter().enumerate() {
+ if index > 0 {
+ write!(fmt, ", ")?;
+ }
+ write!(fmt, "{arg:?}")?;
+ }
+ write!(fmt, ")")
+ }
+ Assert { cond, expected, msg, .. } => {
+ write!(fmt, "assert(")?;
+ if !expected {
+ write!(fmt, "!")?;
+ }
+ write!(fmt, "{cond:?}, ")?;
+ msg.fmt_assert_args(fmt)?;
+ write!(fmt, ")")
+ }
+ FalseEdge { .. } => write!(fmt, "falseEdge"),
+ FalseUnwind { .. } => write!(fmt, "falseUnwind"),
+ InlineAsm { template, ref operands, options, .. } => {
+ write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
+ for op in operands {
+ write!(fmt, ", ")?;
+ let print_late = |&late| if late { "late" } else { "" };
+ match op {
+ InlineAsmOperand::In { reg, value } => {
+ write!(fmt, "in({reg}) {value:?}")?;
+ }
+ InlineAsmOperand::Out { reg, late, place: Some(place) } => {
+ write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
+ }
+ InlineAsmOperand::Out { reg, late, place: None } => {
+ write!(fmt, "{}out({}) _", print_late(late), reg)?;
+ }
+ InlineAsmOperand::InOut {
+ reg,
+ late,
+ in_value,
+ out_place: Some(out_place),
+ } => {
+ write!(
+ fmt,
+ "in{}out({}) {:?} => {:?}",
+ print_late(late),
+ reg,
+ in_value,
+ out_place
+ )?;
+ }
+ InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
+ write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
+ }
+ InlineAsmOperand::Const { value } => {
+ write!(fmt, "const {value:?}")?;
+ }
+ InlineAsmOperand::SymFn { value } => {
+ write!(fmt, "sym_fn {value:?}")?;
+ }
+ InlineAsmOperand::SymStatic { def_id } => {
+ write!(fmt, "sym_static {def_id:?}")?;
+ }
+ }
+ }
+ write!(fmt, ", options({options:?}))")
+ }
+ }
+ }
+
+ /// Returns the list of labels for the edges to the successor basic blocks.
+ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
+ use self::TerminatorKind::*;
+ match *self {
+ Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
+ Goto { .. } => vec!["".into()],
+ SwitchInt { ref targets, .. } => targets
+ .values
+ .iter()
+ .map(|&u| Cow::Owned(u.to_string()))
+ .chain(iter::once("otherwise".into()))
+ .collect(),
+ Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["return".into(), "unwind".into()]
+ }
+ Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
+ Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
+ Call { target: None, unwind: _, .. } => vec![],
+ Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
+ Yield { drop: None, .. } => vec!["resume".into()],
+ Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
+ Drop { unwind: _, .. } => vec!["return".into()],
+ Assert { unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["success".into(), "unwind".into()]
+ }
+ Assert { unwind: _, .. } => vec!["success".into()],
+ FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
+ FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["real".into(), "unwind".into()]
+ }
+ FalseUnwind { unwind: _, .. } => vec!["real".into()],
+ InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["return".into(), "unwind".into()]
+ }
+ InlineAsm { destination: Some(_), unwind: _, .. } => {
+ vec!["return".into()]
+ }
+ InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["unwind".into()]
+ }
+ InlineAsm { destination: None, unwind: _, .. } => vec![],
+ }
+ }
+}
+
+impl<'tcx> Debug for Rvalue<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ use self::Rvalue::*;
+
+ match *self {
+ Use(ref place) => write!(fmt, "{place:?}"),
+ Repeat(ref a, b) => {
+ write!(fmt, "[{a:?}; ")?;
+ pretty_print_const(b, fmt, false)?;
+ write!(fmt, "]")
+ }
+ Len(ref a) => write!(fmt, "Len({a:?})"),
+ Cast(ref kind, ref place, ref ty) => {
+ with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
+ }
+ BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
+ CheckedBinaryOp(ref op, box (ref a, ref b)) => {
+ write!(fmt, "Checked{op:?}({a:?}, {b:?})")
+ }
+ UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
+ Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
+ NullaryOp(ref op, ref t) => {
+ let t = with_no_trimmed_paths!(format!("{}", t));
+ match op {
+ NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
+ NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
+ NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
+ }
+ }
+ ThreadLocalRef(did) => ty::tls::with(|tcx| {
+ let muta = tcx.static_mutability(did).unwrap().prefix_str();
+ write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
+ }),
+ Ref(region, borrow_kind, ref place) => {
+ let kind_str = match borrow_kind {
+ BorrowKind::Shared => "",
+ BorrowKind::Shallow => "shallow ",
+ BorrowKind::Mut { .. } => "mut ",
+ };
+
+ // When printing regions, add trailing space if necessary.
+ let print_region = ty::tls::with(|tcx| {
+ tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
+ });
+ let region = if print_region {
+ let mut region = region.to_string();
+ if !region.is_empty() {
+ region.push(' ');
+ }
+ region
+ } else {
+ // Do not even print 'static
+ String::new()
+ };
+ write!(fmt, "&{region}{kind_str}{place:?}")
+ }
+
+ CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
+
+ AddressOf(mutability, ref place) => {
+ let kind_str = match mutability {
+ Mutability::Mut => "mut",
+ Mutability::Not => "const",
+ };
+
+ write!(fmt, "&raw {kind_str} {place:?}")
+ }
+
+ Aggregate(ref kind, ref places) => {
+ let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
+ let mut tuple_fmt = fmt.debug_tuple(name);
+ for place in places {
+ tuple_fmt.field(place);
+ }
+ tuple_fmt.finish()
+ };
+
+ match **kind {
+ AggregateKind::Array(_) => write!(fmt, "{places:?}"),
+
+ AggregateKind::Tuple => {
+ if places.is_empty() {
+ write!(fmt, "()")
+ } else {
+ fmt_tuple(fmt, "")
+ }
+ }
+
+ AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
+ ty::tls::with(|tcx| {
+ let variant_def = &tcx.adt_def(adt_did).variant(variant);
+ let args = tcx.lift(args).expect("could not lift for printing");
+ let name = FmtPrinter::new(tcx, Namespace::ValueNS)
+ .print_def_path(variant_def.def_id, args)?
+ .into_buffer();
+
+ match variant_def.ctor_kind() {
+ Some(CtorKind::Const) => fmt.write_str(&name),
+ Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
+ None => {
+ let mut struct_fmt = fmt.debug_struct(&name);
+ for (field, place) in iter::zip(&variant_def.fields, places) {
+ struct_fmt.field(field.name.as_str(), place);
+ }
+ struct_fmt.finish()
+ }
+ }
+ })
+ }
+
+ AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
+ let name = if tcx.sess.opts.unstable_opts.span_free_formats {
+ let args = tcx.lift(args).unwrap();
+ format!("[closure@{}]", tcx.def_path_str_with_args(def_id, args),)
+ } else {
+ let span = tcx.def_span(def_id);
+ format!(
+ "[closure@{}]",
+ tcx.sess.source_map().span_to_diagnostic_string(span)
+ )
+ };
+ let mut struct_fmt = fmt.debug_struct(&name);
+
+ // FIXME(project-rfc-2229#48): This should be a list of capture names/places
+ if let Some(def_id) = def_id.as_local()
+ && let Some(upvars) = tcx.upvars_mentioned(def_id)
+ {
+ for (&var_id, place) in iter::zip(upvars.keys(), places) {
+ let var_name = tcx.hir().name(var_id);
+ struct_fmt.field(var_name.as_str(), place);
+ }
+ } else {
+ for (index, place) in places.iter().enumerate() {
+ struct_fmt.field(&format!("{index}"), place);
+ }
+ }
+
+ struct_fmt.finish()
+ }),
+
+ AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
+ let name = format!("[generator@{:?}]", tcx.def_span(def_id));
+ let mut struct_fmt = fmt.debug_struct(&name);
+
+ // FIXME(project-rfc-2229#48): This should be a list of capture names/places
+ if let Some(def_id) = def_id.as_local()
+ && let Some(upvars) = tcx.upvars_mentioned(def_id)
+ {
+ for (&var_id, place) in iter::zip(upvars.keys(), places) {
+ let var_name = tcx.hir().name(var_id);
+ struct_fmt.field(var_name.as_str(), place);
+ }
+ } else {
+ for (index, place) in places.iter().enumerate() {
+ struct_fmt.field(&format!("{index}"), place);
+ }
+ }
+
+ struct_fmt.finish()
+ }),
+ }
+ }
+
+ ShallowInitBox(ref place, ref ty) => {
+ with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
+ }
+ }
+ }
+}
+
+impl<'tcx> Debug for Operand<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ use self::Operand::*;
+ match *self {
+ Constant(ref a) => write!(fmt, "{a:?}"),
+ Copy(ref place) => write!(fmt, "{place:?}"),
+ Move(ref place) => write!(fmt, "move {place:?}"),
+ }
+ }
+}
+
+impl Debug for Place<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ self.as_ref().fmt(fmt)
+ }
+}
+
+impl Debug for PlaceRef<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ pre_fmt_projection(self.projection, fmt)?;
+ write!(fmt, "{:?}", self.local)?;
+ post_fmt_projection(self.projection, fmt)
+ }
+}
+
+fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+ for &elem in projection.iter().rev() {
+ match elem {
+ ProjectionElem::OpaqueCast(_)
+ | ProjectionElem::Downcast(_, _)
+ | ProjectionElem::Field(_, _) => {
+ write!(fmt, "(").unwrap();
+ }
+ ProjectionElem::Deref => {
+ write!(fmt, "(*").unwrap();
+ }
+ ProjectionElem::Index(_)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => {}
+ }
+ }
+
+ Ok(())
+}
+
+fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+ for &elem in projection.iter() {
+ match elem {
+ ProjectionElem::OpaqueCast(ty) => {
+ write!(fmt, " as {ty})")?;
+ }
+ ProjectionElem::Downcast(Some(name), _index) => {
+ write!(fmt, " as {name})")?;
+ }
+ ProjectionElem::Downcast(None, index) => {
+ write!(fmt, " as variant#{index:?})")?;
+ }
+ ProjectionElem::Deref => {
+ write!(fmt, ")")?;
+ }
+ ProjectionElem::Field(field, ty) => {
+ with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
+ }
+ ProjectionElem::Index(ref index) => {
+ write!(fmt, "[{index:?}]")?;
+ }
+ ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
+ write!(fmt, "[{offset:?} of {min_length:?}]")?;
+ }
+ ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
+ write!(fmt, "[-{offset:?} of {min_length:?}]")?;
+ }
+ ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
+ write!(fmt, "[{from:?}:]")?;
+ }
+ ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
+ write!(fmt, "[:-{to:?}]")?;
+ }
+ ProjectionElem::Subslice { from, to, from_end: true } => {
+ write!(fmt, "[{from:?}:-{to:?}]")?;
+ }
+ ProjectionElem::Subslice { from, to, from_end: false } => {
+ write!(fmt, "[{from:?}..{to:?}]")?;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// After we print the main statement, we sometimes dump extra
+/// information. There's often a lot of little things "nuzzled up" in
+/// a statement.
+fn write_extra<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ write: &mut dyn io::Write,
+ mut visit_op: F,
+) -> io::Result<()>
+where
+ F: FnMut(&mut ExtraComments<'tcx>),
+{
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ let mut extra_comments = ExtraComments { tcx, comments: vec![] };
+ visit_op(&mut extra_comments);
+ for comment in extra_comments.comments {
+ writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
+ }
+ }
+ Ok(())
+}
+
+struct ExtraComments<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ comments: Vec<String>,
+}
+
+impl<'tcx> ExtraComments<'tcx> {
+ fn push(&mut self, lines: &str) {
+ for line in lines.split('\n') {
+ self.comments.push(line.to_string());
+ }
+ }
+}
+
+fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
+ match *ty.kind() {
+ ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
+ // Unit type
+ ty::Tuple(g_args) if g_args.is_empty() => false,
+ ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
+ ty::Array(ty, _) => use_verbose(ty, fn_def),
+ ty::FnDef(..) => fn_def,
+ _ => true,
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
+ fn visit_constant(&mut self, constant: &Constant<'tcx>, _location: Location) {
+ let Constant { span, user_ty, literal } = constant;
+ if use_verbose(literal.ty(), true) {
+ self.push("mir::Constant");
+ self.push(&format!(
+ "+ span: {}",
+ self.tcx.sess.source_map().span_to_embeddable_string(*span)
+ ));
+ if let Some(user_ty) = user_ty {
+ self.push(&format!("+ user_ty: {user_ty:?}"));
+ }
+
+ let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
+ let tcx = self.tcx;
+ rustc_data_structures::make_display(move |fmt| {
+ pretty_print_const_value_tcx(tcx, val, ty, fmt)
+ })
+ };
+
+ // FIXME: call pretty_print_const_valtree?
+ let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
+ ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"),
+ ty::ValTree::Branch(_) => "Branch(..)".to_string(),
+ };
+
+ let val = match literal {
+ ConstantKind::Ty(ct) => match ct.kind() {
+ ty::ConstKind::Param(p) => format!("ty::Param({p})"),
+ ty::ConstKind::Unevaluated(uv) => {
+ format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
+ }
+ ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)),
+ // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
+ ty::ConstKind::Error(_) => "Error".to_string(),
+ // These variants shouldn't exist in the MIR.
+ ty::ConstKind::Placeholder(_)
+ | ty::ConstKind::Infer(_)
+ | ty::ConstKind::Expr(_)
+ | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
+ },
+ ConstantKind::Unevaluated(uv, _) => {
+ format!(
+ "Unevaluated({}, {:?}, {:?})",
+ self.tcx.def_path_str(uv.def),
+ uv.args,
+ uv.promoted,
+ )
+ }
+ ConstantKind::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
+ };
+
+ // This reflects what `Const` looked liked before `val` was renamed
+ // as `kind`. We print it like this to avoid having to update
+ // expected output in a lot of tests.
+ self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val));
+ }
+ }
+
+ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+ self.super_rvalue(rvalue, location);
+ if let Rvalue::Aggregate(kind, _) = rvalue {
+ match **kind {
+ AggregateKind::Closure(def_id, args) => {
+ self.push("closure");
+ self.push(&format!("+ def_id: {def_id:?}"));
+ self.push(&format!("+ args: {args:#?}"));
+ }
+
+ AggregateKind::Generator(def_id, args, movability) => {
+ self.push("generator");
+ self.push(&format!("+ def_id: {def_id:?}"));
+ self.push(&format!("+ args: {args:#?}"));
+ self.push(&format!("+ movability: {movability:?}"));
+ }
+
+ AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
+ self.push("adt");
+ self.push(&format!("+ user_ty: {user_ty:?}"));
+ }
+
+ _ => {}
+ }
+ }
+ }
+}
+
+fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
+ let location = tcx.sess.source_map().span_to_embeddable_string(span);
+ format!("scope {} at {}", scope.index(), location,)
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Allocations
+
/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
/// allocations.
pub fn write_allocations<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'_>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()> {
fn alloc_ids_from_alloc(
alloc: ConstAllocation<'_>,
@@ -739,7 +1332,7 @@
let mut todo: Vec<_> = seen.iter().copied().collect();
while let Some(id) = todo.pop() {
let mut write_allocation_track_relocs =
- |w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
+ |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
// `.rev()` because we are popping them from the back of the `todo` vector.
for id in alloc_ids_from_alloc(alloc).rev() {
if seen.insert(id) {
@@ -1000,91 +1593,173 @@
Ok(())
}
-fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> {
- use rustc_hir::def::DefKind;
+///////////////////////////////////////////////////////////////////////////
+// Constants
- trace!("write_mir_sig: {:?}", body.source.instance);
- let def_id = body.source.def_id();
- let kind = tcx.def_kind(def_id);
- let is_function = match kind {
- DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
- _ => tcx.is_closure(def_id),
- };
- match (kind, body.source.promoted) {
- (_, Some(i)) => write!(w, "{i:?} in ")?,
- (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
- (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
- (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
- (_, _) if is_function => write!(w, "fn ")?,
- (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
- _ => bug!("Unexpected def kind {:?}", kind),
- }
+fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
+ write!(fmt, "b\"{}\"", byte_str.escape_ascii())
+}
- ty::print::with_forced_impl_filename_line! {
- // see notes on #41697 elsewhere
- write!(w, "{}", tcx.def_path_str(def_id))?
- }
-
- if body.source.promoted.is_none() && is_function {
- write!(w, "(")?;
-
- // fn argument types.
- for (i, arg) in body.args_iter().enumerate() {
- if i != 0 {
- write!(w, ", ")?;
- }
- write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+fn comma_sep<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ fmt: &mut Formatter<'_>,
+ elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+) -> fmt::Result {
+ let mut first = true;
+ for (ct, ty) in elems {
+ if !first {
+ fmt.write_str(", ")?;
}
-
- write!(w, ") -> {}", body.return_ty())?;
- } else {
- assert_eq!(body.arg_count, 0);
- write!(w, ": {} =", body.return_ty())?;
- }
-
- if let Some(yield_ty) = body.yield_ty() {
- writeln!(w)?;
- writeln!(w, "yields {yield_ty}")?;
- }
-
- write!(w, " ")?;
- // Next thing that gets printed is the opening {
-
- Ok(())
-}
-
-fn write_user_type_annotations(
- tcx: TyCtxt<'_>,
- body: &Body<'_>,
- w: &mut dyn Write,
-) -> io::Result<()> {
- if !body.user_type_annotations.is_empty() {
- writeln!(w, "| User Type Annotations")?;
- }
- for (index, annotation) in body.user_type_annotations.iter_enumerated() {
- writeln!(
- w,
- "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
- index.index(),
- annotation.user_ty,
- tcx.sess.source_map().span_to_embeddable_string(annotation.span),
- with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
- )?;
- }
- if !body.user_type_annotations.is_empty() {
- writeln!(w, "|")?;
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+ first = false;
}
Ok(())
}
-pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
- if let Some(i) = single {
- vec![i]
- } else {
- tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
+fn pretty_print_const_value_tcx<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ct: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+ fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+ use crate::ty::print::PrettyPrinter;
+
+ if tcx.sess.verbose() {
+ fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
+ return Ok(());
}
+
+ let u8_type = tcx.types.u8;
+ match (ct, ty.kind()) {
+ // Byte/string slices, printed as (byte) string literals.
+ (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
+ if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+ fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
+ return Ok(());
+ }
+ }
+ (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
+ if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+ pretty_print_byte_str(fmt, data)?;
+ return Ok(());
+ }
+ }
+ (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
+ let n = n.try_to_target_usize(tcx).unwrap();
+ let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
+ // cast is ok because we already checked for pointer size (32 or 64 bit) above
+ let range = AllocRange { start: offset, size: Size::from_bytes(n) };
+ let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
+ fmt.write_str("*")?;
+ pretty_print_byte_str(fmt, byte_str)?;
+ return Ok(());
+ }
+ // Aggregates, printed as array/tuple/struct/variant construction syntax.
+ //
+ // NB: the `has_non_region_param` check ensures that we can use
+ // the `destructure_const` query with an empty `ty::ParamEnv` without
+ // introducing ICEs (e.g. via `layout_of`) from missing bounds.
+ // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
+ // to be able to destructure the tuple into `(0u8, *mut T)`
+ (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
+ let ct = tcx.lift(ct).unwrap();
+ let ty = tcx.lift(ty).unwrap();
+ if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
+ let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
+ match *ty.kind() {
+ ty::Array(..) => {
+ fmt.write_str("[")?;
+ comma_sep(tcx, fmt, fields)?;
+ fmt.write_str("]")?;
+ }
+ ty::Tuple(..) => {
+ fmt.write_str("(")?;
+ comma_sep(tcx, fmt, fields)?;
+ if contents.fields.len() == 1 {
+ fmt.write_str(",")?;
+ }
+ fmt.write_str(")")?;
+ }
+ ty::Adt(def, _) if def.variants().is_empty() => {
+ fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
+ }
+ ty::Adt(def, args) => {
+ let variant_idx = contents
+ .variant
+ .expect("destructed mir constant of adt without variant idx");
+ let variant_def = &def.variant(variant_idx);
+ let args = tcx.lift(args).unwrap();
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let cx = cx.print_value_path(variant_def.def_id, args)?;
+ fmt.write_str(&cx.into_buffer())?;
+
+ match variant_def.ctor_kind() {
+ Some(CtorKind::Const) => {}
+ Some(CtorKind::Fn) => {
+ fmt.write_str("(")?;
+ comma_sep(tcx, fmt, fields)?;
+ fmt.write_str(")")?;
+ }
+ None => {
+ fmt.write_str(" {{ ")?;
+ let mut first = true;
+ for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
+ {
+ if !first {
+ fmt.write_str(", ")?;
+ }
+ write!(fmt, "{}: ", field_def.name)?;
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+ first = false;
+ }
+ fmt.write_str(" }}")?;
+ }
+ }
+ }
+ _ => unreachable!(),
+ }
+ return Ok(());
+ }
+ }
+ (ConstValue::Scalar(scalar), _) => {
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let ty = tcx.lift(ty).unwrap();
+ cx = cx.pretty_print_const_scalar(scalar, ty)?;
+ fmt.write_str(&cx.into_buffer())?;
+ return Ok(());
+ }
+ (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let cx = cx.print_value_path(*d, s)?;
+ fmt.write_str(&cx.into_buffer())?;
+ return Ok(());
+ }
+ // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
+ // their fields instead of just dumping the memory.
+ _ => {}
+ }
+ // Fall back to debug pretty printing for invalid constants.
+ write!(fmt, "{ct:?}: {ty}")
}
+pub(crate) fn pretty_print_const_value<'tcx>(
+ ct: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+ fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+ ty::tls::with(|tcx| {
+ let ct = tcx.lift(ct).unwrap();
+ let ty = tcx.lift(ty).unwrap();
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)
+ })
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Miscellaneous
+
/// Calc converted u64 decimal into hex and return it's length in chars
///
/// ```ignore (cannot-test-private-function)
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 0c80610..c157b70 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,6 +1,5 @@
//! Values computed by queries that use MIR.
-use crate::mir::interpret::ConstValue;
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::UnordSet;
@@ -16,7 +15,7 @@
use std::cell::Cell;
use std::fmt::{self, Debug};
-use super::SourceInfo;
+use super::{ConstValue, SourceInfo};
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnsafetyViolationKind {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
new file mode 100644
index 0000000..25534f4
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -0,0 +1,441 @@
+/// Functionality for statements, operands, places, and things that appear in them.
+use super::*;
+
+///////////////////////////////////////////////////////////////////////////
+// Statements
+
+/// A statement in a basic block, including information about its source code.
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct Statement<'tcx> {
+ pub source_info: SourceInfo,
+ pub kind: StatementKind<'tcx>,
+}
+
+impl Statement<'_> {
+ /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
+ /// invalidating statement indices in `Location`s.
+ pub fn make_nop(&mut self) {
+ self.kind = StatementKind::Nop
+ }
+
+ /// Changes a statement to a nop and returns the original statement.
+ #[must_use = "If you don't need the statement, use `make_nop` instead"]
+ pub fn replace_nop(&mut self) -> Self {
+ Statement {
+ source_info: self.source_info,
+ kind: mem::replace(&mut self.kind, StatementKind::Nop),
+ }
+ }
+}
+
+impl<'tcx> StatementKind<'tcx> {
+ pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
+ match self {
+ StatementKind::Assign(x) => Some(x),
+ _ => None,
+ }
+ }
+
+ pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
+ match self {
+ StatementKind::Assign(x) => Some(x),
+ _ => None,
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Places
+
+impl<V, T> ProjectionElem<V, T> {
+ /// Returns `true` if the target of this projection may refer to a different region of memory
+ /// than the base.
+ fn is_indirect(&self) -> bool {
+ match self {
+ Self::Deref => true,
+
+ Self::Field(_, _)
+ | Self::Index(_)
+ | Self::OpaqueCast(_)
+ | Self::ConstantIndex { .. }
+ | Self::Subslice { .. }
+ | Self::Downcast(_, _) => false,
+ }
+ }
+
+ /// Returns `true` if the target of this projection always refers to the same memory region
+ /// whatever the state of the program.
+ pub fn is_stable_offset(&self) -> bool {
+ match self {
+ Self::Deref | Self::Index(_) => false,
+ Self::Field(_, _)
+ | Self::OpaqueCast(_)
+ | Self::ConstantIndex { .. }
+ | Self::Subslice { .. }
+ | Self::Downcast(_, _) => true,
+ }
+ }
+
+ /// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
+ pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
+ matches!(*self, Self::Downcast(_, x) if x == v)
+ }
+
+ /// Returns `true` if this is a `Field` projection with the given index.
+ pub fn is_field_to(&self, f: FieldIdx) -> bool {
+ matches!(*self, Self::Field(x, _) if x == f)
+ }
+
+ /// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
+ pub fn can_use_in_debuginfo(&self) -> bool {
+ match self {
+ Self::ConstantIndex { from_end: false, .. }
+ | Self::Deref
+ | Self::Downcast(_, _)
+ | Self::Field(_, _) => true,
+ Self::ConstantIndex { from_end: true, .. }
+ | Self::Index(_)
+ | Self::OpaqueCast(_)
+ | Self::Subslice { .. } => false,
+ }
+ }
+}
+
+/// Alias for projections as they appear in `UserTypeProjection`, where we
+/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
+pub type ProjectionKind = ProjectionElem<(), ()>;
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct PlaceRef<'tcx> {
+ pub local: Local,
+ pub projection: &'tcx [PlaceElem<'tcx>],
+}
+
+// Once we stop implementing `Ord` for `DefId`,
+// this impl will be unnecessary. Until then, we'll
+// leave this impl in place to prevent re-adding a
+// dependency on the `Ord` impl for `DefId`
+impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
+
+impl<'tcx> Place<'tcx> {
+ // FIXME change this to a const fn by also making List::empty a const fn.
+ pub fn return_place() -> Place<'tcx> {
+ Place { local: RETURN_PLACE, projection: List::empty() }
+ }
+
+ /// Returns `true` if this `Place` contains a `Deref` projection.
+ ///
+ /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
+ /// same region of memory as its base.
+ pub fn is_indirect(&self) -> bool {
+ self.projection.iter().any(|elem| elem.is_indirect())
+ }
+
+ /// Returns `true` if this `Place`'s first projection is `Deref`.
+ ///
+ /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
+ /// `Deref` projections can only occur as the first projection. In that case this method
+ /// is equivalent to `is_indirect`, but faster.
+ pub fn is_indirect_first_projection(&self) -> bool {
+ self.as_ref().is_indirect_first_projection()
+ }
+
+ /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
+ /// a single deref of a local.
+ #[inline(always)]
+ pub fn local_or_deref_local(&self) -> Option<Local> {
+ self.as_ref().local_or_deref_local()
+ }
+
+ /// If this place represents a local variable like `_X` with no
+ /// projections, return `Some(_X)`.
+ #[inline(always)]
+ pub fn as_local(&self) -> Option<Local> {
+ self.as_ref().as_local()
+ }
+
+ #[inline]
+ pub fn as_ref(&self) -> PlaceRef<'tcx> {
+ PlaceRef { local: self.local, projection: &self.projection }
+ }
+
+ /// Iterate over the projections in evaluation order, i.e., the first element is the base with
+ /// its projection and then subsequently more projections are added.
+ /// As a concrete example, given the place a.b.c, this would yield:
+ /// - (a, .b)
+ /// - (a.b, .c)
+ ///
+ /// Given a place without projections, the iterator is empty.
+ #[inline]
+ pub fn iter_projections(
+ self,
+ ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
+ self.as_ref().iter_projections()
+ }
+
+ /// Generates a new place by appending `more_projections` to the existing ones
+ /// and interning the result.
+ pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
+ if more_projections.is_empty() {
+ return self;
+ }
+
+ self.as_ref().project_deeper(more_projections, tcx)
+ }
+}
+
+impl From<Local> for Place<'_> {
+ #[inline]
+ fn from(local: Local) -> Self {
+ Place { local, projection: List::empty() }
+ }
+}
+
+impl<'tcx> PlaceRef<'tcx> {
+ /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
+ /// a single deref of a local.
+ pub fn local_or_deref_local(&self) -> Option<Local> {
+ match *self {
+ PlaceRef { local, projection: [] }
+ | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
+ _ => None,
+ }
+ }
+
+ /// Returns `true` if this `Place` contains a `Deref` projection.
+ ///
+ /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
+ /// same region of memory as its base.
+ pub fn is_indirect(&self) -> bool {
+ self.projection.iter().any(|elem| elem.is_indirect())
+ }
+
+ /// Returns `true` if this `Place`'s first projection is `Deref`.
+ ///
+ /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
+ /// `Deref` projections can only occur as the first projection. In that case this method
+ /// is equivalent to `is_indirect`, but faster.
+ pub fn is_indirect_first_projection(&self) -> bool {
+ // To make sure this is not accidentally used in wrong mir phase
+ debug_assert!(
+ self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
+ );
+ self.projection.first() == Some(&PlaceElem::Deref)
+ }
+
+ /// If this place represents a local variable like `_X` with no
+ /// projections, return `Some(_X)`.
+ #[inline]
+ pub fn as_local(&self) -> Option<Local> {
+ match *self {
+ PlaceRef { local, projection: [] } => Some(local),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
+ if let &[ref proj_base @ .., elem] = self.projection {
+ Some((PlaceRef { local: self.local, projection: proj_base }, elem))
+ } else {
+ None
+ }
+ }
+
+ /// Iterate over the projections in evaluation order, i.e., the first element is the base with
+ /// its projection and then subsequently more projections are added.
+ /// As a concrete example, given the place a.b.c, this would yield:
+ /// - (a, .b)
+ /// - (a.b, .c)
+ ///
+ /// Given a place without projections, the iterator is empty.
+ #[inline]
+ pub fn iter_projections(
+ self,
+ ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
+ self.projection.iter().enumerate().map(move |(i, proj)| {
+ let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
+ (base, *proj)
+ })
+ }
+
+ /// Generates a new place by appending `more_projections` to the existing ones
+ /// and interning the result.
+ pub fn project_deeper(
+ self,
+ more_projections: &[PlaceElem<'tcx>],
+ tcx: TyCtxt<'tcx>,
+ ) -> Place<'tcx> {
+ let mut v: Vec<PlaceElem<'tcx>>;
+
+ let new_projections = if self.projection.is_empty() {
+ more_projections
+ } else {
+ v = Vec::with_capacity(self.projection.len() + more_projections.len());
+ v.extend(self.projection);
+ v.extend(more_projections);
+ &v
+ };
+
+ Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
+ }
+}
+
+impl From<Local> for PlaceRef<'_> {
+ #[inline]
+ fn from(local: Local) -> Self {
+ PlaceRef { local, projection: &[] }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Operands
+
+impl<'tcx> Operand<'tcx> {
+ /// Convenience helper to make a constant that refers to the fn
+ /// with given `DefId` and args. Since this is used to synthesize
+ /// MIR, assumes `user_ty` is None.
+ pub fn function_handle(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ args: impl IntoIterator<Item = GenericArg<'tcx>>,
+ span: Span,
+ ) -> Self {
+ let ty = Ty::new_fn_def(tcx, def_id, args);
+ Operand::Constant(Box::new(Constant {
+ span,
+ user_ty: None,
+ literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
+ }))
+ }
+
+ pub fn is_move(&self) -> bool {
+ matches!(self, Operand::Move(..))
+ }
+
+ /// Convenience helper to make a literal-like constant from a given scalar value.
+ /// Since this is used to synthesize MIR, assumes `user_ty` is None.
+ pub fn const_from_scalar(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ val: Scalar,
+ span: Span,
+ ) -> Operand<'tcx> {
+ debug_assert!({
+ let param_env_and_ty = ty::ParamEnv::empty().and(ty);
+ let type_size = tcx
+ .layout_of(param_env_and_ty)
+ .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
+ .size;
+ let scalar_size = match val {
+ Scalar::Int(int) => int.size(),
+ _ => panic!("Invalid scalar type {val:?}"),
+ };
+ scalar_size == type_size
+ });
+ Operand::Constant(Box::new(Constant {
+ span,
+ user_ty: None,
+ literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
+ }))
+ }
+
+ pub fn to_copy(&self) -> Self {
+ match *self {
+ Operand::Copy(_) | Operand::Constant(_) => self.clone(),
+ Operand::Move(place) => Operand::Copy(place),
+ }
+ }
+
+ /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
+ /// constant.
+ pub fn place(&self) -> Option<Place<'tcx>> {
+ match self {
+ Operand::Copy(place) | Operand::Move(place) => Some(*place),
+ Operand::Constant(_) => None,
+ }
+ }
+
+ /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
+ /// place.
+ pub fn constant(&self) -> Option<&Constant<'tcx>> {
+ match self {
+ Operand::Constant(x) => Some(&**x),
+ Operand::Copy(_) | Operand::Move(_) => None,
+ }
+ }
+
+ /// Gets the `ty::FnDef` from an operand if it's a constant function item.
+ ///
+ /// While this is unlikely in general, it's the normal case of what you'll
+ /// find as the `func` in a [`TerminatorKind::Call`].
+ pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
+ let const_ty = self.constant()?.literal.ty();
+ if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// Rvalues
+
+impl<'tcx> Rvalue<'tcx> {
+ /// Returns true if rvalue can be safely removed when the result is unused.
+ #[inline]
+ pub fn is_safe_to_remove(&self) -> bool {
+ match self {
+ // Pointer to int casts may be side-effects due to exposing the provenance.
+ // While the model is undecided, we should be conservative. See
+ // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
+ Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
+
+ Rvalue::Use(_)
+ | Rvalue::CopyForDeref(_)
+ | Rvalue::Repeat(_, _)
+ | Rvalue::Ref(_, _, _)
+ | Rvalue::ThreadLocalRef(_)
+ | Rvalue::AddressOf(_, _)
+ | Rvalue::Len(_)
+ | Rvalue::Cast(
+ CastKind::IntToInt
+ | CastKind::FloatToInt
+ | CastKind::FloatToFloat
+ | CastKind::IntToFloat
+ | CastKind::FnPtrToPtr
+ | CastKind::PtrToPtr
+ | CastKind::PointerCoercion(_)
+ | CastKind::PointerFromExposedAddress
+ | CastKind::DynStar
+ | CastKind::Transmute,
+ _,
+ _,
+ )
+ | Rvalue::BinaryOp(_, _)
+ | Rvalue::CheckedBinaryOp(_, _)
+ | Rvalue::NullaryOp(_, _)
+ | Rvalue::UnaryOp(_, _)
+ | Rvalue::Discriminant(_)
+ | Rvalue::Aggregate(_, _)
+ | Rvalue::ShallowInitBox(_, _) => true,
+ }
+ }
+}
+
+impl BorrowKind {
+ pub fn mutability(&self) -> Mutability {
+ match *self {
+ BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
+ BorrowKind::Mut { .. } => Mutability::Mut,
+ }
+ }
+
+ pub fn allows_two_phase_borrow(&self) -> bool {
+ match *self {
+ BorrowKind::Shared
+ | BorrowKind::Shallow
+ | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
+ false
+ }
+ BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 79b64a4..f99084d 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -3,7 +3,7 @@
//! This is in a dedicated file so that changes to this file can be reviewed more carefully.
//! The intention is that this file only contains datatype declarations, no code.
-use super::{BasicBlock, Constant, Local, SwitchTargets, UserTypeProjection};
+use super::{BasicBlock, Constant, Local, UserTypeProjection};
use crate::mir::coverage::{CodeRegion, CoverageKind};
use crate::traits::Reveal;
@@ -24,6 +24,7 @@
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use rustc_target::asm::InlineAsmRegOrRegClass;
+use smallvec::SmallVec;
/// Represents the "flavors" of MIR.
///
@@ -828,6 +829,27 @@
}
}
+#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
+pub struct SwitchTargets {
+ /// Possible values. The locations to branch to in each case
+ /// are found in the corresponding indices from the `targets` vector.
+ pub(super) values: SmallVec<[u128; 1]>,
+
+ /// Possible branch sites. The last element of this vector is used
+ /// for the otherwise branch, so targets.len() == values.len() + 1
+ /// should hold.
+ //
+ // This invariant is quite non-obvious and also could be improved.
+ // One way to make this invariant is to have something like this instead:
+ //
+ // branches: Vec<(ConstInt, BasicBlock)>,
+ // otherwise: Option<BasicBlock> // exhaustive if None
+ //
+ // However we’ve decided to keep this as-is until we figure a case
+ // where some other approach seems to be strictly better than other.
+ pub(super) targets: SmallVec<[BasicBlock; 2]>,
+}
+
/// Action to be taken when a stack unwind happens.
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 7eddf13..02aab4a 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -1,39 +1,16 @@
+/// Functionality for terminators and helper types that appear in terminators.
use rustc_hir::LangItem;
use smallvec::SmallVec;
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction};
-use rustc_ast::InlineAsmTemplatePiece;
pub use rustc_ast::Mutability;
use rustc_macros::HashStable;
-use std::borrow::Cow;
-use std::fmt::{self, Debug, Formatter, Write};
use std::iter;
use std::slice;
pub use super::query::*;
use super::*;
-#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
-pub struct SwitchTargets {
- /// Possible values. The locations to branch to in each case
- /// are found in the corresponding indices from the `targets` vector.
- values: SmallVec<[u128; 1]>,
-
- /// Possible branch sites. The last element of this vector is used
- /// for the otherwise branch, so targets.len() == values.len() + 1
- /// should hold.
- //
- // This invariant is quite non-obvious and also could be improved.
- // One way to make this invariant is to have something like this instead:
- //
- // branches: Vec<(ConstInt, BasicBlock)>,
- // otherwise: Option<BasicBlock> // exhaustive if None
- //
- // However we’ve decided to keep this as-is until we figure a case
- // where some other approach seems to be strictly better than other.
- targets: SmallVec<[BasicBlock; 2]>,
-}
-
impl SwitchTargets {
/// Creates switch targets from an iterator of values and target blocks.
///
@@ -135,6 +112,168 @@
}
}
+impl<O> AssertKind<O> {
+ /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
+ pub fn is_optional_overflow_check(&self) -> bool {
+ use AssertKind::*;
+ use BinOp::*;
+ matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
+ }
+
+ /// Get the message that is printed at runtime when this assertion fails.
+ ///
+ /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
+ /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
+ /// instead of printing a static message.
+ pub fn description(&self) -> &'static str {
+ use AssertKind::*;
+ match self {
+ Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
+ Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
+ Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
+ Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
+ Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
+ OverflowNeg(_) => "attempt to negate with overflow",
+ Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
+ Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
+ Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
+ DivisionByZero(_) => "attempt to divide by zero",
+ RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
+ ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
+ ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
+ ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
+ ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
+ BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
+ bug!("Unexpected AssertKind")
+ }
+ }
+ }
+
+ /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
+ ///
+ /// Needs to be kept in sync with the run-time behavior (which is defined by
+ /// `AssertKind::description` and the lang items mentioned in its docs).
+ /// Note that we deliberately show more details here than we do at runtime, such as the actual
+ /// numbers that overflowed -- it is much easier to do so here than at runtime.
+ pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
+ where
+ O: Debug,
+ {
+ use AssertKind::*;
+ match self {
+ BoundsCheck { ref len, ref index } => write!(
+ f,
+ "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
+ ),
+
+ OverflowNeg(op) => {
+ write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
+ }
+ DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
+ RemainderByZero(op) => write!(
+ f,
+ "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
+ ),
+ Overflow(BinOp::Add, l, r) => write!(
+ f,
+ "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
+ ),
+ Overflow(BinOp::Sub, l, r) => write!(
+ f,
+ "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
+ ),
+ Overflow(BinOp::Mul, l, r) => write!(
+ f,
+ "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
+ ),
+ Overflow(BinOp::Div, l, r) => write!(
+ f,
+ "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
+ ),
+ Overflow(BinOp::Rem, l, r) => write!(
+ f,
+ "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
+ ),
+ Overflow(BinOp::Shr, _, r) => {
+ write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
+ }
+ Overflow(BinOp::Shl, _, r) => {
+ write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
+ }
+ MisalignedPointerDereference { required, found } => {
+ write!(
+ f,
+ "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
+ )
+ }
+ _ => write!(f, "\"{}\"", self.description()),
+ }
+ }
+
+ /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
+ ///
+ /// Needs to be kept in sync with the run-time behavior (which is defined by
+ /// `AssertKind::description` and the lang items mentioned in its docs).
+ /// Note that we deliberately show more details here than we do at runtime, such as the actual
+ /// numbers that overflowed -- it is much easier to do so here than at runtime.
+ pub fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ use AssertKind::*;
+
+ match self {
+ BoundsCheck { .. } => middle_bounds_check,
+ Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
+ Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
+ Overflow(_, _, _) => middle_assert_op_overflow,
+ OverflowNeg(_) => middle_assert_overflow_neg,
+ DivisionByZero(_) => middle_assert_divide_by_zero,
+ RemainderByZero(_) => middle_assert_remainder_by_zero,
+ ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
+ ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
+ ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
+ ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
+
+ MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
+ }
+ }
+
+ pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
+ where
+ O: fmt::Debug,
+ {
+ use AssertKind::*;
+
+ macro_rules! add {
+ ($name: expr, $value: expr) => {
+ adder($name.into(), $value.into_diagnostic_arg());
+ };
+ }
+
+ match self {
+ BoundsCheck { len, index } => {
+ add!("len", format!("{len:?}"));
+ add!("index", format!("{index:?}"));
+ }
+ Overflow(BinOp::Shl | BinOp::Shr, _, val)
+ | DivisionByZero(val)
+ | RemainderByZero(val)
+ | OverflowNeg(val) => {
+ add!("val", format!("{val:#?}"));
+ }
+ Overflow(binop, left, right) => {
+ add!("op", binop.to_hir_binop().as_str());
+ add!("left", format!("{left:#?}"));
+ add!("right", format!("{right:#?}"));
+ }
+ ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
+ MisalignedPointerDereference { required, found } => {
+ add!("required", format!("{required:#?}"));
+ add!("found", format!("{found:#?}"));
+ }
+ }
+ }
+}
+
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct Terminator<'tcx> {
pub source_info: SourceInfo,
@@ -299,187 +438,6 @@
}
}
-impl<'tcx> Debug for TerminatorKind<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- self.fmt_head(fmt)?;
- let successor_count = self.successors().count();
- let labels = self.fmt_successor_labels();
- assert_eq!(successor_count, labels.len());
-
- // `Cleanup` is already included in successors
- let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
- let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
- write!(fmt, "unwind ")?;
- match self.unwind() {
- // Not needed or included in successors
- None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
- Some(UnwindAction::Continue) => write!(fmt, "continue"),
- Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
- Some(UnwindAction::Terminate(reason)) => {
- write!(fmt, "terminate({})", reason.as_short_str())
- }
- }
- };
-
- match (successor_count, show_unwind) {
- (0, false) => Ok(()),
- (0, true) => {
- write!(fmt, " -> ")?;
- fmt_unwind(fmt)
- }
- (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
- _ => {
- write!(fmt, " -> [")?;
- for (i, target) in self.successors().enumerate() {
- if i > 0 {
- write!(fmt, ", ")?;
- }
- write!(fmt, "{}: {:?}", labels[i], target)?;
- }
- if show_unwind {
- write!(fmt, ", ")?;
- fmt_unwind(fmt)?;
- }
- write!(fmt, "]")
- }
- }
- }
-}
-
-impl<'tcx> TerminatorKind<'tcx> {
- /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
- /// successor basic block, if any. The only information not included is the list of possible
- /// successors, which may be rendered differently between the text and the graphviz format.
- pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
- use self::TerminatorKind::*;
- match self {
- Goto { .. } => write!(fmt, "goto"),
- SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
- Return => write!(fmt, "return"),
- GeneratorDrop => write!(fmt, "generator_drop"),
- UnwindResume => write!(fmt, "resume"),
- UnwindTerminate(reason) => {
- write!(fmt, "abort({})", reason.as_short_str())
- }
- Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
- Unreachable => write!(fmt, "unreachable"),
- Drop { place, .. } => write!(fmt, "drop({place:?})"),
- Call { func, args, destination, .. } => {
- write!(fmt, "{destination:?} = ")?;
- write!(fmt, "{func:?}(")?;
- for (index, arg) in args.iter().enumerate() {
- if index > 0 {
- write!(fmt, ", ")?;
- }
- write!(fmt, "{arg:?}")?;
- }
- write!(fmt, ")")
- }
- Assert { cond, expected, msg, .. } => {
- write!(fmt, "assert(")?;
- if !expected {
- write!(fmt, "!")?;
- }
- write!(fmt, "{cond:?}, ")?;
- msg.fmt_assert_args(fmt)?;
- write!(fmt, ")")
- }
- FalseEdge { .. } => write!(fmt, "falseEdge"),
- FalseUnwind { .. } => write!(fmt, "falseUnwind"),
- InlineAsm { template, ref operands, options, .. } => {
- write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
- for op in operands {
- write!(fmt, ", ")?;
- let print_late = |&late| if late { "late" } else { "" };
- match op {
- InlineAsmOperand::In { reg, value } => {
- write!(fmt, "in({reg}) {value:?}")?;
- }
- InlineAsmOperand::Out { reg, late, place: Some(place) } => {
- write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
- }
- InlineAsmOperand::Out { reg, late, place: None } => {
- write!(fmt, "{}out({}) _", print_late(late), reg)?;
- }
- InlineAsmOperand::InOut {
- reg,
- late,
- in_value,
- out_place: Some(out_place),
- } => {
- write!(
- fmt,
- "in{}out({}) {:?} => {:?}",
- print_late(late),
- reg,
- in_value,
- out_place
- )?;
- }
- InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
- write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
- }
- InlineAsmOperand::Const { value } => {
- write!(fmt, "const {value:?}")?;
- }
- InlineAsmOperand::SymFn { value } => {
- write!(fmt, "sym_fn {value:?}")?;
- }
- InlineAsmOperand::SymStatic { def_id } => {
- write!(fmt, "sym_static {def_id:?}")?;
- }
- }
- }
- write!(fmt, ", options({options:?}))")
- }
- }
- }
-
- /// Returns the list of labels for the edges to the successor basic blocks.
- pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
- use self::TerminatorKind::*;
- match *self {
- Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
- Goto { .. } => vec!["".into()],
- SwitchInt { ref targets, .. } => targets
- .values
- .iter()
- .map(|&u| Cow::Owned(u.to_string()))
- .chain(iter::once("otherwise".into()))
- .collect(),
- Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
- vec!["return".into(), "unwind".into()]
- }
- Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
- Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
- Call { target: None, unwind: _, .. } => vec![],
- Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
- Yield { drop: None, .. } => vec!["resume".into()],
- Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
- Drop { unwind: _, .. } => vec!["return".into()],
- Assert { unwind: UnwindAction::Cleanup(_), .. } => {
- vec!["success".into(), "unwind".into()]
- }
- Assert { unwind: _, .. } => vec!["success".into()],
- FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
- FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
- vec!["real".into(), "unwind".into()]
- }
- FalseUnwind { unwind: _, .. } => vec!["real".into()],
- InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
- vec!["return".into(), "unwind".into()]
- }
- InlineAsm { destination: Some(_), unwind: _, .. } => {
- vec!["return".into()]
- }
- InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
- vec!["unwind".into()]
- }
- InlineAsm { destination: None, unwind: _, .. } => vec![],
- }
- }
-}
-
#[derive(Copy, Clone, Debug)]
pub enum TerminatorEdges<'mir, 'tcx> {
/// For terminators that have no successor, like `return`.
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 9b66622..247fcd2 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -121,16 +121,12 @@
[u8; size_of::<Result<mir::ConstantKind<'static>, mir::interpret::LitToConstError>>()];
}
-impl EraseType for Result<mir::interpret::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
- type Result = [u8; size_of::<
- Result<mir::interpret::ConstAlloc<'static>, mir::interpret::ErrorHandled>,
- >()];
+impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
+ type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
}
-impl EraseType for Result<mir::interpret::ConstValue<'_>, mir::interpret::ErrorHandled> {
- type Result = [u8; size_of::<
- Result<mir::interpret::ConstValue<'static>, mir::interpret::ErrorHandled>,
- >()];
+impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
+ type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
}
impl EraseType for Result<Option<ty::ValTree<'_>>, mir::interpret::ErrorHandled> {
@@ -317,8 +313,8 @@
rustc_middle::middle::exported_symbols::ExportedSymbol,
rustc_middle::mir::ConstantKind,
rustc_middle::mir::DestructuredConstant,
- rustc_middle::mir::interpret::ConstAlloc,
- rustc_middle::mir::interpret::ConstValue,
+ rustc_middle::mir::ConstAlloc,
+ rustc_middle::mir::ConstValue,
rustc_middle::mir::interpret::GlobalId,
rustc_middle::mir::interpret::LitToConstInput,
rustc_middle::traits::query::MethodAutoderefStepsResult,
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 01bdc4c..af4c57e 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -2,7 +2,6 @@
use crate::infer::canonical::Canonical;
use crate::mir;
-use crate::mir::interpret::ConstValue;
use crate::traits;
use crate::ty::fast_reject::SimplifiedType;
use crate::ty::layout::{TyAndLayout, ValidityRequirement};
@@ -369,7 +368,7 @@
}
}
-impl<'tcx> Key for (ConstValue<'tcx>, Ty<'tcx>) {
+impl<'tcx> Key for (mir::ConstValue<'tcx>, Ty<'tcx>) {
type CacheSelector = DefaultCacheSelector<Self>;
fn default_span(&self, _: TyCtxt<'_>) -> Span {
@@ -377,7 +376,7 @@
}
}
-impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
+impl<'tcx> Key for mir::ConstAlloc<'tcx> {
type CacheSelector = DefaultCacheSelector<Self>;
fn default_span(&self, _: TyCtxt<'_>) -> Span {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 9e358ea..b552956 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -21,7 +21,7 @@
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::{
- ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
+ EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
@@ -1091,7 +1091,7 @@
}
/// Converts a type level constant value into `ConstValue`
- query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> ConstValue<'tcx> {
+ query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to mir constant value"}
}
@@ -1104,14 +1104,14 @@
/// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
/// and its field values. This should only be used for pretty printing.
query try_destructure_mir_constant_for_diagnostics(
- key: (ConstValue<'tcx>, Ty<'tcx>)
+ key: (mir::ConstValue<'tcx>, Ty<'tcx>)
) -> Option<mir::DestructuredConstant<'tcx>> {
desc { "destructuring MIR constant"}
no_hash
eval_always
}
- query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
+ query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> mir::ConstValue<'tcx> {
desc { "getting a &core::panic::Location referring to a span" }
}
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index d8b3a06..c3ed408 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -15,9 +15,15 @@
}
#[derive(Eq, PartialEq)]
+pub enum GoalEvaluationKind {
+ Root,
+ Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+#[derive(Eq, PartialEq)]
pub struct GoalEvaluation<'tcx> {
pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
- pub is_normalizes_to_hack: IsNormalizesToHack,
+ pub kind: GoalEvaluationKind,
pub evaluation: CanonicalGoalEvaluation<'tcx>,
pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
@@ -25,12 +31,12 @@
#[derive(Eq, PartialEq)]
pub struct CanonicalGoalEvaluation<'tcx> {
pub goal: CanonicalInput<'tcx>,
- pub kind: GoalEvaluationKind<'tcx>,
+ pub kind: CanonicalGoalEvaluationKind<'tcx>,
pub result: QueryResult<'tcx>,
}
#[derive(Eq, PartialEq)]
-pub enum GoalEvaluationKind<'tcx> {
+pub enum CanonicalGoalEvaluationKind<'tcx> {
Overflow,
CacheHit(CacheHit),
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
@@ -52,22 +58,31 @@
pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
/// The actual evaluation of the goal, always `ProbeKind::Root`.
- pub evaluation: GoalCandidate<'tcx>,
+ pub evaluation: Probe<'tcx>,
}
+/// A self-contained computation during trait solving. This either
+/// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation
+/// of a goal.
#[derive(Eq, PartialEq)]
-pub struct GoalCandidate<'tcx> {
- pub added_goals_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
- pub candidates: Vec<GoalCandidate<'tcx>>,
+pub struct Probe<'tcx> {
+ pub steps: Vec<ProbeStep<'tcx>>,
pub kind: ProbeKind<'tcx>,
}
-impl Debug for GoalCandidate<'_> {
+impl Debug for Probe<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- ProofTreeFormatter::new(f).format_candidate(self)
+ ProofTreeFormatter::new(f).format_probe(self)
}
}
+#[derive(Eq, PartialEq)]
+pub enum ProbeStep<'tcx> {
+ AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+ EvaluateGoals(AddedGoalsEvaluation<'tcx>),
+ NestedProbe(Probe<'tcx>),
+}
+
#[derive(Debug, PartialEq, Eq)]
pub enum ProbeKind<'tcx> {
/// The root inference context while proving a goal.
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index d916e80..d33e83a 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -40,9 +40,12 @@
}
pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
- let goal_text = match eval.is_normalizes_to_hack {
- IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
- IsNormalizesToHack::No => "GOAL",
+ let goal_text = match eval.kind {
+ GoalEvaluationKind::Root => "ROOT GOAL",
+ GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
+ IsNormalizesToHack::No => "GOAL",
+ IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
+ },
};
writeln!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))?;
@@ -68,16 +71,16 @@
writeln!(self.f, "GOAL: {:?}", eval.goal)?;
match &eval.kind {
- GoalEvaluationKind::Overflow => {
+ CanonicalGoalEvaluationKind::Overflow => {
writeln!(self.f, "OVERFLOW: {:?}", eval.result)
}
- GoalEvaluationKind::CacheHit(CacheHit::Global) => {
+ CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
}
- GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
+ CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
}
- GoalEvaluationKind::Uncached { revisions } => {
+ CanonicalGoalEvaluationKind::Uncached { revisions } => {
for (n, step) in revisions.iter().enumerate() {
writeln!(self.f, "REVISION {n}")?;
self.nested(|this| this.format_evaluation_step(step))?;
@@ -92,11 +95,11 @@
evaluation_step: &GoalEvaluationStep<'_>,
) -> std::fmt::Result {
writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
- self.format_candidate(&evaluation_step.evaluation)
+ self.format_probe(&evaluation_step.evaluation)
}
- pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
- match &candidate.kind {
+ pub(super) fn format_probe(&mut self, probe: &Probe<'_>) -> std::fmt::Result {
+ match &probe.kind {
ProbeKind::Root { result } => {
writeln!(self.f, "ROOT RESULT: {result:?}")
}
@@ -118,11 +121,12 @@
}?;
self.nested(|this| {
- for candidate in &candidate.candidates {
- this.format_candidate(candidate)?;
- }
- for nested in &candidate.added_goals_evaluations {
- this.format_added_goals_evaluation(nested)?;
+ for step in &probe.steps {
+ match step {
+ ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
+ ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
+ ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
+ }
}
Ok(())
})
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index b4c6e0d..219927f 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -478,8 +478,8 @@
}
Err(err) => {
let msg = match err {
- ErrorHandled::Reported(_) => "enum discriminant evaluation failed",
- ErrorHandled::TooGeneric => "enum discriminant depends on generics",
+ ErrorHandled::Reported(..) => "enum discriminant evaluation failed",
+ ErrorHandled::TooGeneric(..) => "enum discriminant depends on generics",
};
tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg);
None
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 629f9f8..ba871d6 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -300,7 +300,7 @@
| ConstKind::Infer(_)
| ConstKind::Bound(_, _)
| ConstKind::Placeholder(_)
- | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric),
+ | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span.unwrap_or(DUMMY_SP))),
}
}
@@ -309,8 +309,8 @@
pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
match self.eval(tcx, param_env, None) {
Ok(val) => Self::new_value(tcx, val, self.ty()),
- Err(ErrorHandled::Reported(r)) => Self::new_error(tcx, r.into(), self.ty()),
- Err(ErrorHandled::TooGeneric) => self,
+ Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into(), self.ty()),
+ Err(ErrorHandled::TooGeneric(_span)) => self,
}
}
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 880d302..9ff4b64 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -82,6 +82,7 @@
impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtDef = ty::AdtDef<'tcx>;
type GenericArgsRef = ty::GenericArgsRef<'tcx>;
+ type GenericArg = ty::GenericArg<'tcx>;
type DefId = DefId;
type Binder<T> = Binder<'tcx, T>;
type Ty = Ty<'tcx>;
@@ -1214,6 +1215,25 @@
impl<'a, 'tcx> Lift<'tcx> for $ty {
type Lifted = $lifted;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+ // Assert that the set has the right type.
+ // Given an argument that has an interned type, the return type has the type of
+ // the corresponding interner set. This won't actually return anything, we're
+ // just doing this to compute said type!
+ fn _intern_set_ty_from_interned_ty<'tcx, Inner>(
+ _x: Interned<'tcx, Inner>,
+ ) -> InternedSet<'tcx, Inner> {
+ unreachable!()
+ }
+ fn _type_eq<T>(_x: &T, _y: &T) {}
+ fn _test<'tcx>(x: $lifted, tcx: TyCtxt<'tcx>) {
+ // If `x` is a newtype around an `Interned<T>`, then `interner` is an
+ // interner of appropriate type. (Ideally we'd also check that `x` is a
+ // newtype with just that one field. Not sure how to do that.)
+ let interner = _intern_set_ty_from_interned_ty(x.0);
+ // Now check that this is the same type as `interners.$set`.
+ _type_eq(&interner, &tcx.interners.$set);
+ }
+
tcx.interners
.$set
.contains_pointer_to(&InternedInSet(&*self.0.0))
@@ -1230,6 +1250,11 @@
impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> {
type Lifted = &'tcx List<$lifted>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+ // Assert that the set has the right type.
+ if false {
+ let _x: &InternedSet<'tcx, List<$lifted>> = &tcx.interners.$set;
+ }
+
if self.is_empty() {
return Some(List::empty());
}
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 9a0e72d..8b425ce 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1118,6 +1118,10 @@
fn is_unit(this: TyAndLayout<'tcx>) -> bool {
matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
}
+
+ fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
+ matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
+ }
}
/// Calculates whether a function's ABI can unwind or not.
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index e1d4e43..0ee63e9 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1713,6 +1713,21 @@
}
}
+pub(crate) fn pretty_print_const<'tcx>(
+ c: ty::Const<'tcx>,
+ fmt: &mut fmt::Formatter<'_>,
+ print_types: bool,
+) -> fmt::Result {
+ ty::tls::with(|tcx| {
+ let literal = tcx.lift(c).unwrap();
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let cx = cx.pretty_print_const(literal, print_types)?;
+ fmt.write_str(&cx.into_buffer())?;
+ Ok(())
+ })
+}
+
// HACK(eddyb) boxed to avoid moving around a large struct by-value.
pub struct FmtPrinter<'a, 'tcx>(Box<FmtPrinterData<'a, 'tcx>>);
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index edab7fe..a14d09b 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2945,6 +2945,33 @@
_ => false,
}
}
+
+ pub fn is_known_rigid(self) -> bool {
+ match self.kind() {
+ Bool
+ | Char
+ | Int(_)
+ | Uint(_)
+ | Float(_)
+ | Adt(_, _)
+ | Foreign(_)
+ | Str
+ | Array(_, _)
+ | Slice(_)
+ | RawPtr(_)
+ | Ref(_, _, _)
+ | FnDef(_, _)
+ | FnPtr(_)
+ | Dynamic(_, _, _)
+ | Closure(_, _)
+ | Generator(_, _, _)
+ | GeneratorWitness(_)
+ | GeneratorWitnessMIR(_, _)
+ | Never
+ | Tuple(_) => true,
+ Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
+ }
+ }
}
/// Extra information about why we ended up with a particular variance.
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index da5f2b1..0eff2df 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -1,4 +1,4 @@
-use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::ty::cast::mir_cast_kind;
use rustc_middle::{mir::*, thir::*, ty};
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index aaa3744..22712fd 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -3,9 +3,7 @@
use crate::build::{parse_float_into_constval, Builder};
use rustc_ast as ast;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{
- Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
-};
+use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::{
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 4e10916..7748ffa 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -15,7 +15,6 @@
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::middle::region;
-use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::{
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 0ea61ec..3d9e634 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -18,7 +18,7 @@
use rustc_hir::RangeEnd;
use rustc_index::Idx;
use rustc_middle::mir::interpret::{
- ConstValue, ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
+ ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
};
use rustc_middle::mir::{self, ConstantKind, UserTypeProjection};
use rustc_middle::mir::{BorrowKind, Mutability};
@@ -566,7 +566,7 @@
pattern
}
}
- Err(ErrorHandled::TooGeneric) => {
+ Err(ErrorHandled::TooGeneric(_)) => {
// While `Reported | Linted` cases will have diagnostics emitted already
// it is not true for TooGeneric case, so we need to give user more information.
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
@@ -640,14 +640,14 @@
.kind
} else {
// If that fails, convert it to an opaque constant pattern.
- match tcx.const_eval_resolve(self.param_env, uneval, None) {
+ match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) {
Ok(val) => self.const_to_pat(mir::ConstantKind::Val(val, ty), id, span, None).kind,
- Err(ErrorHandled::TooGeneric) => {
+ Err(ErrorHandled::TooGeneric(_)) => {
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
PatKind::Wild
}
- Err(ErrorHandled::Reported(_)) => PatKind::Wild,
+ Err(ErrorHandled::Reported(..)) => PatKind::Wild,
}
}
}
@@ -855,8 +855,8 @@
ty::Float(_) | ty::Int(_) => {} // require special handling, see below
_ => match (a, b) {
(
- mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(a)), _a_ty),
- mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
+ mir::ConstantKind::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _a_ty),
+ mir::ConstantKind::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _b_ty),
) => return Some(a.cmp(&b)),
(mir::ConstantKind::Ty(a), mir::ConstantKind::Ty(b)) => {
return Some(a.kind().cmp(&b.kind()));
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 4892ace..fe66a9a 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -4,7 +4,7 @@
use rustc_index::IndexVec;
use rustc_middle::mir::*;
use rustc_middle::mir::{
- interpret::{ConstValue, Scalar},
+ interpret::Scalar,
visit::{PlaceContext, Visitor},
};
use rustc_middle::ty::{Ty, TyCtxt, TypeAndMut};
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 00e3e3a..c6aac2c 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -22,8 +22,8 @@
use crate::dataflow_const_prop::Patch;
use crate::MirPass;
use rustc_const_eval::interpret::{
- self, compile_time_machine, AllocId, ConstAllocation, ConstValue, FnArg, Frame, ImmTy,
- Immediate, InterpCx, InterpResult, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, StackPopCleanup,
+ self, compile_time_machine, AllocId, ConstAllocation, FnArg, Frame, ImmTy, Immediate, InterpCx,
+ InterpResult, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, StackPopCleanup,
};
/// The maximum number of bytes that we'll allocate space for a local or the return value.
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index b52827a..fb33b3b 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -105,25 +105,12 @@
trace!("ConstProp starting for {:?}", def_id);
- let dummy_body = &Body::new(
- body.source,
- (*body.basic_blocks).to_owned(),
- body.source_scopes.clone(),
- body.local_decls.clone(),
- Default::default(),
- body.arg_count,
- Default::default(),
- body.span,
- body.generator_kind(),
- body.tainted_by_errors,
- );
-
// FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
// constants, instead of just checking for const-folding succeeding.
// That would require a uniform one-def no-mutation analysis
// and RPO (or recursing when needing the value of a local).
- let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx);
- optimization_finder.visit_body(body);
+ let mut linter = ConstPropagator::new(body, tcx);
+ linter.visit_body(body);
trace!("ConstProp done for {:?}", def_id);
}
@@ -169,11 +156,7 @@
}
impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
- fn new(
- body: &Body<'tcx>,
- dummy_body: &'mir Body<'tcx>,
- tcx: TyCtxt<'tcx>,
- ) -> ConstPropagator<'mir, 'tcx> {
+ fn new(body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>) -> ConstPropagator<'mir, 'tcx> {
let def_id = body.source.def_id();
let args = &GenericArgs::identity_for_item(tcx, def_id);
let param_env = tcx.param_env_reveal_all_normalized(def_id);
@@ -204,7 +187,7 @@
ecx.push_stack_frame(
Instance::new(def_id, args),
- dummy_body,
+ body,
&ret,
StackPopCleanup::Root { cleanup: false },
)
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 6046169..b6b0463 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -199,12 +199,8 @@
}
#[inline(always)]
- pub fn rank_partial_cmp(
- &self,
- a: BasicCoverageBlock,
- b: BasicCoverageBlock,
- ) -> Option<Ordering> {
- self.dominators.as_ref().unwrap().rank_partial_cmp(a, b)
+ pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering {
+ self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
}
}
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 717763a..32e8ca2 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -12,7 +12,6 @@
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
use std::cell::OnceCell;
-use std::cmp::Ordering;
#[derive(Debug, Copy, Clone)]
pub(super) enum CoverageStatement {
@@ -333,30 +332,21 @@
initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span));
- initial_spans.sort_unstable_by(|a, b| {
- if a.span.lo() == b.span.lo() {
- if a.span.hi() == b.span.hi() {
- if a.is_in_same_bcb(b) {
- Some(Ordering::Equal)
- } else {
- // Sort equal spans by dominator relationship (so dominators always come
- // before the dominated equal spans). When later comparing two spans in
- // order, the first will either dominate the second, or they will have no
- // dominator relationship.
- self.basic_coverage_blocks.rank_partial_cmp(a.bcb, b.bcb)
- }
- } else {
- // Sort hi() in reverse order so shorter spans are attempted after longer spans.
- // This guarantees that, if a `prev` span overlaps, and is not equal to, a
- // `curr` span, the prev span either extends further left of the curr span, or
- // they start at the same position and the prev span extends further right of
- // the end of the curr span.
- b.span.hi().partial_cmp(&a.span.hi())
- }
- } else {
- a.span.lo().partial_cmp(&b.span.lo())
- }
- .unwrap()
+ initial_spans.sort_by(|a, b| {
+ // First sort by span start.
+ Ord::cmp(&a.span.lo(), &b.span.lo())
+ // If span starts are the same, sort by span end in reverse order.
+ // This ensures that if spans A and B are adjacent in the list,
+ // and they overlap but are not equal, then either:
+ // - Span A extends further left, or
+ // - Both have the same start and span A extends further right
+ .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
+ // If both spans are equal, sort the BCBs in dominator order,
+ // so that dominating BCBs come before other BCBs they dominate.
+ .then_with(|| self.basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
+ // If two spans are otherwise identical, put closure spans first,
+ // as this seems to be what the refinement step expects.
+ .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
});
initial_spans
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index c7c5f17..cf827c9 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -6,7 +6,7 @@
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
-use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ConstValue, InterpResult, Scalar};
+use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar};
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::layout::TyAndLayout;
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 8afbe41..fc49c9b 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -153,7 +153,7 @@
span,
user_ty: None,
literal: ConstantKind::Val(
- interpret::ConstValue::Indirect { alloc_id, offset: Size::ZERO },
+ ConstValue::Indirect { alloc_id, offset: Size::ZERO },
tmp_ty,
),
};
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index c13bafa..dcc4cd8 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -1,7 +1,6 @@
//! Removes operations on ZST places, and convert ZST operands to constants.
use crate::MirPass;
-use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index f5cfc41..67e821d 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -170,8 +170,7 @@
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::lang_items::LangItem;
-use rustc_middle::mir::interpret::{AllocId, ConstValue};
-use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
+use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::mir::visit::Visitor as MirVisitor;
use rustc_middle::mir::{self, Local, Location};
@@ -752,8 +751,8 @@
let param_env = ty::ParamEnv::reveal_all();
let val = match literal.eval(self.tcx, param_env, None) {
Ok(v) => v,
- Err(ErrorHandled::Reported(_)) => return,
- Err(ErrorHandled::TooGeneric) => span_bug!(
+ Err(ErrorHandled::Reported(..)) => return,
+ Err(ErrorHandled::TooGeneric(..)) => span_bug!(
self.body.source_info(location).span,
"collection encountered polymorphic constant: {:?}",
literal
@@ -1442,13 +1441,15 @@
#[instrument(skip(tcx, output), level = "debug")]
fn collect_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
- value: ConstValue<'tcx>,
+ value: mir::ConstValue<'tcx>,
output: &mut MonoItems<'tcx>,
) {
match value {
- ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_alloc(tcx, ptr.provenance, output),
- ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
- ConstValue::Slice { data, start: _, end: _ } => {
+ mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => {
+ collect_alloc(tcx, ptr.provenance, output)
+ }
+ mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
+ mir::ConstValue::Slice { data, start: _, end: _ } => {
for &id in data.inner().provenance().ptrs().values() {
collect_alloc(tcx, id, output);
}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 73fd796..f2c8f0b 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1536,6 +1536,8 @@
"generate human-readable, predictable names for codegen units (default: no)"),
identify_regions: bool = (false, parse_bool, [UNTRACKED],
"display unnamed regions as `'<id>`, using a non-ident unique id (default: no)"),
+ ignore_directory_in_diagnostics_source_blocks: Vec<String> = (Vec::new(), parse_string_push, [UNTRACKED],
+ "do not display the source code block in diagnostics for files in the directory"),
incremental_ignore_spans: bool = (false, parse_bool, [TRACKED],
"ignore spans during ICH computation -- used for testing (default: no)"),
incremental_info: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 9bff901..86f4e7b 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1295,7 +1295,10 @@
.diagnostic_width(sopts.diagnostic_width)
.macro_backtrace(macro_backtrace)
.track_diagnostics(track_diagnostics)
- .terminal_url(terminal_url);
+ .terminal_url(terminal_url)
+ .ignored_directories_in_source_blocks(
+ sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
+ );
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
}
}
@@ -1312,7 +1315,10 @@
track_diagnostics,
terminal_url,
)
- .ui_testing(sopts.unstable_opts.ui_testing),
+ .ui_testing(sopts.unstable_opts.ui_testing)
+ .ignored_directories_in_source_blocks(
+ sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
+ ),
),
}
}
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index 35e65c1..58b695f 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -1,4 +1,7 @@
-use rustc_middle::mir::interpret::{alloc_range, AllocRange, ConstValue, Pointer};
+use rustc_middle::mir::{
+ interpret::{alloc_range, AllocRange, Pointer},
+ ConstValue,
+};
use crate::{
rustc_smir::{Stable, Tables},
diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs
index d90dce2..93a2045 100644
--- a/compiler/rustc_target/src/abi/call/riscv.rs
+++ b/compiler/rustc_target/src/abi/call/riscv.rs
@@ -89,6 +89,17 @@
}
FieldsShape::Union(_) => {
if !arg_layout.is_zst() {
+ if arg_layout.is_transparent() {
+ let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
+ return should_use_fp_conv_helper(
+ cx,
+ &non_1zst_elem,
+ xlen,
+ flen,
+ field1_kind,
+ field2_kind,
+ );
+ }
return Err(CannotUseFpConv);
}
}
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 636adcf..74fe989 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -66,6 +66,7 @@
fn is_never(this: TyAndLayout<'a, Self>) -> bool;
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
}
impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -136,6 +137,13 @@
Ty::is_unit(self)
}
+ pub fn is_transparent<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_transparent(self)
+ }
+
pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
where
Ty: TyAbiInterface<'a, C>,
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs
index e2df7e0..b29ab14 100644
--- a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs
@@ -1,5 +1,5 @@
use super::apple_base::{opts, Arch};
-use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions};
+use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions};
pub fn target() -> Target {
let llvm_target = "arm64-apple-ios14.0-macabi";
@@ -7,6 +7,7 @@
let arch = Arch::Arm64_macabi;
let mut base = opts("ios", arch);
base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]);
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
Target {
llvm_target: llvm_target.into(),
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
index 50f359c3..fd1926f 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs
@@ -1,5 +1,5 @@
use super::apple_base::{opts, Arch};
-use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
+use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let llvm_target = "x86_64-apple-ios14.0-macabi";
@@ -7,6 +7,7 @@
let arch = Arch::X86_64_macabi;
let mut base = opts("ios", arch);
base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]);
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
Target {
llvm_target: llvm_target.into(),
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 307c051..7941f64 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -28,8 +28,8 @@
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use super::inspect::ProofTreeBuilder;
-use super::search_graph;
use super::SolverMode;
+use super::{search_graph, GoalEvaluationKind};
use super::{search_graph::SearchGraph, Goal};
pub use select::InferCtxtSelectExt;
@@ -85,7 +85,7 @@
// evaluation code.
tainted: Result<(), NoSolution>,
- inspect: ProofTreeBuilder<'tcx>,
+ pub(super) inspect: ProofTreeBuilder<'tcx>,
}
#[derive(Debug, Clone)]
@@ -164,7 +164,7 @@
Option<inspect::GoalEvaluation<'tcx>>,
) {
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
- ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+ ecx.evaluate_goal(GoalEvaluationKind::Root, goal)
})
}
}
@@ -340,11 +340,11 @@
/// been constrained and the certainty of the result.
fn evaluate_goal(
&mut self,
- is_normalizes_to_hack: IsNormalizesToHack,
+ goal_evaluation_kind: GoalEvaluationKind,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+ let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, goal_evaluation_kind);
let encountered_overflow = self.search_graph.encountered_overflow();
let canonical_response = EvalCtxt::evaluate_canonical_goal(
self.tcx(),
@@ -389,7 +389,10 @@
// solver cycle.
if cfg!(debug_assertions)
&& has_changed
- && is_normalizes_to_hack == IsNormalizesToHack::No
+ && !matches!(
+ goal_evaluation_kind,
+ GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }
+ )
&& !self.search_graph.in_cycle()
{
// The nested evaluation has to happen with the original state
@@ -561,8 +564,10 @@
},
);
- let (_, certainty, instantiate_goals) =
- self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?;
+ let (_, certainty, instantiate_goals) = self.evaluate_goal(
+ GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
+ unconstrained_goal,
+ )?;
self.add_goals(instantiate_goals);
// Finally, equate the goal's RHS with the unconstrained var.
@@ -596,8 +601,10 @@
}
for goal in goals.goals.drain(..) {
- let (has_changed, certainty, instantiate_goals) =
- self.evaluate_goal(IsNormalizesToHack::No, goal)?;
+ let (has_changed, certainty, instantiate_goals) = self.evaluate_goal(
+ GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
+ goal,
+ )?;
self.add_goals(instantiate_goals);
if has_changed {
unchanged_certainty = None;
@@ -949,8 +956,10 @@
use rustc_middle::mir::interpret::ErrorHandled;
match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
Ok(ct) => Some(ct),
- Err(ErrorHandled::Reported(e)) => Some(ty::Const::new_error(self.tcx(), e.into(), ty)),
- Err(ErrorHandled::TooGeneric) => None,
+ Err(ErrorHandled::Reported(e, _)) => {
+ Some(ty::Const::new_error(self.tcx(), e.into(), ty))
+ }
+ Err(ErrorHandled::TooGeneric(_)) => None,
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index f88cfba..6087b91 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -24,13 +24,13 @@
search_graph: outer_ecx.search_graph,
nested_goals: outer_ecx.nested_goals.clone(),
tainted: outer_ecx.tainted,
- inspect: outer_ecx.inspect.new_goal_candidate(),
+ inspect: outer_ecx.inspect.new_probe(),
};
let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
if !outer_ecx.inspect.is_noop() {
let probe_kind = probe_kind(&r);
nested_ecx.inspect.probe_kind(probe_kind);
- outer_ecx.inspect.goal_candidate(nested_ecx.inspect);
+ outer_ecx.inspect.finish_probe(nested_ecx.inspect);
}
r
}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 46025da..749bba3 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -7,13 +7,13 @@
use rustc_session::config::DumpSolverProofTree;
use super::eval_ctxt::UseGlobalCache;
-use super::GenerateProofTree;
+use super::{GenerateProofTree, GoalEvaluationKind};
#[derive(Eq, PartialEq, Debug)]
pub struct WipGoalEvaluation<'tcx> {
pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ pub kind: WipGoalEvaluationKind,
pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>,
- pub is_normalizes_to_hack: IsNormalizesToHack,
pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
@@ -21,8 +21,13 @@
pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
inspect::GoalEvaluation {
uncanonicalized_goal: self.uncanonicalized_goal,
+ kind: match self.kind {
+ WipGoalEvaluationKind::Root => inspect::GoalEvaluationKind::Root,
+ WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+ inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack }
+ }
+ },
evaluation: self.evaluation.unwrap().finalize(),
- is_normalizes_to_hack: self.is_normalizes_to_hack,
returned_goals: self.returned_goals,
}
}
@@ -30,6 +35,12 @@
#[derive(Eq, PartialEq, Debug)]
pub enum WipGoalEvaluationKind {
+ Root,
+ Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+#[derive(Eq, PartialEq, Debug)]
+pub enum WipCanonicalGoalEvaluationKind {
Overflow,
CacheHit(CacheHit),
}
@@ -37,7 +48,7 @@
#[derive(Eq, PartialEq, Debug)]
pub struct WipCanonicalGoalEvaluation<'tcx> {
pub goal: CanonicalInput<'tcx>,
- pub kind: Option<WipGoalEvaluationKind>,
+ pub kind: Option<WipCanonicalGoalEvaluationKind>,
pub revisions: Vec<WipGoalEvaluationStep<'tcx>>,
pub result: Option<QueryResult<'tcx>>,
}
@@ -45,11 +56,13 @@
impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
pub fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
let kind = match self.kind {
- Some(WipGoalEvaluationKind::Overflow) => inspect::GoalEvaluationKind::Overflow,
- Some(WipGoalEvaluationKind::CacheHit(hit)) => {
- inspect::GoalEvaluationKind::CacheHit(hit)
+ Some(WipCanonicalGoalEvaluationKind::Overflow) => {
+ inspect::CanonicalGoalEvaluationKind::Overflow
}
- None => inspect::GoalEvaluationKind::Uncached {
+ Some(WipCanonicalGoalEvaluationKind::CacheHit(hit)) => {
+ inspect::CanonicalGoalEvaluationKind::CacheHit(hit)
+ }
+ None => inspect::CanonicalGoalEvaluationKind::Uncached {
revisions: self
.revisions
.into_iter()
@@ -87,7 +100,7 @@
pub struct WipGoalEvaluationStep<'tcx> {
pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
- pub evaluation: WipGoalCandidate<'tcx>,
+ pub evaluation: WipProbe<'tcx>,
}
impl<'tcx> WipGoalEvaluationStep<'tcx> {
@@ -102,26 +115,37 @@
}
#[derive(Eq, PartialEq, Debug)]
-pub struct WipGoalCandidate<'tcx> {
- pub added_goals_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
- pub candidates: Vec<WipGoalCandidate<'tcx>>,
+pub struct WipProbe<'tcx> {
+ pub steps: Vec<WipProbeStep<'tcx>>,
pub kind: Option<ProbeKind<'tcx>>,
}
-impl<'tcx> WipGoalCandidate<'tcx> {
- pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
- inspect::GoalCandidate {
- added_goals_evaluations: self
- .added_goals_evaluations
- .into_iter()
- .map(WipAddedGoalsEvaluation::finalize)
- .collect(),
- candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+impl<'tcx> WipProbe<'tcx> {
+ pub fn finalize(self) -> inspect::Probe<'tcx> {
+ inspect::Probe {
+ steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
kind: self.kind.unwrap(),
}
}
}
+#[derive(Eq, PartialEq, Debug)]
+pub enum WipProbeStep<'tcx> {
+ AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+ EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
+ NestedProbe(WipProbe<'tcx>),
+}
+
+impl<'tcx> WipProbeStep<'tcx> {
+ pub fn finalize(self) -> inspect::ProbeStep<'tcx> {
+ match self {
+ WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
+ WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
+ WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
+ }
+ }
+}
+
#[derive(Debug)]
pub enum DebugSolver<'tcx> {
Root,
@@ -129,7 +153,7 @@
CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
- GoalCandidate(WipGoalCandidate<'tcx>),
+ Probe(WipProbe<'tcx>),
}
impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
@@ -156,9 +180,9 @@
}
}
-impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
- fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> {
- DebugSolver::GoalCandidate(g)
+impl<'tcx> From<WipProbe<'tcx>> for DebugSolver<'tcx> {
+ fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::Probe(p)
}
}
@@ -249,15 +273,20 @@
self.state.is_none()
}
- pub fn new_goal_evaluation(
+ pub(super) fn new_goal_evaluation(
&mut self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
- is_normalizes_to_hack: IsNormalizesToHack,
+ kind: GoalEvaluationKind,
) -> ProofTreeBuilder<'tcx> {
self.nested(|| WipGoalEvaluation {
uncanonicalized_goal: goal,
+ kind: match kind {
+ GoalEvaluationKind::Root => WipGoalEvaluationKind::Root,
+ GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+ WipGoalEvaluationKind::Nested { is_normalizes_to_hack }
+ }
+ },
evaluation: None,
- is_normalizes_to_hack,
returned_goals: vec![],
})
}
@@ -286,7 +315,7 @@
}
}
- pub fn goal_evaluation_kind(&mut self, kind: WipGoalEvaluationKind) {
+ pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) {
if let Some(this) = self.as_mut() {
match this {
DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
@@ -329,11 +358,7 @@
) -> ProofTreeBuilder<'tcx> {
self.nested(|| WipGoalEvaluationStep {
instantiated_goal,
- evaluation: WipGoalCandidate {
- added_goals_evaluations: vec![],
- candidates: vec![],
- kind: None,
- },
+ evaluation: WipProbe { steps: vec![], kind: None },
})
}
pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
@@ -350,18 +375,14 @@
}
}
- pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
- self.nested(|| WipGoalCandidate {
- added_goals_evaluations: vec![],
- candidates: vec![],
- kind: None,
- })
+ pub fn new_probe(&mut self) -> ProofTreeBuilder<'tcx> {
+ self.nested(|| WipProbe { steps: vec![], kind: None })
}
pub fn probe_kind(&mut self, probe_kind: ProbeKind<'tcx>) {
if let Some(this) = self.as_mut() {
match this {
- DebugSolver::GoalCandidate(this) => {
+ DebugSolver::Probe(this) => {
assert_eq!(this.kind.replace(probe_kind), None)
}
_ => unreachable!(),
@@ -369,17 +390,32 @@
}
}
- pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
+ pub fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
if let Some(this) = self.as_mut() {
- match (this, candidate.state.unwrap().tree) {
+ match this {
+ DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+ evaluation: WipProbe { steps, .. },
+ ..
+ })
+ | DebugSolver::Probe(WipProbe { steps, .. }) => {
+ steps.push(WipProbeStep::AddGoal(goal))
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, probe.state.unwrap().tree) {
(
- DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
+ DebugSolver::Probe(WipProbe { steps, .. })
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
- evaluation: WipGoalCandidate { candidates, .. },
+ evaluation: WipProbe { steps, .. },
..
}),
- DebugSolver::GoalCandidate(candidate),
- ) => candidates.push(candidate),
+ DebugSolver::Probe(probe),
+ ) => steps.push(WipProbeStep::NestedProbe(probe)),
_ => unreachable!(),
}
}
@@ -416,14 +452,12 @@
match (this, added_goals_evaluation.state.unwrap().tree) {
(
DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
- evaluation: WipGoalCandidate { added_goals_evaluations, .. },
+ evaluation: WipProbe { steps, .. },
..
})
- | DebugSolver::GoalCandidate(WipGoalCandidate {
- added_goals_evaluations, ..
- }),
+ | DebugSolver::Probe(WipProbe { steps, .. }),
DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
- ) => added_goals_evaluations.push(added_goals_evaluation),
+ ) => steps.push(WipProbeStep::EvaluateGoals(added_goals_evaluation)),
_ => unreachable!(),
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index c492408..bd612ce 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -19,7 +19,8 @@
use rustc_infer::traits::query::NoSolution;
use rustc_middle::infer::canonical::CanonicalVarInfos;
use rustc_middle::traits::solve::{
- CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response,
+ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult,
+ Response,
};
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{
@@ -59,6 +60,12 @@
Coherence,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum GoalEvaluationKind {
+ Root,
+ Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
trait CanonicalResponseExt {
fn has_no_inference_or_external_constraints(&self) -> bool;
@@ -228,6 +235,7 @@
#[instrument(level = "debug", skip(self))]
fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
+ self.inspect.add_goal(goal);
self.nested_goals.goals.push(goal);
}
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index c816b51..16de518 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -187,7 +187,7 @@
last.encountered_overflow = true;
}
- inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::Overflow);
+ inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
};
@@ -203,7 +203,7 @@
available_depth,
)
{
- inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+ inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
CacheHit::Global,
));
self.on_cache_hit(reached_depth, encountered_overflow);
@@ -240,7 +240,7 @@
// Finally we can return either the provisional response for that goal if we have a
// coinductive cycle or an ambiguous result if the cycle is inductive.
Entry::Occupied(entry_index) => {
- inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+ inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
CacheHit::Provisional,
));
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index ba5000d..8096d79 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -793,7 +793,7 @@
span: tcx.def_span(unevaluated.def),
unevaluated: unevaluated,
});
- Err(ErrorHandled::Reported(reported.into()))
+ Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def)))
}
Err(err) => Err(err),
}
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 3d0d381..62ab1e1 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -73,13 +73,13 @@
ty::ConstKind::Unevaluated(uv) => {
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
match concrete {
- Err(ErrorHandled::TooGeneric) => {
+ Err(ErrorHandled::TooGeneric(_)) => {
Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
span,
"Missing value for constant, but no error reported?",
)))
}
- Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())),
+ Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())),
Ok(_) => Ok(()),
}
}
@@ -132,7 +132,7 @@
.emit()
}
- Err(ErrorHandled::TooGeneric) => {
+ Err(ErrorHandled::TooGeneric(_)) => {
let err = if uv.has_non_region_infer() {
NotConstEvaluatable::MentionsInfer
} else if uv.has_non_region_param() {
@@ -147,7 +147,7 @@
Err(err)
}
- Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e.into())),
+ Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())),
Ok(_) => Ok(()),
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 1354106..7d754cb 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -986,6 +986,8 @@
}
}
+ self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
+
// Return early if the trait is Debug or Display and the invocation
// originates within a standard library macro, because the output
// is otherwise overwhelming and unhelpful (see #85844 for an
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index d6ac720..a08ebe5 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -406,6 +406,14 @@
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
);
+
+ fn explain_hrtb_projection(
+ &self,
+ diag: &mut Diagnostic,
+ pred: ty::PolyTraitPredicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ cause: &ObligationCause<'tcx>,
+ );
}
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -4027,6 +4035,71 @@
}
}
}
+
+ fn explain_hrtb_projection(
+ &self,
+ diag: &mut Diagnostic,
+ pred: ty::PolyTraitPredicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ cause: &ObligationCause<'tcx>,
+ ) {
+ if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer()
+ {
+ self.probe(|_| {
+ let ocx = ObligationCtxt::new(self);
+ let pred = self.instantiate_binder_with_placeholders(pred);
+ let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
+ ocx.register_obligation(Obligation::new(
+ self.tcx,
+ ObligationCause::dummy(),
+ param_env,
+ pred,
+ ));
+ if !ocx.select_where_possible().is_empty() {
+ // encountered errors.
+ return;
+ }
+
+ if let ObligationCauseCode::FunctionArgumentObligation {
+ call_hir_id,
+ arg_hir_id,
+ parent_code: _,
+ } = cause.code()
+ {
+ let arg_span = self.tcx.hir().span(*arg_hir_id);
+ let mut sp: MultiSpan = arg_span.into();
+
+ sp.push_span_label(
+ arg_span,
+ "the trait solver is unable to infer the \
+ generic types that should be inferred from this argument",
+ );
+ sp.push_span_label(
+ self.tcx.hir().span(*call_hir_id),
+ "add turbofish arguments to this call to \
+ specify the types manually, even if it's redundant",
+ );
+ diag.span_note(
+ sp,
+ "this is a known limitation of the trait solver that \
+ will be lifted in the future",
+ );
+ } else {
+ let mut sp: MultiSpan = cause.span.into();
+ sp.push_span_label(
+ cause.span,
+ "try adding turbofish arguments to this expression to \
+ specify the types manually, even if it's redundant",
+ );
+ diag.span_note(
+ sp,
+ "this is a known limitation of the trait solver that \
+ will be lifted in the future",
+ );
+ }
+ });
+ }
+ }
}
/// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index f177945..da357da 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -560,30 +560,31 @@
let stalled_on = &mut pending_obligation.stalled_on;
- let mut evaluate =
- |c: Const<'tcx>| {
- if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
- match self.selcx.infcx.try_const_eval_resolve(
- obligation.param_env,
- unevaluated,
- c.ty(),
- Some(obligation.cause.span),
- ) {
- Ok(val) => Ok(val),
- Err(e) => match e {
- ErrorHandled::TooGeneric => {
+ let mut evaluate = |c: Const<'tcx>| {
+ if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() {
+ match self.selcx.infcx.try_const_eval_resolve(
+ obligation.param_env,
+ unevaluated,
+ c.ty(),
+ Some(obligation.cause.span),
+ ) {
+ Ok(val) => Ok(val),
+ Err(e) => {
+ match e {
+ ErrorHandled::TooGeneric(..) => {
stalled_on.extend(unevaluated.args.iter().filter_map(
TyOrConstInferVar::maybe_from_generic_arg,
));
- Err(ErrorHandled::TooGeneric)
}
- _ => Err(e),
- },
+ _ => {}
+ }
+ Err(e)
}
- } else {
- Ok(c)
}
- };
+ } else {
+ Ok(c)
+ }
+ };
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
@@ -603,13 +604,14 @@
),
}
}
- (Err(ErrorHandled::Reported(reported)), _)
- | (_, Err(ErrorHandled::Reported(reported))) => ProcessResult::Error(
+ (Err(ErrorHandled::Reported(reported, _)), _)
+ | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error(
CodeSelectionError(SelectionError::NotConstEvaluatable(
NotConstEvaluatable::Error(reported.into()),
)),
),
- (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
+ (Err(ErrorHandled::TooGeneric(_)), _)
+ | (_, Err(ErrorHandled::TooGeneric(_))) => {
if c1.has_non_region_infer() || c2.has_non_region_infer() {
ProcessResult::Unchanged
} else {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 0d84fee..24d3163 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -989,9 +989,10 @@
Err(_) => Ok(EvaluatedToErr),
}
}
- (Err(ErrorHandled::Reported(_)), _)
- | (_, Err(ErrorHandled::Reported(_))) => Ok(EvaluatedToErr),
- (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
+ (Err(ErrorHandled::Reported(..)), _)
+ | (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr),
+ (Err(ErrorHandled::TooGeneric(..)), _)
+ | (_, Err(ErrorHandled::TooGeneric(..))) => {
if c1.has_non_region_infer() || c2.has_non_region_infer() {
Ok(EvaluatedToAmbig)
} else {
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 0dbac56..01bb1ca 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -3,10 +3,13 @@
use rustc_middle::query::Providers;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::{
normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
};
-use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
+use rustc_trait_selection::traits::{
+ self, FulfillmentErrorCode, ObligationCause, SelectionContext,
+};
use std::sync::atomic::Ordering;
pub(crate) fn provide(p: &mut Providers) {
@@ -40,6 +43,27 @@
&mut obligations,
);
ocx.register_obligations(obligations);
+ // #112047: With projections and opaques, we are able to create opaques that
+ // are recursive (given some substitution of the opaque's type variables).
+ // In that case, we may only realize a cycle error when calling
+ // `normalize_erasing_regions` in mono.
+ if !ocx.infcx.next_trait_solver() {
+ let errors = ocx.select_where_possible();
+ if !errors.is_empty() {
+ // Rustdoc may attempt to normalize type alias types which are not
+ // well-formed. Rustdoc also normalizes types that are just not
+ // well-formed, since we don't do as much HIR analysis (checking
+ // that impl vars are constrained by the signature, for example).
+ if !tcx.sess.opts.actually_rustdoc {
+ for error in &errors {
+ if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code {
+ ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
+ }
+ }
+ }
+ return Err(NoSolution);
+ }
+ }
// FIXME(associated_const_equality): All users of normalize_projection_ty expected
// a type, but there is the possibility it could've been a const now. Maybe change
// it to a Term later?
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index da2958b..91f1c21 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -141,11 +141,34 @@
false
}
};
-
if !eligible {
return Ok(None);
}
+ // HACK: We may have overlapping `dyn Trait` built-in impls and
+ // user-provided blanket impls. Detect that case here, and return
+ // ambiguity.
+ //
+ // This should not affect totally monomorphized contexts, only
+ // resolve calls that happen polymorphically, such as the mir-inliner
+ // and const-prop (and also some lints).
+ let self_ty = rcvr_args.type_at(0);
+ if !self_ty.is_known_rigid() {
+ let predicates = tcx
+ .predicates_of(impl_data.impl_def_id)
+ .instantiate(tcx, impl_data.args)
+ .predicates;
+ let sized_def_id = tcx.lang_items().sized_trait();
+ // If we find a `Self: Sized` bound on the item, then we know
+ // that `dyn Trait` can certainly never apply here.
+ if !predicates.into_iter().filter_map(ty::Clause::as_trait_clause).any(|clause| {
+ Some(clause.def_id()) == sized_def_id
+ && clause.skip_binder().self_ty() == self_ty
+ }) {
+ return Ok(None);
+ }
+ }
+
// Any final impl is required to define all associated items.
if !leaf_def.item.defaultness(tcx).has_value() {
let guard = tcx.sess.delay_span_bug(
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 904f1b3..a03b823 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -36,6 +36,9 @@
let (param_env, ty) = query.into_parts();
debug!(?ty);
+ // Optimization: We convert to RevealAll and convert opaque types in the where bounds
+ // to their hidden types. This reduces overall uncached invocations of `layout_of` and
+ // is thus a small performance improvement.
let param_env = param_env.with_reveal_all_normalized(tcx);
let unnormalized_ty = ty;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index e348591..5df068d 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -41,7 +41,12 @@
pub trait Interner: Sized {
type AdtDef: Clone + Debug + Hash + Ord;
- type GenericArgsRef: Clone + DebugWithInfcx<Self> + Hash + Ord;
+ type GenericArgsRef: Clone
+ + DebugWithInfcx<Self>
+ + Hash
+ + Ord
+ + IntoIterator<Item = Self::GenericArg>;
+ type GenericArg: Clone + DebugWithInfcx<Self> + Hash + Ord;
type DefId: Clone + Debug + Hash + Ord;
type Binder<T>;
type Ty: Clone + DebugWithInfcx<Self> + Hash + Ord;
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index 72bd50a..b574cdc 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -517,7 +517,21 @@
Int(i) => write!(f, "{i:?}"),
Uint(u) => write!(f, "{u:?}"),
Float(float) => write!(f, "{float:?}"),
- Adt(d, s) => f.debug_tuple_field2_finish("Adt", d, &this.wrap(s)),
+ Adt(d, s) => {
+ write!(f, "{d:?}")?;
+ let mut s = s.clone().into_iter();
+ let first = s.next();
+ match first {
+ Some(first) => write!(f, "<{:?}", first)?,
+ None => return Ok(()),
+ };
+
+ for arg in s {
+ write!(f, ", {:?}", arg)?;
+ }
+
+ write!(f, ">")
+ }
Foreign(d) => f.debug_tuple_field1_finish("Foreign", d),
Str => write!(f, "str"),
Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
diff --git a/config.example.toml b/config.example.toml
index 2e0558c..f3c2366 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -458,7 +458,6 @@
# Sets the number of codegen units to build the standard library with,
# regardless of what the codegen-unit setting for the rest of the compiler is.
# NOTE: building with anything other than 1 is known to occasionally have bugs.
-# See https://github.com/rust-lang/rust/issues/83600.
#codegen-units-std = codegen-units
# Whether or not debug assertions are enabled for the compiler and standard library.
diff --git a/library/core/primitive_docs/box_into_raw.md b/library/core/primitive_docs/box_into_raw.md
deleted file mode 100644
index 9dd0344..0000000
--- a/library/core/primitive_docs/box_into_raw.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/boxed/struct.Box.html#method.into_raw
diff --git a/library/core/primitive_docs/fs_file.md b/library/core/primitive_docs/fs_file.md
deleted file mode 100644
index 4023e34..0000000
--- a/library/core/primitive_docs/fs_file.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/fs/struct.File.html
diff --git a/library/core/primitive_docs/io_bufread.md b/library/core/primitive_docs/io_bufread.md
deleted file mode 100644
index 7beda2c..0000000
--- a/library/core/primitive_docs/io_bufread.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/io/trait.BufRead.html
diff --git a/library/core/primitive_docs/io_read.md b/library/core/primitive_docs/io_read.md
deleted file mode 100644
index b7ecf5e..0000000
--- a/library/core/primitive_docs/io_read.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/io/trait.Read.html
diff --git a/library/core/primitive_docs/io_seek.md b/library/core/primitive_docs/io_seek.md
deleted file mode 100644
index db0274d..0000000
--- a/library/core/primitive_docs/io_seek.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/io/trait.Seek.html
diff --git a/library/core/primitive_docs/io_write.md b/library/core/primitive_docs/io_write.md
deleted file mode 100644
index 92a3b88..0000000
--- a/library/core/primitive_docs/io_write.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/io/trait.Write.html
diff --git a/library/core/primitive_docs/net_tosocketaddrs.md b/library/core/primitive_docs/net_tosocketaddrs.md
deleted file mode 100644
index 4daa10d..0000000
--- a/library/core/primitive_docs/net_tosocketaddrs.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/net/trait.ToSocketAddrs.html
diff --git a/library/core/primitive_docs/process_exit.md b/library/core/primitive_docs/process_exit.md
deleted file mode 100644
index cae34d1..0000000
--- a/library/core/primitive_docs/process_exit.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/process/fn.exit.html
diff --git a/library/core/primitive_docs/string_string.md b/library/core/primitive_docs/string_string.md
deleted file mode 100644
index 303dc07..0000000
--- a/library/core/primitive_docs/string_string.md
+++ /dev/null
@@ -1 +0,0 @@
-../std/string/struct.String.html
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 739f0c3..33226b0 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -1289,6 +1289,91 @@
max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
}
+/// Compares and sorts two values, returning minimum and maximum.
+///
+/// Returns `[v1, v2]` if the comparison determines them to be equal.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(cmp_minmax)]
+/// use std::cmp;
+///
+/// assert_eq!(cmp::minmax(1, 2), [1, 2]);
+/// assert_eq!(cmp::minmax(2, 2), [2, 2]);
+///
+/// // You can destructure the result using array patterns
+/// let [min, max] = cmp::minmax(42, 17);
+/// assert_eq!(min, 17);
+/// assert_eq!(max, 42);
+/// ```
+#[inline]
+#[must_use]
+#[unstable(feature = "cmp_minmax", issue = "115939")]
+pub fn minmax<T>(v1: T, v2: T) -> [T; 2]
+where
+ T: Ord,
+{
+ if v1 <= v2 { [v1, v2] } else { [v2, v1] }
+}
+
+/// Returns minimum and maximum values with respect to the specified comparison function.
+///
+/// Returns `[v1, v2]` if the comparison determines them to be equal.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(cmp_minmax)]
+/// use std::cmp;
+///
+/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]);
+/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]);
+///
+/// // You can destructure the result using array patterns
+/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
+/// assert_eq!(min, 17);
+/// assert_eq!(max, -42);
+/// ```
+#[inline]
+#[must_use]
+#[unstable(feature = "cmp_minmax", issue = "115939")]
+pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2]
+where
+ F: FnOnce(&T, &T) -> Ordering,
+{
+ if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] }
+}
+
+/// Returns minimum and maximum values with respect to the specified key function.
+///
+/// Returns `[v1, v2]` if the comparison determines them to be equal.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(cmp_minmax)]
+/// use std::cmp;
+///
+/// assert_eq!(cmp::minmax_by_key(-2, 1, |x: &i32| x.abs()), [1, -2]);
+/// assert_eq!(cmp::minmax_by_key(-2, 2, |x: &i32| x.abs()), [-2, 2]);
+///
+/// // You can destructure the result using array patterns
+/// let [min, max] = cmp::minmax_by_key(-42, 17, |x: &i32| x.abs());
+/// assert_eq!(min, 17);
+/// assert_eq!(max, -42);
+/// ```
+#[inline]
+#[must_use]
+#[unstable(feature = "cmp_minmax", issue = "115939")]
+pub fn minmax_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> [T; 2]
+where
+ F: FnMut(&T) -> K,
+ K: Ord,
+{
+ minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
+}
+
// Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types
mod impls {
use crate::cmp::Ordering::{self, Equal, Greater, Less};
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 2cfa896..fd5fe5a 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1,6 +1,3 @@
-// `library/{std,core}/src/primitive_docs.rs` should have the same contents.
-// These are different files so that relative links work properly without
-// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same.
#[rustc_doc_primitive = "bool"]
#[doc(alias = "true")]
#[doc(alias = "false")]
@@ -106,7 +103,7 @@
/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
///
/// [`u32`]: prim@u32
-#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))]
+/// [`exit`]: ../std/process/fn.exit.html
///
/// # `!` and generics
///
@@ -191,7 +188,7 @@
/// because `!` coerces to `Result<!, ConnectionError>` automatically.
///
/// [`String::from_str`]: str::FromStr::from_str
-#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
+/// [`String`]: ../std/string/struct.String.html
/// [`FromStr`]: str::FromStr
///
/// # `!` and traits
@@ -267,7 +264,7 @@
/// `impl` for this which simply panics, but the same is true for any type (we could `impl
/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
///
-#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))]
+/// [`File`]: ../std/fs/struct.File.html
/// [`Debug`]: fmt::Debug
/// [`default()`]: Default::default
///
@@ -355,7 +352,7 @@
/// assert_eq!(5, s.len() * std::mem::size_of::<u8>());
/// ```
///
-#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
+/// [`String`]: ../std/string/struct.String.html
///
/// As always, remember that a human intuition for 'character' might not map to
/// Unicode's definitions. For example, despite looking similar, the 'é'
@@ -572,7 +569,7 @@
/// [`null_mut`]: ptr::null_mut
/// [`is_null`]: pointer::is_null
/// [`offset`]: pointer::offset
-#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))]
+/// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw
/// [`write`]: ptr::write
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_pointer {}
@@ -1361,7 +1358,7 @@
///
/// [`std::fmt`]: fmt
/// [`Hash`]: hash::Hash
-#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
+/// [`ToSocketAddrs`]: ../std/net/trait.ToSocketAddrs.html
///
/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
/// implements that trait:
@@ -1381,10 +1378,10 @@
///
/// [`FusedIterator`]: iter::FusedIterator
/// [`TrustedLen`]: iter::TrustedLen
-#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))]
-#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))]
-#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))]
-#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))]
+/// [`Seek`]: ../std/io/trait.Seek.html
+/// [`BufRead`]: ../std/io/trait.BufRead.html
+/// [`Read`]: ../std/io/trait.Read.html
+/// [`io::Write`]: ../std/io/trait.Write.html
///
/// Note that due to method call deref coercion, simply calling a trait method will act like they
/// work on references as well as they do on owned values! The implementations described here are
diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs
index 7782ace..ff292ff 100644
--- a/library/core/src/tuple.rs
+++ b/library/core/src/tuple.rs
@@ -1,4 +1,4 @@
-// See src/libstd/primitive_docs.rs for documentation.
+// See core/src/primitive_docs.rs for documentation.
use crate::cmp::Ordering::{self, *};
use crate::marker::ConstParamTy;
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index 0253449..0dc570b 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -43,7 +43,8 @@
libc::abort();
}
} else if #[cfg(any(target_os = "hermit",
- all(target_vendor = "fortanix", target_env = "sgx")
+ all(target_vendor = "fortanix", target_env = "sgx"),
+ target_os = "xous"
))] {
unsafe fn abort() -> ! {
// call std::sys::abort_internal
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 33c9c6e..e8f64258 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -17,7 +17,7 @@
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
-libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true }
+libc = { version = "0.2.148", default-features = false, features = ['rustc-dep-of-std'], public = true }
compiler_builtins = { version = "0.1.100" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
@@ -36,8 +36,8 @@
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
rand_xorshift = "0.3.0"
-[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
-dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] }
+[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
+dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]
fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true }
diff --git a/library/std/build.rs b/library/std/build.rs
index ddf6e84..a81c456 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -37,6 +37,7 @@
|| target.contains("nintendo-3ds")
|| target.contains("vita")
|| target.contains("nto")
+ || target.contains("xous")
// See src/bootstrap/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
diff --git a/library/std/primitive_docs/box_into_raw.md b/library/std/primitive_docs/box_into_raw.md
deleted file mode 100644
index 307b9c8..0000000
--- a/library/std/primitive_docs/box_into_raw.md
+++ /dev/null
@@ -1 +0,0 @@
-Box::into_raw
diff --git a/library/std/primitive_docs/fs_file.md b/library/std/primitive_docs/fs_file.md
deleted file mode 100644
index 13e4540..0000000
--- a/library/std/primitive_docs/fs_file.md
+++ /dev/null
@@ -1 +0,0 @@
-fs::File
diff --git a/library/std/primitive_docs/io_bufread.md b/library/std/primitive_docs/io_bufread.md
deleted file mode 100644
index bb688e3..0000000
--- a/library/std/primitive_docs/io_bufread.md
+++ /dev/null
@@ -1 +0,0 @@
-io::BufRead
diff --git a/library/std/primitive_docs/io_read.md b/library/std/primitive_docs/io_read.md
deleted file mode 100644
index 5118d7c..0000000
--- a/library/std/primitive_docs/io_read.md
+++ /dev/null
@@ -1 +0,0 @@
-io::Read
diff --git a/library/std/primitive_docs/io_seek.md b/library/std/primitive_docs/io_seek.md
deleted file mode 100644
index 122e6df..0000000
--- a/library/std/primitive_docs/io_seek.md
+++ /dev/null
@@ -1 +0,0 @@
-io::Seek
diff --git a/library/std/primitive_docs/io_write.md b/library/std/primitive_docs/io_write.md
deleted file mode 100644
index 15dfc90..0000000
--- a/library/std/primitive_docs/io_write.md
+++ /dev/null
@@ -1 +0,0 @@
-io::Write
diff --git a/library/std/primitive_docs/net_tosocketaddrs.md b/library/std/primitive_docs/net_tosocketaddrs.md
deleted file mode 100644
index a01f318..0000000
--- a/library/std/primitive_docs/net_tosocketaddrs.md
+++ /dev/null
@@ -1 +0,0 @@
-net::ToSocketAddrs
diff --git a/library/std/primitive_docs/process_exit.md b/library/std/primitive_docs/process_exit.md
deleted file mode 100644
index 565a713..0000000
--- a/library/std/primitive_docs/process_exit.md
+++ /dev/null
@@ -1 +0,0 @@
-process::exit
diff --git a/library/std/primitive_docs/string_string.md b/library/std/primitive_docs/string_string.md
deleted file mode 100644
index ce7815f..0000000
--- a/library/std/primitive_docs/string_string.md
+++ /dev/null
@@ -1 +0,0 @@
-string::String
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index adc1b85..73cce35 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -8,7 +8,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
mod tests;
use crate::ffi::OsString;
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 22ccfc0..6ec158a 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -260,6 +260,7 @@
feature(slice_index_methods, coerce_unsized, sgx_platform)
)]
#![cfg_attr(windows, feature(round_char_boundary))]
+#![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
//
// Language features:
// tidy-alphabetical-start
@@ -673,7 +674,7 @@
// Include a number of private modules that exist solely to provide
// the rustdoc documentation for primitive types. Using `include!`
// because rustdoc only looks for these modules at the crate level.
-include!("primitive_docs.rs");
+include!("../../core/src/primitive_docs.rs");
// Include a number of private modules that exist solely to provide
// the rustdoc documentation for the existing keywords. Using `include!`
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 32fd54c..9667d5f 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -1,6 +1,6 @@
#![deny(unsafe_op_in_unsafe_fn)]
-#[cfg(all(test, not(target_os = "emscripten")))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_os = "xous"))))]
mod tests;
use crate::io::prelude::*;
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index 5ca4ed8..227e418 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -1,4 +1,4 @@
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
mod tests;
use crate::fmt;
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 634c3cc..de6d784 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -146,6 +146,8 @@
pub mod vxworks;
#[cfg(target_os = "watchos")]
pub(crate) mod watchos;
+#[cfg(target_os = "xous")]
+pub mod xous;
#[cfg(any(unix, target_os = "wasi", doc))]
pub mod fd;
diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs
new file mode 100644
index 0000000..8be7fbb
--- /dev/null
+++ b/library/std/src/os/xous/ffi.rs
@@ -0,0 +1,647 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[path = "../unix/ffi/os_str.rs"]
+mod os_str;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::os_str::{OsStrExt, OsStringExt};
+
+mod definitions;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use definitions::*;
+
+fn lend_mut_impl(
+ connection: Connection,
+ opcode: usize,
+ data: &mut [u8],
+ arg1: usize,
+ arg2: usize,
+ blocking: bool,
+) -> Result<(usize, usize), Error> {
+ let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+ let mut a1: usize = connection.try_into().unwrap();
+ let mut a2 = InvokeType::LendMut as usize;
+ let a3 = opcode;
+ let a4 = data.as_mut_ptr() as usize;
+ let a5 = data.len();
+ let a6 = arg1;
+ let a7 = arg2;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::MemoryReturned as usize {
+ Ok((a1, a2))
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+pub(crate) fn lend_mut(
+ connection: Connection,
+ opcode: usize,
+ data: &mut [u8],
+ arg1: usize,
+ arg2: usize,
+) -> Result<(usize, usize), Error> {
+ lend_mut_impl(connection, opcode, data, arg1, arg2, true)
+}
+
+pub(crate) fn try_lend_mut(
+ connection: Connection,
+ opcode: usize,
+ data: &mut [u8],
+ arg1: usize,
+ arg2: usize,
+) -> Result<(usize, usize), Error> {
+ lend_mut_impl(connection, opcode, data, arg1, arg2, false)
+}
+
+fn lend_impl(
+ connection: Connection,
+ opcode: usize,
+ data: &[u8],
+ arg1: usize,
+ arg2: usize,
+ blocking: bool,
+) -> Result<(usize, usize), Error> {
+ let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+ let a1: usize = connection.try_into().unwrap();
+ let a2 = InvokeType::Lend as usize;
+ let a3 = opcode;
+ let a4 = data.as_ptr() as usize;
+ let a5 = data.len();
+ let mut a6 = arg1;
+ let mut a7 = arg2;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1 => _,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6,
+ inlateout("a7") a7,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::MemoryReturned as usize {
+ Ok((a6, a7))
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+pub(crate) fn lend(
+ connection: Connection,
+ opcode: usize,
+ data: &[u8],
+ arg1: usize,
+ arg2: usize,
+) -> Result<(usize, usize), Error> {
+ lend_impl(connection, opcode, data, arg1, arg2, true)
+}
+
+pub(crate) fn try_lend(
+ connection: Connection,
+ opcode: usize,
+ data: &[u8],
+ arg1: usize,
+ arg2: usize,
+) -> Result<(usize, usize), Error> {
+ lend_impl(connection, opcode, data, arg1, arg2, false)
+}
+
+fn scalar_impl(connection: Connection, args: [usize; 5], blocking: bool) -> Result<(), Error> {
+ let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+ let mut a1: usize = connection.try_into().unwrap();
+ let a2 = InvokeType::Scalar as usize;
+ let a3 = args[0];
+ let a4 = args[1];
+ let a5 = args[2];
+ let a6 = args[3];
+ let a7 = args[4];
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Ok as usize {
+ Ok(())
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+pub(crate) fn scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> {
+ scalar_impl(connection, args, true)
+}
+
+pub(crate) fn try_scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> {
+ scalar_impl(connection, args, false)
+}
+
+fn blocking_scalar_impl(
+ connection: Connection,
+ args: [usize; 5],
+ blocking: bool,
+) -> Result<[usize; 5], Error> {
+ let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+ let mut a1: usize = connection.try_into().unwrap();
+ let mut a2 = InvokeType::BlockingScalar as usize;
+ let mut a3 = args[0];
+ let mut a4 = args[1];
+ let mut a5 = args[2];
+ let a6 = args[3];
+ let a7 = args[4];
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2,
+ inlateout("a3") a3,
+ inlateout("a4") a4,
+ inlateout("a5") a5,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Scalar1 as usize {
+ Ok([a1, 0, 0, 0, 0])
+ } else if result == SyscallResult::Scalar2 as usize {
+ Ok([a1, a2, 0, 0, 0])
+ } else if result == SyscallResult::Scalar5 as usize {
+ Ok([a1, a2, a3, a4, a5])
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+pub(crate) fn blocking_scalar(
+ connection: Connection,
+ args: [usize; 5],
+) -> Result<[usize; 5], Error> {
+ blocking_scalar_impl(connection, args, true)
+}
+
+pub(crate) fn try_blocking_scalar(
+ connection: Connection,
+ args: [usize; 5],
+) -> Result<[usize; 5], Error> {
+ blocking_scalar_impl(connection, args, false)
+}
+
+fn connect_impl(address: ServerAddress, blocking: bool) -> Result<Connection, Error> {
+ let a0 = if blocking { Syscall::Connect } else { Syscall::TryConnect } as usize;
+ let address: [u32; 4] = address.into();
+ let a1: usize = address[0].try_into().unwrap();
+ let a2: usize = address[1].try_into().unwrap();
+ let a3: usize = address[2].try_into().unwrap();
+ let a4: usize = address[3].try_into().unwrap();
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ let mut result: usize;
+ let mut value: usize;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0 => result,
+ inlateout("a1") a1 => value,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+ if result == SyscallResult::ConnectionId as usize {
+ Ok(value.try_into().unwrap())
+ } else if result == SyscallResult::Error as usize {
+ Err(value.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Connect to a Xous server represented by the specified `address`.
+///
+/// The current thread will block until the server is available. Returns
+/// an error if the server cannot accept any more connections.
+pub(crate) fn connect(address: ServerAddress) -> Result<Connection, Error> {
+ connect_impl(address, true)
+}
+
+/// Attempt to connect to a Xous server represented by the specified `address`.
+///
+/// If the server does not exist then None is returned.
+pub(crate) fn try_connect(address: ServerAddress) -> Result<Option<Connection>, Error> {
+ match connect_impl(address, false) {
+ Ok(conn) => Ok(Some(conn)),
+ Err(Error::ServerNotFound) => Ok(None),
+ Err(e) => Err(e),
+ }
+}
+
+/// Terminate the current process and return the specified code to the parent process.
+pub(crate) fn exit(return_code: u32) -> ! {
+ let a0 = Syscall::TerminateProcess as usize;
+ let a1 = return_code as usize;
+ let a2 = 0;
+ let a3 = 0;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ in("a0") a0,
+ in("a1") a1,
+ in("a2") a2,
+ in("a3") a3,
+ in("a4") a4,
+ in("a5") a5,
+ in("a6") a6,
+ in("a7") a7,
+ )
+ };
+ unreachable!();
+}
+
+/// Suspend the current thread and allow another thread to run. This thread may
+/// continue executing again immediately if there are no other threads available
+/// to run on the system.
+pub(crate) fn do_yield() {
+ let a0 = Syscall::Yield as usize;
+ let a1 = 0;
+ let a2 = 0;
+ let a3 = 0;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0 => _,
+ inlateout("a1") a1 => _,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+}
+
+/// Allocate memory from the system. An optional physical and/or virtual address
+/// may be specified in order to ensure memory is allocated at specific offsets,
+/// otherwise the kernel will select an address.
+///
+/// # Safety
+///
+/// This function is safe unless a virtual address is specified. In that case,
+/// the kernel will return an alias to the existing range. This violates Rust's
+/// pointer uniqueness guarantee.
+pub(crate) unsafe fn map_memory<T>(
+ phys: Option<core::ptr::NonNull<T>>,
+ virt: Option<core::ptr::NonNull<T>>,
+ count: usize,
+ flags: MemoryFlags,
+) -> Result<&'static mut [T], Error> {
+ let mut a0 = Syscall::MapMemory as usize;
+ let mut a1 = phys.map(|p| p.as_ptr() as usize).unwrap_or_default();
+ let mut a2 = virt.map(|p| p.as_ptr() as usize).unwrap_or_default();
+ let a3 = count * core::mem::size_of::<T>();
+ let a4 = flags.bits();
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::MemoryRange as usize {
+ let start = core::ptr::from_exposed_addr_mut::<T>(a1);
+ let len = a2 / core::mem::size_of::<T>();
+ let end = unsafe { start.add(len) };
+ Ok(unsafe { core::slice::from_raw_parts_mut(start, len) })
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Destroy the given memory, returning it to the compiler.
+///
+/// Safety: The memory pointed to by `range` should not be used after this
+/// function returns, even if this function returns Err().
+pub(crate) unsafe fn unmap_memory<T>(range: *mut [T]) -> Result<(), Error> {
+ let mut a0 = Syscall::UnmapMemory as usize;
+ let mut a1 = range.as_mut_ptr() as usize;
+ let a2 = range.len();
+ let a3 = 0;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Ok as usize {
+ Ok(())
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Adjust the memory flags for the given range. This can be used to remove flags
+/// from a given region in order to harden memory access. Note that flags may
+/// only be removed and may never be added.
+///
+/// Safety: The memory pointed to by `range` may become inaccessible or have its
+/// mutability removed. It is up to the caller to ensure that the flags specified
+/// by `new_flags` are upheld, otherwise the program will crash.
+pub(crate) unsafe fn update_memory_flags<T>(
+ range: *mut [T],
+ new_flags: MemoryFlags,
+) -> Result<(), Error> {
+ let mut a0 = Syscall::UpdateMemoryFlags as usize;
+ let mut a1 = range.as_mut_ptr() as usize;
+ let a2 = range.len();
+ let a3 = new_flags.bits();
+ let a4 = 0; // Process ID is currently None
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Ok as usize {
+ Ok(())
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Create a thread with a given stack and up to four arguments
+pub(crate) fn create_thread(
+ start: *mut usize,
+ stack: *mut [u8],
+ arg0: usize,
+ arg1: usize,
+ arg2: usize,
+ arg3: usize,
+) -> Result<ThreadId, Error> {
+ let mut a0 = Syscall::CreateThread as usize;
+ let mut a1 = start as usize;
+ let a2 = stack.as_mut_ptr() as usize;
+ let a3 = stack.len();
+ let a4 = arg0;
+ let a5 = arg1;
+ let a6 = arg2;
+ let a7 = arg3;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::ThreadId as usize {
+ Ok(a1.into())
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Wait for the given thread to terminate and return the exit code from that thread.
+pub(crate) fn join_thread(thread_id: ThreadId) -> Result<usize, Error> {
+ let mut a0 = Syscall::JoinThread as usize;
+ let mut a1 = thread_id.into();
+ let a2 = 0;
+ let a3 = 0;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Scalar1 as usize {
+ Ok(a1)
+ } else if result == SyscallResult::Scalar2 as usize {
+ Ok(a1)
+ } else if result == SyscallResult::Scalar5 as usize {
+ Ok(a1)
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Get the current thread's ID
+pub(crate) fn thread_id() -> Result<ThreadId, Error> {
+ let mut a0 = Syscall::GetThreadId as usize;
+ let mut a1 = 0;
+ let a2 = 0;
+ let a3 = 0;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::ThreadId as usize {
+ Ok(a1.into())
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
+
+/// Adjust the given `knob` limit to match the new value `new`. The current value must
+/// match the `current` in order for this to take effect.
+///
+/// The new value is returned as a result of this call. If the call fails, then the old
+/// value is returned. In either case, this function returns successfully.
+///
+/// An error is generated if the `knob` is not a valid limit, or if the call
+/// would not succeed.
+pub(crate) fn adjust_limit(knob: Limits, current: usize, new: usize) -> Result<usize, Error> {
+ let mut a0 = Syscall::JoinThread as usize;
+ let mut a1 = knob as usize;
+ let a2 = current;
+ let a3 = new;
+ let a4 = 0;
+ let a5 = 0;
+ let a6 = 0;
+ let a7 = 0;
+
+ unsafe {
+ core::arch::asm!(
+ "ecall",
+ inlateout("a0") a0,
+ inlateout("a1") a1,
+ inlateout("a2") a2 => _,
+ inlateout("a3") a3 => _,
+ inlateout("a4") a4 => _,
+ inlateout("a5") a5 => _,
+ inlateout("a6") a6 => _,
+ inlateout("a7") a7 => _,
+ )
+ };
+
+ let result = a0;
+
+ if result == SyscallResult::Scalar2 as usize && a1 == knob as usize {
+ Ok(a2)
+ } else if result == SyscallResult::Scalar5 as usize && a1 == knob as usize {
+ Ok(a1)
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.into())
+ } else {
+ Err(Error::InternalError)
+ }
+}
diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs
new file mode 100644
index 0000000..345005b
--- /dev/null
+++ b/library/std/src/os/xous/ffi/definitions.rs
@@ -0,0 +1,283 @@
+mod memoryflags;
+pub(crate) use memoryflags::*;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+/// Indicates a particular syscall number as used by the Xous kernel.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub enum Syscall {
+ MapMemory = 2,
+ Yield = 3,
+ UpdateMemoryFlags = 12,
+ ReceiveMessage = 15,
+ SendMessage = 16,
+ Connect = 17,
+ CreateThread = 18,
+ UnmapMemory = 19,
+ ReturnMemory = 20,
+ TerminateProcess = 22,
+ TrySendMessage = 24,
+ TryConnect = 25,
+ GetThreadId = 32,
+ JoinThread = 36,
+ AdjustProcessLimit = 38,
+ ReturnScalar = 40,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+/// Copies of these invocation types here for when we're running
+/// in environments without libxous.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub enum SyscallResult {
+ Ok = 0,
+ Error = 1,
+ MemoryRange = 3,
+ ConnectionId = 7,
+ Message = 9,
+ ThreadId = 10,
+ Scalar1 = 14,
+ Scalar2 = 15,
+ MemoryReturned = 18,
+ Scalar5 = 20,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Copy, Clone)]
+/// A list of all known errors that may be returned by the Xous kernel.
+#[repr(usize)]
+pub enum Error {
+ NoError = 0,
+ BadAlignment = 1,
+ BadAddress = 2,
+ OutOfMemory = 3,
+ MemoryInUse = 4,
+ InterruptNotFound = 5,
+ InterruptInUse = 6,
+ InvalidString = 7,
+ ServerExists = 8,
+ ServerNotFound = 9,
+ ProcessNotFound = 10,
+ ProcessNotChild = 11,
+ ProcessTerminated = 12,
+ Timeout = 13,
+ InternalError = 14,
+ ServerQueueFull = 15,
+ ThreadNotAvailable = 16,
+ UnhandledSyscall = 17,
+ InvalidSyscall = 18,
+ ShareViolation = 19,
+ InvalidThread = 20,
+ InvalidPid = 21,
+ UnknownError = 22,
+ AccessDenied = 23,
+ UseBeforeInit = 24,
+ DoubleFree = 25,
+ DebugInProgress = 26,
+ InvalidLimit = 27,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<usize> for Error {
+ fn from(src: usize) -> Self {
+ match src {
+ 0 => Self::NoError,
+ 1 => Self::BadAlignment,
+ 2 => Self::BadAddress,
+ 3 => Self::OutOfMemory,
+ 4 => Self::MemoryInUse,
+ 5 => Self::InterruptNotFound,
+ 6 => Self::InterruptInUse,
+ 7 => Self::InvalidString,
+ 8 => Self::ServerExists,
+ 9 => Self::ServerNotFound,
+ 10 => Self::ProcessNotFound,
+ 11 => Self::ProcessNotChild,
+ 12 => Self::ProcessTerminated,
+ 13 => Self::Timeout,
+ 14 => Self::InternalError,
+ 15 => Self::ServerQueueFull,
+ 16 => Self::ThreadNotAvailable,
+ 17 => Self::UnhandledSyscall,
+ 18 => Self::InvalidSyscall,
+ 19 => Self::ShareViolation,
+ 20 => Self::InvalidThread,
+ 21 => Self::InvalidPid,
+ 23 => Self::AccessDenied,
+ 24 => Self::UseBeforeInit,
+ 25 => Self::DoubleFree,
+ 26 => Self::DebugInProgress,
+ 27 => Self::InvalidLimit,
+ 22 | _ => Self::UnknownError,
+ }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<i32> for Error {
+ fn from(src: i32) -> Self {
+ let Ok(src) = core::convert::TryInto::<usize>::try_into(src) else {
+ return Self::UnknownError;
+ };
+ src.into()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Display for Error {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Error::NoError => "no error occurred",
+ Error::BadAlignment => "memory was not properly aligned",
+ Error::BadAddress => "an invalid address was supplied",
+ Error::OutOfMemory => "the process or service has run out of memory",
+ Error::MemoryInUse => "the requested address is in use",
+ Error::InterruptNotFound =>
+ "the requested interrupt does not exist on this platform",
+ Error::InterruptInUse => "the requested interrupt is currently in use",
+ Error::InvalidString => "the specified string was not formatted correctly",
+ Error::ServerExists => "a server with that address already exists",
+ Error::ServerNotFound => "the requetsed server could not be found",
+ Error::ProcessNotFound => "the target process does not exist",
+ Error::ProcessNotChild =>
+ "the requested operation can only be done on child processes",
+ Error::ProcessTerminated => "the target process has crashed",
+ Error::Timeout => "the requested operation timed out",
+ Error::InternalError => "an internal error occurred",
+ Error::ServerQueueFull => "the server has too many pending messages",
+ Error::ThreadNotAvailable => "the specified thread does not exist",
+ Error::UnhandledSyscall => "the kernel did not recognize that syscall",
+ Error::InvalidSyscall => "the syscall had incorrect parameters",
+ Error::ShareViolation => "an attempt was made to share memory twice",
+ Error::InvalidThread => "tried to resume a thread that was not ready",
+ Error::InvalidPid => "kernel attempted to use a pid that was not valid",
+ Error::AccessDenied => "no permission to perform the requested operation",
+ Error::UseBeforeInit => "attempt to use a service before initialization finished",
+ Error::DoubleFree => "the requested resource was freed twice",
+ Error::DebugInProgress => "kernel attempted to activate a thread being debugged",
+ Error::InvalidLimit => "process attempted to adjust an invalid limit",
+ Error::UnknownError => "an unknown error occurred",
+ }
+ )
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Debug for Error {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{}", self)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl crate::error::Error for Error {}
+
+/// Indicates the type of Message that is sent when making a `SendMessage` syscall.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub(crate) enum InvokeType {
+ LendMut = 1,
+ Lend = 2,
+ Move = 3,
+ Scalar = 4,
+ BlockingScalar = 5,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Debug, Copy, Clone)]
+/// A representation of a connection to a Xous service.
+pub struct Connection(u32);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<u32> for Connection {
+ fn from(src: u32) -> Connection {
+ Connection(src)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryFrom<usize> for Connection {
+ type Error = core::num::TryFromIntError;
+ fn try_from(src: usize) -> Result<Self, Self::Error> {
+ Ok(Connection(src.try_into()?))
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Into<u32> for Connection {
+ fn into(self) -> u32 {
+ self.0
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryInto<usize> for Connection {
+ type Error = core::num::TryFromIntError;
+ fn try_into(self) -> Result<usize, Self::Error> {
+ self.0.try_into()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Debug)]
+pub enum ServerAddressError {
+ InvalidLength,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct ServerAddress([u32; 4]);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryFrom<&str> for ServerAddress {
+ type Error = ServerAddressError;
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ let b = value.as_bytes();
+ if b.len() == 0 || b.len() > 16 {
+ return Err(Self::Error::InvalidLength);
+ }
+
+ let mut this_temp = [0u8; 16];
+ for (dest, src) in this_temp.iter_mut().zip(b.iter()) {
+ *dest = *src;
+ }
+
+ let mut this = [0u32; 4];
+ for (dest, src) in this.iter_mut().zip(this_temp.chunks_exact(4)) {
+ *dest = u32::from_le_bytes(src.try_into().unwrap());
+ }
+ Ok(ServerAddress(this))
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Into<[u32; 4]> for ServerAddress {
+ fn into(self) -> [u32; 4] {
+ self.0
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub(crate) struct ThreadId(usize);
+
+impl From<usize> for ThreadId {
+ fn from(src: usize) -> ThreadId {
+ ThreadId(src)
+ }
+}
+
+impl Into<usize> for ThreadId {
+ fn into(self) -> usize {
+ self.0
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(usize)]
+/// Limits that can be passed to `AdjustLimit`
+pub(crate) enum Limits {
+ HeapMaximum = 1,
+ HeapSize = 2,
+}
diff --git a/library/std/src/os/xous/ffi/definitions/memoryflags.rs b/library/std/src/os/xous/ffi/definitions/memoryflags.rs
new file mode 100644
index 0000000..af9de3c
--- /dev/null
+++ b/library/std/src/os/xous/ffi/definitions/memoryflags.rs
@@ -0,0 +1,176 @@
+/// Flags to be passed to the MapMemory struct.
+/// Note that it is an error to have memory be
+/// writable and not readable.
+#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Debug)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct MemoryFlags {
+ bits: usize,
+}
+
+impl MemoryFlags {
+ /// Free this memory
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub const FREE: Self = Self { bits: 0b0000_0000 };
+
+ /// Immediately allocate this memory. Otherwise it will
+ /// be demand-paged. This is implicitly set when `phys`
+ /// is not 0.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub const RESERVE: Self = Self { bits: 0b0000_0001 };
+
+ /// Allow the CPU to read from this page.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub const R: Self = Self { bits: 0b0000_0010 };
+
+ /// Allow the CPU to write to this page.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub const W: Self = Self { bits: 0b0000_0100 };
+
+ /// Allow the CPU to execute from this page.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub const X: Self = Self { bits: 0b0000_1000 };
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn bits(&self) -> usize {
+ self.bits
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn from_bits(raw: usize) -> Option<MemoryFlags> {
+ if raw > 16 { None } else { Some(MemoryFlags { bits: raw }) }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn is_empty(&self) -> bool {
+ self.bits == 0
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn empty() -> MemoryFlags {
+ MemoryFlags { bits: 0 }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn all() -> MemoryFlags {
+ MemoryFlags { bits: 15 }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Binary for MemoryFlags {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::Binary::fmt(&self.bits, f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Octal for MemoryFlags {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::Octal::fmt(&self.bits, f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::LowerHex for MemoryFlags {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::LowerHex::fmt(&self.bits, f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::UpperHex for MemoryFlags {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ core::fmt::UpperHex::fmt(&self.bits, f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitOr for MemoryFlags {
+ type Output = Self;
+
+ /// Returns the union of the two sets of flags.
+ #[inline]
+ fn bitor(self, other: MemoryFlags) -> Self {
+ Self { bits: self.bits | other.bits }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitOrAssign for MemoryFlags {
+ /// Adds the set of flags.
+ #[inline]
+ fn bitor_assign(&mut self, other: Self) {
+ self.bits |= other.bits;
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitXor for MemoryFlags {
+ type Output = Self;
+
+ /// Returns the left flags, but with all the right flags toggled.
+ #[inline]
+ fn bitxor(self, other: Self) -> Self {
+ Self { bits: self.bits ^ other.bits }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitXorAssign for MemoryFlags {
+ /// Toggles the set of flags.
+ #[inline]
+ fn bitxor_assign(&mut self, other: Self) {
+ self.bits ^= other.bits;
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitAnd for MemoryFlags {
+ type Output = Self;
+
+ /// Returns the intersection between the two sets of flags.
+ #[inline]
+ fn bitand(self, other: Self) -> Self {
+ Self { bits: self.bits & other.bits }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitAndAssign for MemoryFlags {
+ /// Disables all flags disabled in the set.
+ #[inline]
+ fn bitand_assign(&mut self, other: Self) {
+ self.bits &= other.bits;
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::Sub for MemoryFlags {
+ type Output = Self;
+
+ /// Returns the set difference of the two sets of flags.
+ #[inline]
+ fn sub(self, other: Self) -> Self {
+ Self { bits: self.bits & !other.bits }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::SubAssign for MemoryFlags {
+ /// Disables all flags enabled in the set.
+ #[inline]
+ fn sub_assign(&mut self, other: Self) {
+ self.bits &= !other.bits;
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::Not for MemoryFlags {
+ type Output = Self;
+
+ /// Returns the complement of this set of flags.
+ #[inline]
+ fn not(self) -> Self {
+ Self { bits: !self.bits } & MemoryFlags { bits: 15 }
+ }
+}
diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs
new file mode 100644
index 0000000..153694a
--- /dev/null
+++ b/library/std/src/os/xous/mod.rs
@@ -0,0 +1,17 @@
+#![stable(feature = "rust1", since = "1.0.0")]
+#![doc(cfg(target_os = "xous"))]
+
+pub mod ffi;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod services;
+
+/// A prelude for conveniently writing platform-specific code.
+///
+/// Includes all extension traits, and some important type definitions.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod prelude {
+ #[doc(no_inline)]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::ffi::{OsStrExt, OsStringExt};
+}
diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs
new file mode 100644
index 0000000..5c219f1
--- /dev/null
+++ b/library/std/src/os/xous/services.rs
@@ -0,0 +1,132 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+mod log;
+pub(crate) use log::*;
+
+mod systime;
+pub(crate) use systime::*;
+
+mod ticktimer;
+pub(crate) use ticktimer::*;
+
+mod ns {
+ const NAME_MAX_LENGTH: usize = 64;
+ use crate::os::xous::ffi::{lend_mut, Connection};
+ // By making this repr(C), the layout of this struct becomes well-defined
+ // and no longer shifts around.
+ // By marking it as `align(4096)` we define that it will be page-aligned,
+ // meaning it can be sent between processes. We make sure to pad out the
+ // entire struct so that memory isn't leaked to the name server.
+ #[repr(C, align(4096))]
+ struct ConnectRequest {
+ data: [u8; 4096],
+ }
+
+ impl ConnectRequest {
+ pub fn new(name: &str) -> Self {
+ let mut cr = ConnectRequest { data: [0u8; 4096] };
+ let name_bytes = name.as_bytes();
+
+ // Copy the string into our backing store.
+ for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.data[0..NAME_MAX_LENGTH]) {
+ *dest_byte = src_byte;
+ }
+
+ // Set the string length to the length of the passed-in String,
+ // or the maximum possible length. Which ever is smaller.
+ for (&src_byte, dest_byte) in (name.len().min(NAME_MAX_LENGTH) as u32)
+ .to_le_bytes()
+ .iter()
+ .zip(&mut cr.data[NAME_MAX_LENGTH..])
+ {
+ *dest_byte = src_byte;
+ }
+ cr
+ }
+ }
+
+ pub fn connect_with_name_impl(name: &str, blocking: bool) -> Option<Connection> {
+ let mut request = ConnectRequest::new(name);
+ let opcode = if blocking {
+ 6 /* BlockingConnect */
+ } else {
+ 7 /* TryConnect */
+ };
+ let cid = if blocking { super::name_server() } else { super::try_name_server()? };
+
+ lend_mut(cid, opcode, &mut request.data, 0, name.len().min(NAME_MAX_LENGTH))
+ .expect("unable to perform lookup");
+
+ // Read the result code back from the nameserver
+ let result = u32::from_le_bytes(request.data[0..4].try_into().unwrap());
+ if result == 0 {
+ // If the result was successful, then the CID is stored in the next 4 bytes
+ Some(u32::from_le_bytes(request.data[4..8].try_into().unwrap()).into())
+ } else {
+ None
+ }
+ }
+
+ pub fn connect_with_name(name: &str) -> Option<Connection> {
+ connect_with_name_impl(name, true)
+ }
+
+ pub fn try_connect_with_name(name: &str) -> Option<Connection> {
+ connect_with_name_impl(name, false)
+ }
+}
+
+/// Attempt to connect to a server by name. If the server does not exist, this will
+/// block until the server is created.
+///
+/// Note that this is different from connecting to a server by address. Server
+/// addresses are always 16 bytes long, whereas server names are arbitrary-length
+/// strings up to 64 bytes in length.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn connect(name: &str) -> Option<Connection> {
+ ns::connect_with_name(name)
+}
+
+/// Attempt to connect to a server by name. If the server does not exist, this will
+/// immediately return `None`.
+///
+/// Note that this is different from connecting to a server by address. Server
+/// addresses are always 16 bytes long, whereas server names are arbitrary-length
+/// strings.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn try_connect(name: &str) -> Option<Connection> {
+ ns::try_connect_with_name(name)
+}
+
+static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+
+/// Return a `Connection` to the name server. If the name server has not been started,
+/// then this call will block until the name server has been started. The `Connection`
+/// will be shared among all connections in a process, so it is safe to call this
+/// multiple times.
+pub(crate) fn name_server() -> Connection {
+ let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
+ if cid != 0 {
+ return cid.into();
+ }
+
+ let cid = crate::os::xous::ffi::connect("xous-name-server".try_into().unwrap()).unwrap();
+ NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+ cid
+}
+
+fn try_name_server() -> Option<Connection> {
+ let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
+ if cid != 0 {
+ return Some(cid.into());
+ }
+
+ if let Ok(Some(cid)) = crate::os::xous::ffi::try_connect("xous-name-server".try_into().unwrap())
+ {
+ NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+ Some(cid)
+ } else {
+ None
+ }
+}
diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs
new file mode 100644
index 0000000..e6bae92
--- /dev/null
+++ b/library/std/src/os/xous/services/log.rs
@@ -0,0 +1,63 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+/// Group `usize` bytes into a `usize` and return it, beginning
+/// from `offset` * sizeof(usize) bytes from the start. For example,
+/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
+/// return a usize with 5678 packed into it.
+fn group_or_null(data: &[u8], offset: usize) -> usize {
+ let start = offset * core::mem::size_of::<usize>();
+ let mut out_array = [0u8; core::mem::size_of::<usize>()];
+ if start < data.len() {
+ for (dest, src) in out_array.iter_mut().zip(&data[start..]) {
+ *dest = *src;
+ }
+ }
+ usize::from_le_bytes(out_array)
+}
+
+pub(crate) enum LogScalar<'a> {
+ /// A panic occurred, and a panic log is forthcoming
+ BeginPanic,
+
+ /// Some number of bytes will be appended to the log message
+ AppendPanicMessage(&'a [u8]),
+}
+
+impl<'a> Into<[usize; 5]> for LogScalar<'a> {
+ fn into(self) -> [usize; 5] {
+ match self {
+ LogScalar::BeginPanic => [1000, 0, 0, 0, 0],
+ LogScalar::AppendPanicMessage(c) =>
+ // Text is grouped into 4x `usize` words. The id is 1100 plus
+ // the number of characters in this message.
+ // Ignore errors since we're already panicking.
+ {
+ [
+ 1100 + c.len(),
+ group_or_null(&c, 0),
+ group_or_null(&c, 1),
+ group_or_null(&c, 2),
+ group_or_null(&c, 3),
+ ]
+ }
+ }
+ }
+}
+
+/// Return a `Connection` to the log server, which is used for printing messages to
+/// the console and reporting panics. If the log server has not yet started, this
+/// will block until the server is running. It is safe to call this multiple times,
+/// because the address is shared among all threads in a process.
+pub(crate) fn log_server() -> Connection {
+ static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+
+ let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed);
+ if cid != 0 {
+ return cid.into();
+ }
+
+ let cid = crate::os::xous::ffi::connect("xous-log-server ".try_into().unwrap()).unwrap();
+ LOG_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+ cid
+}
diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs
new file mode 100644
index 0000000..bbb875c
--- /dev/null
+++ b/library/std/src/os/xous/services/systime.rs
@@ -0,0 +1,28 @@
+use crate::os::xous::ffi::{connect, Connection};
+use core::sync::atomic::{AtomicU32, Ordering};
+
+pub(crate) enum SystimeScalar {
+ GetUtcTimeMs,
+}
+
+impl Into<[usize; 5]> for SystimeScalar {
+ fn into(self) -> [usize; 5] {
+ match self {
+ SystimeScalar::GetUtcTimeMs => [3, 0, 0, 0, 0],
+ }
+ }
+}
+
+/// Return a `Connection` to the systime server. This server is used for reporting the
+/// realtime clock.
+pub(crate) fn systime_server() -> Connection {
+ static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+ let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed);
+ if cid != 0 {
+ return cid.into();
+ }
+
+ let cid = connect("timeserverpublic".try_into().unwrap()).unwrap();
+ SYSTIME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+ cid
+}
diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs
new file mode 100644
index 0000000..7759303
--- /dev/null
+++ b/library/std/src/os/xous/services/ticktimer.rs
@@ -0,0 +1,42 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+pub(crate) enum TicktimerScalar {
+ ElapsedMs,
+ SleepMs(usize),
+ LockMutex(usize /* cookie */),
+ UnlockMutex(usize /* cookie */),
+ WaitForCondition(usize /* cookie */, usize /* timeout (ms) */),
+ NotifyCondition(usize /* cookie */, usize /* count */),
+ FreeMutex(usize /* cookie */),
+ FreeCondition(usize /* cookie */),
+}
+
+impl Into<[usize; 5]> for TicktimerScalar {
+ fn into(self) -> [usize; 5] {
+ match self {
+ TicktimerScalar::ElapsedMs => [0, 0, 0, 0, 0],
+ TicktimerScalar::SleepMs(msecs) => [1, msecs, 0, 0, 0],
+ TicktimerScalar::LockMutex(cookie) => [6, cookie, 0, 0, 0],
+ TicktimerScalar::UnlockMutex(cookie) => [7, cookie, 0, 0, 0],
+ TicktimerScalar::WaitForCondition(cookie, timeout_ms) => [8, cookie, timeout_ms, 0, 0],
+ TicktimerScalar::NotifyCondition(cookie, count) => [9, cookie, count, 0, 0],
+ TicktimerScalar::FreeMutex(cookie) => [10, cookie, 0, 0, 0],
+ TicktimerScalar::FreeCondition(cookie) => [11, cookie, 0, 0, 0],
+ }
+ }
+}
+
+/// Return a `Connection` to the ticktimer server. This server is used for synchronization
+/// primitives such as sleep, Mutex, and Condvar.
+pub(crate) fn ticktimer_server() -> Connection {
+ static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+ let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed);
+ if cid != 0 {
+ return cid.into();
+ }
+
+ let cid = crate::os::xous::ffi::connect("ticktimer-server".try_into().unwrap()).unwrap();
+ TICKTIMER_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+ cid
+}
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
deleted file mode 100644
index 2cfa896..0000000
--- a/library/std/src/primitive_docs.rs
+++ /dev/null
@@ -1,1593 +0,0 @@
-// `library/{std,core}/src/primitive_docs.rs` should have the same contents.
-// These are different files so that relative links work properly without
-// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same.
-#[rustc_doc_primitive = "bool"]
-#[doc(alias = "true")]
-#[doc(alias = "false")]
-/// The boolean type.
-///
-/// The `bool` represents a value, which could only be either [`true`] or [`false`]. If you cast
-/// a `bool` into an integer, [`true`] will be 1 and [`false`] will be 0.
-///
-/// # Basic usage
-///
-/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc.,
-/// which allow us to perform boolean operations using `&`, `|` and `!`.
-///
-/// [`if`] requires a `bool` value as its conditional. [`assert!`], which is an
-/// important macro in testing, checks whether an expression is [`true`] and panics
-/// if it isn't.
-///
-/// ```
-/// let bool_val = true & false | false;
-/// assert!(!bool_val);
-/// ```
-///
-/// [`true`]: ../std/keyword.true.html
-/// [`false`]: ../std/keyword.false.html
-/// [`BitAnd`]: ops::BitAnd
-/// [`BitOr`]: ops::BitOr
-/// [`Not`]: ops::Not
-/// [`if`]: ../std/keyword.if.html
-///
-/// # Examples
-///
-/// A trivial example of the usage of `bool`:
-///
-/// ```
-/// let praise_the_borrow_checker = true;
-///
-/// // using the `if` conditional
-/// if praise_the_borrow_checker {
-/// println!("oh, yeah!");
-/// } else {
-/// println!("what?!!");
-/// }
-///
-/// // ... or, a match pattern
-/// match praise_the_borrow_checker {
-/// true => println!("keep praising!"),
-/// false => println!("you should praise!"),
-/// }
-/// ```
-///
-/// Also, since `bool` implements the [`Copy`] trait, we don't
-/// have to worry about the move semantics (just like the integer and float primitives).
-///
-/// Now an example of `bool` cast to integer type:
-///
-/// ```
-/// assert_eq!(true as i32, 1);
-/// assert_eq!(false as i32, 0);
-/// ```
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_bool {}
-
-#[rustc_doc_primitive = "never"]
-#[doc(alias = "!")]
-//
-/// The `!` type, also called "never".
-///
-/// `!` represents the type of computations which never resolve to any value at all. For example,
-/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and
-/// so returns `!`.
-///
-/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to
-/// write:
-///
-/// ```
-/// #![feature(never_type)]
-/// # fn foo() -> u32 {
-/// let x: ! = {
-/// return 123
-/// };
-/// # }
-/// ```
-///
-/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never
-/// assigned a value (because `return` returns from the entire function), `x` can be given type
-/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code
-/// would still be valid.
-///
-/// A more realistic usage of `!` is in this code:
-///
-/// ```
-/// # fn get_a_number() -> Option<u32> { None }
-/// # loop {
-/// let num: u32 = match get_a_number() {
-/// Some(num) => num,
-/// None => break,
-/// };
-/// # }
-/// ```
-///
-/// Both match arms must produce values of type [`u32`], but since `break` never produces a value
-/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
-/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
-///
-/// [`u32`]: prim@u32
-#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))]
-///
-/// # `!` and generics
-///
-/// ## Infallible errors
-///
-/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`]
-/// trait:
-///
-/// ```
-/// trait FromStr: Sized {
-/// type Err;
-/// fn from_str(s: &str) -> Result<Self, Self::Err>;
-/// }
-/// ```
-///
-/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since
-/// converting a string into a string will never result in an error, the appropriate type is `!`.
-/// (Currently the type actually used is an enum with no variants, though this is only because `!`
-/// was added to Rust at a later date and it may change in the future.) With an [`Err`] type of
-/// `!`, if we have to call [`String::from_str`] for some reason the result will be a
-/// [`Result<String, !>`] which we can unpack like this:
-///
-/// ```
-/// #![feature(exhaustive_patterns)]
-/// use std::str::FromStr;
-/// let Ok(s) = String::from_str("hello");
-/// ```
-///
-/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns`
-/// feature is present this means we can exhaustively match on [`Result<T, !>`] by just taking the
-/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain
-/// enum variants from generic types like `Result`.
-///
-/// ## Infinite loops
-///
-/// While [`Result<T, !>`] is very useful for removing errors, `!` can also be used to remove
-/// successes as well. If we think of [`Result<T, !>`] as "if this function returns, it has not
-/// errored," we get a very intuitive idea of [`Result<!, E>`] as well: if the function returns, it
-/// *has* errored.
-///
-/// For example, consider the case of a simple web server, which can be simplified to:
-///
-/// ```ignore (hypothetical-example)
-/// loop {
-/// let (client, request) = get_request().expect("disconnected");
-/// let response = request.process();
-/// response.send(client);
-/// }
-/// ```
-///
-/// Currently, this isn't ideal, because we simply panic whenever we fail to get a new connection.
-/// Instead, we'd like to keep track of this error, like this:
-///
-/// ```ignore (hypothetical-example)
-/// loop {
-/// match get_request() {
-/// Err(err) => break err,
-/// Ok((client, request)) => {
-/// let response = request.process();
-/// response.send(client);
-/// },
-/// }
-/// }
-/// ```
-///
-/// Now, when the server disconnects, we exit the loop with an error instead of panicking. While it
-/// might be intuitive to simply return the error, we might want to wrap it in a [`Result<!, E>`]
-/// instead:
-///
-/// ```ignore (hypothetical-example)
-/// fn server_loop() -> Result<!, ConnectionError> {
-/// loop {
-/// let (client, request) = get_request()?;
-/// let response = request.process();
-/// response.send(client);
-/// }
-/// }
-/// ```
-///
-/// Now, we can use `?` instead of `match`, and the return type makes a lot more sense: if the loop
-/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok`
-/// because `!` coerces to `Result<!, ConnectionError>` automatically.
-///
-/// [`String::from_str`]: str::FromStr::from_str
-#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
-/// [`FromStr`]: str::FromStr
-///
-/// # `!` and traits
-///
-/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
-/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!`
-/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other
-/// words, they can't return `!` from every code path. As an example, this code doesn't compile:
-///
-/// ```compile_fail
-/// use std::ops::Add;
-///
-/// fn foo() -> impl Add<u32> {
-/// unimplemented!()
-/// }
-/// ```
-///
-/// But this code does:
-///
-/// ```
-/// use std::ops::Add;
-///
-/// fn foo() -> impl Add<u32> {
-/// if true {
-/// unimplemented!()
-/// } else {
-/// 0
-/// }
-/// }
-/// ```
-///
-/// The reason is that, in the first example, there are many possible types that `!` could coerce
-/// to, because many types implement `Add<u32>`. However, in the second example,
-/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type
-/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375]
-/// for more information on this quirk of `!`.
-///
-/// [#36375]: https://github.com/rust-lang/rust/issues/36375
-///
-/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`]
-/// for example:
-///
-/// ```
-/// #![feature(never_type)]
-/// # use std::fmt;
-/// # trait Debug {
-/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result;
-/// # }
-/// impl Debug for ! {
-/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-/// *self
-/// }
-/// }
-/// ```
-///
-/// Once again we're using `!`'s ability to coerce into any other type, in this case
-/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be
-/// called (because there is no value of type `!` for it to be called with). Writing `*self`
-/// essentially tells the compiler "We know that this code can never be run, so just treat the
-/// entire function body as having type [`fmt::Result`]". This pattern can be used a lot when
-/// implementing traits for `!`. Generally, any trait which only has methods which take a `self`
-/// parameter should have such an impl.
-///
-/// On the other hand, one trait which would not be appropriate to implement is [`Default`]:
-///
-/// ```
-/// trait Default {
-/// fn default() -> Self;
-/// }
-/// ```
-///
-/// Since `!` has no values, it has no default value either. It's true that we could write an
-/// `impl` for this which simply panics, but the same is true for any type (we could `impl
-/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
-///
-#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))]
-/// [`Debug`]: fmt::Debug
-/// [`default()`]: Default::default
-///
-#[unstable(feature = "never_type", issue = "35121")]
-mod prim_never {}
-
-#[rustc_doc_primitive = "char"]
-#[allow(rustdoc::invalid_rust_codeblocks)]
-/// A character type.
-///
-/// The `char` type represents a single character. More specifically, since
-/// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode
-/// scalar value]'.
-///
-/// This documentation describes a number of methods and trait implementations on the
-/// `char` type. For technical reasons, there is additional, separate
-/// documentation in [the `std::char` module](char/index.html) as well.
-///
-/// # Validity
-///
-/// A `char` is a '[Unicode scalar value]', which is any '[Unicode code point]'
-/// other than a [surrogate code point]. This has a fixed numerical definition:
-/// code points are in the range 0 to 0x10FFFF, inclusive.
-/// Surrogate code points, used by UTF-16, are in the range 0xD800 to 0xDFFF.
-///
-/// No `char` may be constructed, whether as a literal or at runtime, that is not a
-/// Unicode scalar value:
-///
-/// ```compile_fail
-/// // Each of these is a compiler error
-/// ['\u{D800}', '\u{DFFF}', '\u{110000}'];
-/// ```
-///
-/// ```should_panic
-/// // Panics; from_u32 returns None.
-/// char::from_u32(0xDE01).unwrap();
-/// ```
-///
-/// ```no_run
-/// // Undefined behaviour
-/// let _ = unsafe { char::from_u32_unchecked(0x110000) };
-/// ```
-///
-/// USVs are also the exact set of values that may be encoded in UTF-8. Because
-/// `char` values are USVs and `str` values are valid UTF-8, it is safe to store
-/// any `char` in a `str` or read any character from a `str` as a `char`.
-///
-/// The gap in valid `char` values is understood by the compiler, so in the
-/// below example the two ranges are understood to cover the whole range of
-/// possible `char` values and there is no error for a [non-exhaustive match].
-///
-/// ```
-/// let c: char = 'a';
-/// match c {
-/// '\0' ..= '\u{D7FF}' => false,
-/// '\u{E000}' ..= '\u{10FFFF}' => true,
-/// };
-/// ```
-///
-/// All USVs are valid `char` values, but not all of them represent a real
-/// character. Many USVs are not currently assigned to a character, but may be
-/// in the future ("reserved"); some will never be a character
-/// ("noncharacters"); and some may be given different meanings by different
-/// users ("private use").
-///
-/// [Unicode code point]: https://www.unicode.org/glossary/#code_point
-/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
-/// [non-exhaustive match]: ../book/ch06-02-match.html#matches-are-exhaustive
-/// [surrogate code point]: https://www.unicode.org/glossary/#surrogate_code_point
-///
-/// # Representation
-///
-/// `char` is always four bytes in size. This is a different representation than
-/// a given character would have as part of a [`String`]. For example:
-///
-/// ```
-/// let v = vec!['h', 'e', 'l', 'l', 'o'];
-///
-/// // five elements times four bytes for each element
-/// assert_eq!(20, v.len() * std::mem::size_of::<char>());
-///
-/// let s = String::from("hello");
-///
-/// // five elements times one byte per element
-/// assert_eq!(5, s.len() * std::mem::size_of::<u8>());
-/// ```
-///
-#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
-///
-/// As always, remember that a human intuition for 'character' might not map to
-/// Unicode's definitions. For example, despite looking similar, the 'é'
-/// character is one Unicode code point while 'é' is two Unicode code points:
-///
-/// ```
-/// let mut chars = "é".chars();
-/// // U+00e9: 'latin small letter e with acute'
-/// assert_eq!(Some('\u{00e9}'), chars.next());
-/// assert_eq!(None, chars.next());
-///
-/// let mut chars = "é".chars();
-/// // U+0065: 'latin small letter e'
-/// assert_eq!(Some('\u{0065}'), chars.next());
-/// // U+0301: 'combining acute accent'
-/// assert_eq!(Some('\u{0301}'), chars.next());
-/// assert_eq!(None, chars.next());
-/// ```
-///
-/// This means that the contents of the first string above _will_ fit into a
-/// `char` while the contents of the second string _will not_. Trying to create
-/// a `char` literal with the contents of the second string gives an error:
-///
-/// ```text
-/// error: character literal may only contain one codepoint: 'é'
-/// let c = 'é';
-/// ^^^
-/// ```
-///
-/// Another implication of the 4-byte fixed size of a `char` is that
-/// per-`char` processing can end up using a lot more memory:
-///
-/// ```
-/// let s = String::from("love: ❤️");
-/// let v: Vec<char> = s.chars().collect();
-///
-/// assert_eq!(12, std::mem::size_of_val(&s[..]));
-/// assert_eq!(32, std::mem::size_of_val(&v[..]));
-/// ```
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_char {}
-
-#[rustc_doc_primitive = "unit"]
-#[doc(alias = "(")]
-#[doc(alias = ")")]
-#[doc(alias = "()")]
-//
-/// The `()` type, also called "unit".
-///
-/// The `()` type has exactly one value `()`, and is used when there
-/// is no other meaningful value that could be returned. `()` is most
-/// commonly seen implicitly: functions without a `-> ...` implicitly
-/// have return type `()`, that is, these are equivalent:
-///
-/// ```rust
-/// fn long() -> () {}
-///
-/// fn short() {}
-/// ```
-///
-/// The semicolon `;` can be used to discard the result of an
-/// expression at the end of a block, making the expression (and thus
-/// the block) evaluate to `()`. For example,
-///
-/// ```rust
-/// fn returns_i64() -> i64 {
-/// 1i64
-/// }
-/// fn returns_unit() {
-/// 1i64;
-/// }
-///
-/// let is_i64 = {
-/// returns_i64()
-/// };
-/// let is_unit = {
-/// returns_i64();
-/// };
-/// ```
-///
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_unit {}
-
-// Required to make auto trait impls render.
-// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
-#[doc(hidden)]
-impl () {}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Clone for () {
- fn clone(&self) -> Self {
- loop {}
- }
-}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Copy for () {
- // empty
-}
-
-#[rustc_doc_primitive = "pointer"]
-#[doc(alias = "ptr")]
-#[doc(alias = "*")]
-#[doc(alias = "*const")]
-#[doc(alias = "*mut")]
-//
-/// Raw, unsafe pointers, `*const T`, and `*mut T`.
-///
-/// *[See also the `std::ptr` module](ptr).*
-///
-/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns.
-/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is
-/// dereferenced (using the `*` operator), it must be non-null and aligned.
-///
-/// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so
-/// [`write`] must be used if the type has drop glue and memory is not already
-/// initialized - otherwise `drop` would be called on the uninitialized memory.
-///
-/// Use the [`null`] and [`null_mut`] functions to create null pointers, and the
-/// [`is_null`] method of the `*const T` and `*mut T` types to check for null.
-/// The `*const T` and `*mut T` types also define the [`offset`] method, for
-/// pointer math.
-///
-/// # Common ways to create raw pointers
-///
-/// ## 1. Coerce a reference (`&T`) or mutable reference (`&mut T`).
-///
-/// ```
-/// let my_num: i32 = 10;
-/// let my_num_ptr: *const i32 = &my_num;
-/// let mut my_speed: i32 = 88;
-/// let my_speed_ptr: *mut i32 = &mut my_speed;
-/// ```
-///
-/// To get a pointer to a boxed value, dereference the box:
-///
-/// ```
-/// let my_num: Box<i32> = Box::new(10);
-/// let my_num_ptr: *const i32 = &*my_num;
-/// let mut my_speed: Box<i32> = Box::new(88);
-/// let my_speed_ptr: *mut i32 = &mut *my_speed;
-/// ```
-///
-/// This does not take ownership of the original allocation
-/// and requires no resource management later,
-/// but you must not use the pointer after its lifetime.
-///
-/// ## 2. Consume a box (`Box<T>`).
-///
-/// The [`into_raw`] function consumes a box and returns
-/// the raw pointer. It doesn't destroy `T` or deallocate any memory.
-///
-/// ```
-/// let my_speed: Box<i32> = Box::new(88);
-/// let my_speed: *mut i32 = Box::into_raw(my_speed);
-///
-/// // By taking ownership of the original `Box<T>` though
-/// // we are obligated to put it together later to be destroyed.
-/// unsafe {
-/// drop(Box::from_raw(my_speed));
-/// }
-/// ```
-///
-/// Note that here the call to [`drop`] is for clarity - it indicates
-/// that we are done with the given value and it should be destroyed.
-///
-/// ## 3. Create it using `ptr::addr_of!`
-///
-/// Instead of coercing a reference to a raw pointer, you can use the macros
-/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`).
-/// These macros allow you to create raw pointers to fields to which you cannot
-/// create a reference (without causing undefined behaviour), such as an
-/// unaligned field. This might be necessary if packed structs or uninitialized
-/// memory is involved.
-///
-/// ```
-/// #[derive(Debug, Default, Copy, Clone)]
-/// #[repr(C, packed)]
-/// struct S {
-/// aligned: u8,
-/// unaligned: u32,
-/// }
-/// let s = S::default();
-/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion
-/// ```
-///
-/// ## 4. Get it from C.
-///
-/// ```
-/// # #![feature(rustc_private)]
-/// #[allow(unused_extern_crates)]
-/// extern crate libc;
-///
-/// use std::mem;
-///
-/// unsafe {
-/// let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
-/// if my_num.is_null() {
-/// panic!("failed to allocate memory");
-/// }
-/// libc::free(my_num as *mut libc::c_void);
-/// }
-/// ```
-///
-/// Usually you wouldn't literally use `malloc` and `free` from Rust,
-/// but C APIs hand out a lot of pointers generally, so are a common source
-/// of raw pointers in Rust.
-///
-/// [`null`]: ptr::null
-/// [`null_mut`]: ptr::null_mut
-/// [`is_null`]: pointer::is_null
-/// [`offset`]: pointer::offset
-#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))]
-/// [`write`]: ptr::write
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_pointer {}
-
-#[rustc_doc_primitive = "array"]
-#[doc(alias = "[]")]
-#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases
-#[doc(alias = "[T; N]")]
-/// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the
-/// non-negative compile-time constant size, `N`.
-///
-/// There are two syntactic forms for creating an array:
-///
-/// * A list with each element, i.e., `[x, y, z]`.
-/// * A repeat expression `[expr; N]` where `N` is how many times to repeat `expr` in the array. `expr` must either be:
-///
-/// * A value of a type implementing the [`Copy`] trait
-/// * A `const` value
-///
-/// Note that `[expr; 0]` is allowed, and produces an empty array.
-/// This will still evaluate `expr`, however, and immediately drop the resulting value, so
-/// be mindful of side effects.
-///
-/// Arrays of *any* size implement the following traits if the element type allows it:
-///
-/// - [`Copy`]
-/// - [`Clone`]
-/// - [`Debug`]
-/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
-/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
-/// - [`Hash`]
-/// - [`AsRef`], [`AsMut`]
-/// - [`Borrow`], [`BorrowMut`]
-///
-/// Arrays of sizes from 0 to 32 (inclusive) implement the [`Default`] trait
-/// if the element type allows it. As a stopgap, trait implementations are
-/// statically generated up to size 32.
-///
-/// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
-/// is a homogeneous [prim@tuple] of appropriate length.
-///
-/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
-/// an array. Indeed, this provides most of the API for working with arrays.
-///
-/// Slices have a dynamic size and do not coerce to arrays. Instead, use
-/// `slice.try_into().unwrap()` or `<ArrayType>::try_from(slice).unwrap()`.
-///
-/// Array's `try_from(slice)` implementations (and the corresponding `slice.try_into()`
-/// array implementations) succeed if the input slice length is the same as the result
-/// array length. They optimize especially well when the optimizer can easily determine
-/// the slice length, e.g. `<[u8; 4]>::try_from(&slice[4..8]).unwrap()`. Array implements
-/// [TryFrom](crate::convert::TryFrom) returning:
-///
-/// - `[T; N]` copies from the slice's elements
-/// - `&[T; N]` references the original slice's elements
-/// - `&mut [T; N]` references the original slice's elements
-///
-/// You can move elements out of an array with a [slice pattern]. If you want
-/// one element, see [`mem::replace`].
-///
-/// # Examples
-///
-/// ```
-/// let mut array: [i32; 3] = [0; 3];
-///
-/// array[1] = 1;
-/// array[2] = 2;
-///
-/// assert_eq!([1, 2], &array[1..]);
-///
-/// // This loop prints: 0 1 2
-/// for x in array {
-/// print!("{x} ");
-/// }
-/// ```
-///
-/// You can also iterate over reference to the array's elements:
-///
-/// ```
-/// let array: [i32; 3] = [0; 3];
-///
-/// for x in &array { }
-/// ```
-///
-/// You can use `<ArrayType>::try_from(slice)` or `slice.try_into()` to get an array from
-/// a slice:
-///
-/// ```
-/// let bytes: [u8; 3] = [1, 0, 2];
-/// assert_eq!(1, u16::from_le_bytes(<[u8; 2]>::try_from(&bytes[0..2]).unwrap()));
-/// assert_eq!(512, u16::from_le_bytes(bytes[1..3].try_into().unwrap()));
-/// ```
-///
-/// You can use a [slice pattern] to move elements out of an array:
-///
-/// ```
-/// fn move_away(_: String) { /* Do interesting things. */ }
-///
-/// let [john, roa] = ["John".to_string(), "Roa".to_string()];
-/// move_away(john);
-/// move_away(roa);
-/// ```
-///
-/// Arrays can be created from homogeneous tuples of appropriate length:
-///
-/// ```
-/// let tuple: (u32, u32, u32) = (1, 2, 3);
-/// let array: [u32; 3] = tuple.into();
-/// ```
-///
-/// # Editions
-///
-/// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call
-/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old
-/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring
-/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition
-/// might be made consistent to the behavior of later editions.
-///
-/// ```rust,edition2018
-/// // Rust 2015 and 2018:
-///
-/// # #![allow(array_into_iter)] // override our `deny(warnings)`
-/// let array: [i32; 3] = [0; 3];
-///
-/// // This creates a slice iterator, producing references to each value.
-/// for item in array.into_iter().enumerate() {
-/// let (i, x): (usize, &i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-///
-/// // The `array_into_iter` lint suggests this change for future compatibility:
-/// for item in array.iter().enumerate() {
-/// let (i, x): (usize, &i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-///
-/// // You can explicitly iterate an array by value using `IntoIterator::into_iter`
-/// for item in IntoIterator::into_iter(array).enumerate() {
-/// let (i, x): (usize, i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-/// ```
-///
-/// Starting in the 2021 edition, `array.into_iter()` uses `IntoIterator` normally to iterate
-/// by value, and `iter()` should be used to iterate by reference like previous editions.
-///
-/// ```rust,edition2021
-/// // Rust 2021:
-///
-/// let array: [i32; 3] = [0; 3];
-///
-/// // This iterates by reference:
-/// for item in array.iter().enumerate() {
-/// let (i, x): (usize, &i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-///
-/// // This iterates by value:
-/// for item in array.into_iter().enumerate() {
-/// let (i, x): (usize, i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-/// ```
-///
-/// Future language versions might start treating the `array.into_iter()`
-/// syntax on editions 2015 and 2018 the same as on edition 2021. So code using
-/// those older editions should still be written with this change in mind, to
-/// prevent breakage in the future. The safest way to accomplish this is to
-/// avoid the `into_iter` syntax on those editions. If an edition update is not
-/// viable/desired, there are multiple alternatives:
-/// * use `iter`, equivalent to the old behavior, creating references
-/// * use [`IntoIterator::into_iter`], equivalent to the post-2021 behavior (Rust 1.53+)
-/// * replace `for ... in array.into_iter() {` with `for ... in array {`,
-/// equivalent to the post-2021 behavior (Rust 1.53+)
-///
-/// ```rust,edition2018
-/// // Rust 2015 and 2018:
-///
-/// let array: [i32; 3] = [0; 3];
-///
-/// // This iterates by reference:
-/// for item in array.iter() {
-/// let x: &i32 = item;
-/// println!("{x}");
-/// }
-///
-/// // This iterates by value:
-/// for item in IntoIterator::into_iter(array) {
-/// let x: i32 = item;
-/// println!("{x}");
-/// }
-///
-/// // This iterates by value:
-/// for item in array {
-/// let x: i32 = item;
-/// println!("{x}");
-/// }
-///
-/// // IntoIter can also start a chain.
-/// // This iterates by value:
-/// for item in IntoIterator::into_iter(array).enumerate() {
-/// let (i, x): (usize, i32) = item;
-/// println!("array[{i}] = {x}");
-/// }
-/// ```
-///
-/// [slice]: prim@slice
-/// [`Debug`]: fmt::Debug
-/// [`Hash`]: hash::Hash
-/// [`Borrow`]: borrow::Borrow
-/// [`BorrowMut`]: borrow::BorrowMut
-/// [slice pattern]: ../reference/patterns.html#slice-patterns
-/// [`From<Tuple>`]: convert::From
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_array {}
-
-#[rustc_doc_primitive = "slice"]
-#[doc(alias = "[")]
-#[doc(alias = "]")]
-#[doc(alias = "[]")]
-/// A dynamically-sized view into a contiguous sequence, `[T]`. Contiguous here
-/// means that elements are laid out so that every element is the same
-/// distance from its neighbors.
-///
-/// *[See also the `std::slice` module](crate::slice).*
-///
-/// Slices are a view into a block of memory represented as a pointer and a
-/// length.
-///
-/// ```
-/// // slicing a Vec
-/// let vec = vec![1, 2, 3];
-/// let int_slice = &vec[..];
-/// // coercing an array to a slice
-/// let str_slice: &[&str] = &["one", "two", "three"];
-/// ```
-///
-/// Slices are either mutable or shared. The shared slice type is `&[T]`,
-/// while the mutable slice type is `&mut [T]`, where `T` represents the element
-/// type. For example, you can mutate the block of memory that a mutable slice
-/// points to:
-///
-/// ```
-/// let mut x = [1, 2, 3];
-/// let x = &mut x[..]; // Take a full slice of `x`.
-/// x[1] = 7;
-/// assert_eq!(x, &[1, 7, 3]);
-/// ```
-///
-/// As slices store the length of the sequence they refer to, they have twice
-/// the size of pointers to [`Sized`](marker/trait.Sized.html) types.
-/// Also see the reference on
-/// [dynamically sized types](../reference/dynamically-sized-types.html).
-///
-/// ```
-/// # use std::rc::Rc;
-/// let pointer_size = std::mem::size_of::<&u8>();
-/// assert_eq!(2 * pointer_size, std::mem::size_of::<&[u8]>());
-/// assert_eq!(2 * pointer_size, std::mem::size_of::<*const [u8]>());
-/// assert_eq!(2 * pointer_size, std::mem::size_of::<Box<[u8]>>());
-/// assert_eq!(2 * pointer_size, std::mem::size_of::<Rc<[u8]>>());
-/// ```
-///
-/// ## Trait Implementations
-///
-/// Some traits are implemented for slices if the element type implements
-/// that trait. This includes [`Eq`], [`Hash`] and [`Ord`].
-///
-/// ## Iteration
-///
-/// The slices implement `IntoIterator`. The iterator yields references to the
-/// slice elements.
-///
-/// ```
-/// let numbers: &[i32] = &[0, 1, 2];
-/// for n in numbers {
-/// println!("{n} is a number!");
-/// }
-/// ```
-///
-/// The mutable slice yields mutable references to the elements:
-///
-/// ```
-/// let mut scores: &mut [i32] = &mut [7, 8, 9];
-/// for score in scores {
-/// *score += 1;
-/// }
-/// ```
-///
-/// This iterator yields mutable references to the slice's elements, so while
-/// the element type of the slice is `i32`, the element type of the iterator is
-/// `&mut i32`.
-///
-/// * [`.iter`] and [`.iter_mut`] are the explicit methods to return the default
-/// iterators.
-/// * Further methods that return iterators are [`.split`], [`.splitn`],
-/// [`.chunks`], [`.windows`] and more.
-///
-/// [`Hash`]: core::hash::Hash
-/// [`.iter`]: slice::iter
-/// [`.iter_mut`]: slice::iter_mut
-/// [`.split`]: slice::split
-/// [`.splitn`]: slice::splitn
-/// [`.chunks`]: slice::chunks
-/// [`.windows`]: slice::windows
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_slice {}
-
-#[rustc_doc_primitive = "str"]
-/// String slices.
-///
-/// *[See also the `std::str` module](crate::str).*
-///
-/// The `str` type, also called a 'string slice', is the most primitive string
-/// type. It is usually seen in its borrowed form, `&str`. It is also the type
-/// of string literals, `&'static str`.
-///
-/// String slices are always valid UTF-8.
-///
-/// # Basic Usage
-///
-/// String literals are string slices:
-///
-/// ```
-/// let hello_world = "Hello, World!";
-/// ```
-///
-/// Here we have declared a string slice initialized with a string literal.
-/// String literals have a static lifetime, which means the string `hello_world`
-/// is guaranteed to be valid for the duration of the entire program.
-/// We can explicitly specify `hello_world`'s lifetime as well:
-///
-/// ```
-/// let hello_world: &'static str = "Hello, world!";
-/// ```
-///
-/// # Representation
-///
-/// A `&str` is made up of two components: a pointer to some bytes, and a
-/// length. You can look at these with the [`as_ptr`] and [`len`] methods:
-///
-/// ```
-/// use std::slice;
-/// use std::str;
-///
-/// let story = "Once upon a time...";
-///
-/// let ptr = story.as_ptr();
-/// let len = story.len();
-///
-/// // story has nineteen bytes
-/// assert_eq!(19, len);
-///
-/// // We can re-build a str out of ptr and len. This is all unsafe because
-/// // we are responsible for making sure the two components are valid:
-/// let s = unsafe {
-/// // First, we build a &[u8]...
-/// let slice = slice::from_raw_parts(ptr, len);
-///
-/// // ... and then convert that slice into a string slice
-/// str::from_utf8(slice)
-/// };
-///
-/// assert_eq!(s, Ok(story));
-/// ```
-///
-/// [`as_ptr`]: str::as_ptr
-/// [`len`]: str::len
-///
-/// Note: This example shows the internals of `&str`. `unsafe` should not be
-/// used to get a string slice under normal circumstances. Use `as_str`
-/// instead.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_str {}
-
-#[rustc_doc_primitive = "tuple"]
-#[doc(alias = "(")]
-#[doc(alias = ")")]
-#[doc(alias = "()")]
-//
-/// A finite heterogeneous sequence, `(T, U, ..)`.
-///
-/// Let's cover each of those in turn:
-///
-/// Tuples are *finite*. In other words, a tuple has a length. Here's a tuple
-/// of length `3`:
-///
-/// ```
-/// ("hello", 5, 'c');
-/// ```
-///
-/// 'Length' is also sometimes called 'arity' here; each tuple of a different
-/// length is a different, distinct type.
-///
-/// Tuples are *heterogeneous*. This means that each element of the tuple can
-/// have a different type. In that tuple above, it has the type:
-///
-/// ```
-/// # let _:
-/// (&'static str, i32, char)
-/// # = ("hello", 5, 'c');
-/// ```
-///
-/// Tuples are a *sequence*. This means that they can be accessed by position;
-/// this is called 'tuple indexing', and it looks like this:
-///
-/// ```rust
-/// let tuple = ("hello", 5, 'c');
-///
-/// assert_eq!(tuple.0, "hello");
-/// assert_eq!(tuple.1, 5);
-/// assert_eq!(tuple.2, 'c');
-/// ```
-///
-/// The sequential nature of the tuple applies to its implementations of various
-/// traits. For example, in [`PartialOrd`] and [`Ord`], the elements are compared
-/// sequentially until the first non-equal set is found.
-///
-/// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type).
-///
-// Hardcoded anchor in src/librustdoc/html/format.rs
-// linked to as `#trait-implementations-1`
-/// # Trait implementations
-///
-/// In this documentation the shorthand `(T₁, T₂, …, Tₙ)` is used to represent tuples of varying
-/// length. When that is used, any trait bound expressed on `T` applies to each element of the
-/// tuple independently. Note that this is a convenience notation to avoid repetitive
-/// documentation, not valid Rust syntax.
-///
-/// Due to a temporary restriction in Rust’s type system, the following traits are only
-/// implemented on tuples of arity 12 or less. In the future, this may change:
-///
-/// * [`PartialEq`]
-/// * [`Eq`]
-/// * [`PartialOrd`]
-/// * [`Ord`]
-/// * [`Debug`]
-/// * [`Default`]
-/// * [`Hash`]
-/// * [`From<[T; N]>`][from]
-///
-/// [from]: convert::From
-/// [`Debug`]: fmt::Debug
-/// [`Hash`]: hash::Hash
-///
-/// The following traits are implemented for tuples of any length. These traits have
-/// implementations that are automatically generated by the compiler, so are not limited by
-/// missing language features.
-///
-/// * [`Clone`]
-/// * [`Copy`]
-/// * [`Send`]
-/// * [`Sync`]
-/// * [`Unpin`]
-/// * [`UnwindSafe`]
-/// * [`RefUnwindSafe`]
-///
-/// [`UnwindSafe`]: panic::UnwindSafe
-/// [`RefUnwindSafe`]: panic::RefUnwindSafe
-///
-/// # Examples
-///
-/// Basic usage:
-///
-/// ```
-/// let tuple = ("hello", 5, 'c');
-///
-/// assert_eq!(tuple.0, "hello");
-/// ```
-///
-/// Tuples are often used as a return type when you want to return more than
-/// one value:
-///
-/// ```
-/// fn calculate_point() -> (i32, i32) {
-/// // Don't do a calculation, that's not the point of the example
-/// (4, 5)
-/// }
-///
-/// let point = calculate_point();
-///
-/// assert_eq!(point.0, 4);
-/// assert_eq!(point.1, 5);
-///
-/// // Combining this with patterns can be nicer.
-///
-/// let (x, y) = calculate_point();
-///
-/// assert_eq!(x, 4);
-/// assert_eq!(y, 5);
-/// ```
-///
-/// Homogeneous tuples can be created from arrays of appropriate length:
-///
-/// ```
-/// let array: [u32; 3] = [1, 2, 3];
-/// let tuple: (u32, u32, u32) = array.into();
-/// ```
-///
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_tuple {}
-
-// Required to make auto trait impls render.
-// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
-#[doc(hidden)]
-impl<T> (T,) {}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-#[doc(fake_variadic)]
-/// This trait is implemented on arbitrary-length tuples.
-impl<T: Clone> Clone for (T,) {
- fn clone(&self) -> Self {
- loop {}
- }
-}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-#[doc(fake_variadic)]
-/// This trait is implemented on arbitrary-length tuples.
-impl<T: Copy> Copy for (T,) {
- // empty
-}
-
-#[rustc_doc_primitive = "f32"]
-/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008).
-///
-/// This type can represent a wide range of decimal numbers, like `3.5`, `27`,
-/// `-113.75`, `0.0078125`, `34359738368`, `0`, `-1`. So unlike integer types
-/// (such as `i32`), floating point types can represent non-integer numbers,
-/// too.
-///
-/// However, being able to represent this wide range of numbers comes at the
-/// cost of precision: floats can only represent some of the real numbers and
-/// calculation with floats round to a nearby representable number. For example,
-/// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results
-/// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented
-/// as `f32`. Note, however, that printing floats with `println` and friends will
-/// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will
-/// print `0.2`.
-///
-/// Additionally, `f32` can represent some special values:
-///
-/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a
-/// possible value. For comparison −0.0 = +0.0, but floating point operations can carry
-/// the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and
-/// a negative number rounded to a value smaller than a float can represent also produces −0.0.
-/// - [∞](#associatedconstant.INFINITY) and
-/// [−∞](#associatedconstant.NEG_INFINITY): these result from calculations
-/// like `1.0 / 0.0`.
-/// - [NaN (not a number)](#associatedconstant.NAN): this value results from
-/// calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected
-/// behavior:
-/// - It is not equal to any float, including itself! This is the reason `f32`
-/// doesn't implement the `Eq` trait.
-/// - It is also neither smaller nor greater than any float, making it
-/// impossible to sort by the default comparison operation, which is the
-/// reason `f32` doesn't implement the `Ord` trait.
-/// - It is also considered *infectious* as almost all calculations where one
-/// of the operands is NaN will also result in NaN. The explanations on this
-/// page only explicitly document behavior on NaN operands if this default
-/// is deviated from.
-/// - Lastly, there are multiple bit patterns that are considered NaN.
-/// Rust does not currently guarantee that the bit patterns of NaN are
-/// preserved over arithmetic operations, and they are not guaranteed to be
-/// portable or even fully deterministic! This means that there may be some
-/// surprising results upon inspecting the bit patterns,
-/// as the same calculations might produce NaNs with different bit patterns.
-///
-/// When the number resulting from a primitive operation (addition,
-/// subtraction, multiplication, or division) on this type is not exactly
-/// representable as `f32`, it is rounded according to the roundTiesToEven
-/// direction defined in IEEE 754-2008. That means:
-///
-/// - The result is the representable value closest to the true value, if there
-/// is a unique closest representable value.
-/// - If the true value is exactly half-way between two representable values,
-/// the result is the one with an even least-significant binary digit.
-/// - If the true value's magnitude is ≥ `f32::MAX` + 2<sup>(`f32::MAX_EXP` −
-/// `f32::MANTISSA_DIGITS` − 1)</sup>, the result is ∞ or −∞ (preserving the
-/// true value's sign).
-///
-/// For more information on floating point numbers, see [Wikipedia][wikipedia].
-///
-/// *[See also the `std::f32::consts` module](crate::f32::consts).*
-///
-/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_f32 {}
-
-#[rustc_doc_primitive = "f64"]
-/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008).
-///
-/// This type is very similar to [`f32`], but has increased
-/// precision by using twice as many bits. Please see [the documentation for
-/// `f32`][`f32`] or [Wikipedia on double precision
-/// values][wikipedia] for more information.
-///
-/// *[See also the `std::f64::consts` module](crate::f64::consts).*
-///
-/// [`f32`]: prim@f32
-/// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_f64 {}
-
-#[rustc_doc_primitive = "i8"]
-//
-/// The 8-bit signed integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_i8 {}
-
-#[rustc_doc_primitive = "i16"]
-//
-/// The 16-bit signed integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_i16 {}
-
-#[rustc_doc_primitive = "i32"]
-//
-/// The 32-bit signed integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_i32 {}
-
-#[rustc_doc_primitive = "i64"]
-//
-/// The 64-bit signed integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_i64 {}
-
-#[rustc_doc_primitive = "i128"]
-//
-/// The 128-bit signed integer type.
-#[stable(feature = "i128", since = "1.26.0")]
-mod prim_i128 {}
-
-#[rustc_doc_primitive = "u8"]
-//
-/// The 8-bit unsigned integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_u8 {}
-
-#[rustc_doc_primitive = "u16"]
-//
-/// The 16-bit unsigned integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_u16 {}
-
-#[rustc_doc_primitive = "u32"]
-//
-/// The 32-bit unsigned integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_u32 {}
-
-#[rustc_doc_primitive = "u64"]
-//
-/// The 64-bit unsigned integer type.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_u64 {}
-
-#[rustc_doc_primitive = "u128"]
-//
-/// The 128-bit unsigned integer type.
-#[stable(feature = "i128", since = "1.26.0")]
-mod prim_u128 {}
-
-#[rustc_doc_primitive = "isize"]
-//
-/// The pointer-sized signed integer type.
-///
-/// The size of this primitive is how many bytes it takes to reference any
-/// location in memory. For example, on a 32 bit target, this is 4 bytes
-/// and on a 64 bit target, this is 8 bytes.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_isize {}
-
-#[rustc_doc_primitive = "usize"]
-//
-/// The pointer-sized unsigned integer type.
-///
-/// The size of this primitive is how many bytes it takes to reference any
-/// location in memory. For example, on a 32 bit target, this is 4 bytes
-/// and on a 64 bit target, this is 8 bytes.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_usize {}
-
-#[rustc_doc_primitive = "reference"]
-#[doc(alias = "&")]
-#[doc(alias = "&mut")]
-//
-/// References, `&T` and `&mut T`.
-///
-/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
-/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
-/// <code>[ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html)</code> pattern.
-///
-/// For those familiar with pointers, a reference is just a pointer that is assumed to be
-/// aligned, not null, and pointing to memory containing a valid value of `T` - for example,
-/// <code>&[bool]</code> can only point to an allocation containing the integer values `1`
-/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but
-/// creating a <code>&[bool]</code> that points to an allocation containing
-/// the value `3` causes undefined behaviour.
-/// In fact, <code>[Option]\<&T></code> has the same memory representation as a
-/// nullable but aligned pointer, and can be passed across FFI boundaries as such.
-///
-/// In most cases, references can be used much like the original value. Field access, method
-/// calling, and indexing work the same (save for mutability rules, of course). In addition, the
-/// comparison operators transparently defer to the referent's implementation, allowing references
-/// to be compared the same as owned values.
-///
-/// References have a lifetime attached to them, which represents the scope for which the borrow is
-/// valid. A lifetime is said to "outlive" another one if its representative scope is as long or
-/// longer than the other. The `'static` lifetime is the longest lifetime, which represents the
-/// total life of the program. For example, string literals have a `'static` lifetime because the
-/// text data is embedded into the binary of the program, rather than in an allocation that needs
-/// to be dynamically managed.
-///
-/// `&mut T` references can be freely coerced into `&T` references with the same referent type, and
-/// references with longer lifetimes can be freely coerced into references with shorter ones.
-///
-/// Reference equality by address, instead of comparing the values pointed to, is accomplished via
-/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while
-/// [`PartialEq`] compares values.
-///
-/// ```
-/// use std::ptr;
-///
-/// let five = 5;
-/// let other_five = 5;
-/// let five_ref = &five;
-/// let same_five_ref = &five;
-/// let other_five_ref = &other_five;
-///
-/// assert!(five_ref == same_five_ref);
-/// assert!(five_ref == other_five_ref);
-///
-/// assert!(ptr::eq(five_ref, same_five_ref));
-/// assert!(!ptr::eq(five_ref, other_five_ref));
-/// ```
-///
-/// For more information on how to use references, see [the book's section on "References and
-/// Borrowing"][book-refs].
-///
-/// [book-refs]: ../book/ch04-02-references-and-borrowing.html
-///
-/// # Trait implementations
-///
-/// The following traits are implemented for all `&T`, regardless of the type of its referent:
-///
-/// * [`Copy`]
-/// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!)
-/// * [`Deref`]
-/// * [`Borrow`]
-/// * [`fmt::Pointer`]
-///
-/// [`Deref`]: ops::Deref
-/// [`Borrow`]: borrow::Borrow
-///
-/// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
-/// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
-/// referent:
-///
-/// * [`DerefMut`]
-/// * [`BorrowMut`]
-///
-/// [`DerefMut`]: ops::DerefMut
-/// [`BorrowMut`]: borrow::BorrowMut
-/// [bool]: prim@bool
-///
-/// The following traits are implemented on `&T` references if the underlying `T` also implements
-/// that trait:
-///
-/// * All the traits in [`std::fmt`] except [`fmt::Pointer`] (which is implemented regardless of the type of its referent) and [`fmt::Write`]
-/// * [`PartialOrd`]
-/// * [`Ord`]
-/// * [`PartialEq`]
-/// * [`Eq`]
-/// * [`AsRef`]
-/// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`)
-/// * [`Hash`]
-/// * [`ToSocketAddrs`]
-/// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
-/// * [`Sync`]
-///
-/// [`std::fmt`]: fmt
-/// [`Hash`]: hash::Hash
-#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
-///
-/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
-/// implements that trait:
-///
-/// * [`AsMut`]
-/// * [`FnMut`] \(in addition, `&mut T` references get [`FnOnce`] if `T: FnMut`)
-/// * [`fmt::Write`]
-/// * [`Iterator`]
-/// * [`DoubleEndedIterator`]
-/// * [`ExactSizeIterator`]
-/// * [`FusedIterator`]
-/// * [`TrustedLen`]
-/// * [`io::Write`]
-/// * [`Read`]
-/// * [`Seek`]
-/// * [`BufRead`]
-///
-/// [`FusedIterator`]: iter::FusedIterator
-/// [`TrustedLen`]: iter::TrustedLen
-#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))]
-#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))]
-#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))]
-#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))]
-///
-/// Note that due to method call deref coercion, simply calling a trait method will act like they
-/// work on references as well as they do on owned values! The implementations described here are
-/// meant for generic contexts, where the final type `T` is a type parameter or otherwise not
-/// locally known.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_ref {}
-
-#[rustc_doc_primitive = "fn"]
-//
-/// Function pointers, like `fn(usize) -> bool`.
-///
-/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].*
-///
-/// Function pointers are pointers that point to *code*, not data. They can be called
-/// just like functions. Like references, function pointers are, among other things, assumed to
-/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null
-/// pointers, make your type [`Option<fn()>`](core::option#options-and-pointers-nullable-pointers)
-/// with your required signature.
-///
-/// ### Safety
-///
-/// Plain function pointers are obtained by casting either plain functions, or closures that don't
-/// capture an environment:
-///
-/// ```
-/// fn add_one(x: usize) -> usize {
-/// x + 1
-/// }
-///
-/// let ptr: fn(usize) -> usize = add_one;
-/// assert_eq!(ptr(5), 6);
-///
-/// let clos: fn(usize) -> usize = |x| x + 5;
-/// assert_eq!(clos(5), 10);
-/// ```
-///
-/// In addition to varying based on their signature, function pointers come in two flavors: safe
-/// and unsafe. Plain `fn()` function pointers can only point to safe functions,
-/// while `unsafe fn()` function pointers can point to safe or unsafe functions.
-///
-/// ```
-/// fn add_one(x: usize) -> usize {
-/// x + 1
-/// }
-///
-/// unsafe fn add_one_unsafely(x: usize) -> usize {
-/// x + 1
-/// }
-///
-/// let safe_ptr: fn(usize) -> usize = add_one;
-///
-/// //ERROR: mismatched types: expected normal fn, found unsafe fn
-/// //let bad_ptr: fn(usize) -> usize = add_one_unsafely;
-///
-/// let unsafe_ptr: unsafe fn(usize) -> usize = add_one_unsafely;
-/// let really_safe_ptr: unsafe fn(usize) -> usize = add_one;
-/// ```
-///
-/// ### ABI
-///
-/// On top of that, function pointers can vary based on what ABI they use. This
-/// is achieved by adding the `extern` keyword before the type, followed by the
-/// ABI in question. The default ABI is "Rust", i.e., `fn()` is the exact same
-/// type as `extern "Rust" fn()`. A pointer to a function with C ABI would have
-/// type `extern "C" fn()`.
-///
-/// `extern "ABI" { ... }` blocks declare functions with ABI "ABI". The default
-/// here is "C", i.e., functions declared in an `extern {...}` block have "C"
-/// ABI.
-///
-/// For more information and a list of supported ABIs, see [the nomicon's
-/// section on foreign calling conventions][nomicon-abi].
-///
-/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions
-///
-/// ### Variadic functions
-///
-/// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them
-/// to be called with a variable number of arguments. Normal Rust functions, even those with an
-/// `extern "ABI"`, cannot be variadic. For more information, see [the nomicon's section on
-/// variadic functions][nomicon-variadic].
-///
-/// [nomicon-variadic]: ../nomicon/ffi.html#variadic-functions
-///
-/// ### Creating function pointers
-///
-/// When `bar` is the name of a function, then the expression `bar` is *not* a
-/// function pointer. Rather, it denotes a value of an unnameable type that
-/// uniquely identifies the function `bar`. The value is zero-sized because the
-/// type already identifies the function. This has the advantage that "calling"
-/// the value (it implements the `Fn*` traits) does not require dynamic
-/// dispatch.
-///
-/// This zero-sized type *coerces* to a regular function pointer. For example:
-///
-/// ```rust
-/// use std::mem;
-///
-/// fn bar(x: i32) {}
-///
-/// let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar`
-/// assert_eq!(mem::size_of_val(¬_bar_ptr), 0);
-///
-/// let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer
-/// assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::<usize>());
-///
-/// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar`
-/// ```
-///
-/// The last line shows that `&bar` is not a function pointer either. Rather, it
-/// is a reference to the function-specific ZST. `&bar` is basically never what you
-/// want when `bar` is a function.
-///
-/// ### Casting to and from integers
-///
-/// You cast function pointers directly to integers:
-///
-/// ```rust
-/// let fnptr: fn(i32) -> i32 = |x| x+2;
-/// let fnptr_addr = fnptr as usize;
-/// ```
-///
-/// However, a direct cast back is not possible. You need to use `transmute`:
-///
-/// ```rust
-/// # #[cfg(not(miri))] { // FIXME: use strict provenance APIs once they are stable, then remove this `cfg`
-/// # let fnptr: fn(i32) -> i32 = |x| x+2;
-/// # let fnptr_addr = fnptr as usize;
-/// let fnptr = fnptr_addr as *const ();
-/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) };
-/// assert_eq!(fnptr(40), 42);
-/// # }
-/// ```
-///
-/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
-/// This avoids an integer-to-pointer `transmute`, which can be problematic.
-/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
-///
-/// Note that all of this is not portable to platforms where function pointers and data pointers
-/// have different sizes.
-///
-/// ### Trait implementations
-///
-/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic
-/// function pointers of varying length. Note that this is a convenience notation to avoid
-/// repetitive documentation, not valid Rust syntax.
-///
-/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
-/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
-/// may change:
-///
-/// * [`PartialEq`]
-/// * [`Eq`]
-/// * [`PartialOrd`]
-/// * [`Ord`]
-/// * [`Hash`]
-/// * [`Pointer`]
-/// * [`Debug`]
-///
-/// The following traits are implemented for function pointers with any number of arguments and
-/// any ABI. These traits have implementations that are automatically generated by the compiler,
-/// so are not limited by missing language features:
-///
-/// * [`Clone`]
-/// * [`Copy`]
-/// * [`Send`]
-/// * [`Sync`]
-/// * [`Unpin`]
-/// * [`UnwindSafe`]
-/// * [`RefUnwindSafe`]
-///
-/// [`Hash`]: hash::Hash
-/// [`Pointer`]: fmt::Pointer
-/// [`UnwindSafe`]: panic::UnwindSafe
-/// [`RefUnwindSafe`]: panic::RefUnwindSafe
-///
-/// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because
-/// these traits are specially known to the compiler.
-#[stable(feature = "rust1", since = "1.0.0")]
-mod prim_fn {}
-
-// Required to make auto trait impls render.
-// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
-#[doc(hidden)]
-impl<Ret, T> fn(T) -> Ret {}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-#[doc(fake_variadic)]
-/// This trait is implemented on function pointers with any number of arguments.
-impl<Ret, T> Clone for fn(T) -> Ret {
- fn clone(&self) -> Self {
- loop {}
- }
-}
-
-// Fake impl that's only really used for docs.
-#[cfg(doc)]
-#[stable(feature = "rust1", since = "1.0.0")]
-#[doc(fake_variadic)]
-/// This trait is implemented on function pointers with any number of arguments.
-impl<Ret, T> Copy for fn(T) -> Ret {
- // empty
-}
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 762a1e9..5df1105 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -101,7 +101,7 @@
#![stable(feature = "process", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
mod tests;
use crate::io::prelude::*;
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 63bd783..457eb78 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -44,6 +44,9 @@
} else if #[cfg(target_family = "wasm")] {
mod wasm;
pub use self::wasm::*;
+ } else if #[cfg(target_os = "xous")] {
+ mod xous;
+ pub use self::xous::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use self::sgx::*;
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 7c242d4..2afec89 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -324,8 +324,10 @@
if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
let count = libc::CPU_COUNT(&set) as usize;
let count = count.min(quota);
- // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
- return Ok(NonZeroUsize::new_unchecked(count));
+ // reported to occur on MIPS kernels older than our minimum supported kernel version for those targets
+ let count = NonZeroUsize::new(count)
+ .expect("CPU count must be > 0. This may be a bug in sched_getaffinity(); try upgrading the kernel.");
+ return Ok(count);
}
}
}
diff --git a/library/std/src/sys/xous/alloc.rs b/library/std/src/sys/xous/alloc.rs
new file mode 100644
index 0000000..b3a3e69
--- /dev/null
+++ b/library/std/src/sys/xous/alloc.rs
@@ -0,0 +1,62 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+
+static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling malloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling calloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling free() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling realloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
+ }
+}
+
+mod lock {
+ use crate::sync::atomic::{AtomicI32, Ordering::SeqCst};
+
+ static LOCKED: AtomicI32 = AtomicI32::new(0);
+
+ pub struct DropLock;
+
+ pub fn lock() -> DropLock {
+ loop {
+ if LOCKED.swap(1, SeqCst) == 0 {
+ return DropLock;
+ }
+ crate::os::xous::ffi::do_yield();
+ }
+ }
+
+ impl Drop for DropLock {
+ fn drop(&mut self) {
+ let r = LOCKED.swap(0, SeqCst);
+ debug_assert_eq!(r, 1);
+ }
+ }
+}
diff --git a/library/std/src/sys/xous/locks/condvar.rs b/library/std/src/sys/xous/locks/condvar.rs
new file mode 100644
index 0000000..1bb38df
--- /dev/null
+++ b/library/std/src/sys/xous/locks/condvar.rs
@@ -0,0 +1,111 @@
+use super::mutex::Mutex;
+use crate::os::xous::ffi::{blocking_scalar, scalar};
+use crate::os::xous::services::ticktimer_server;
+use crate::sync::Mutex as StdMutex;
+use crate::time::Duration;
+
+// The implementation is inspired by Andrew D. Birrell's paper
+// "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+ counter: StdMutex<usize>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ #[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+ pub const fn new() -> Condvar {
+ Condvar { counter: StdMutex::new(0) }
+ }
+
+ pub fn notify_one(&self) {
+ let mut counter = self.counter.lock().unwrap();
+ if *counter <= 0 {
+ return;
+ } else {
+ *counter -= 1;
+ }
+ let result = blocking_scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(),
+ );
+ drop(counter);
+ result.expect("failure to send NotifyCondition command");
+ }
+
+ pub fn notify_all(&self) {
+ let mut counter = self.counter.lock().unwrap();
+ if *counter <= 0 {
+ return;
+ }
+ let result = blocking_scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter)
+ .into(),
+ );
+ *counter = 0;
+ drop(counter);
+
+ result.expect("failure to send NotifyCondition command");
+ }
+
+ fn index(&self) -> usize {
+ self as *const Condvar as usize
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let mut counter = self.counter.lock().unwrap();
+ *counter += 1;
+ unsafe { mutex.unlock() };
+ drop(counter);
+
+ let result = blocking_scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(),
+ );
+ unsafe { mutex.lock() };
+
+ result.expect("Ticktimer: failure to send WaitForCondition command");
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ let mut counter = self.counter.lock().unwrap();
+ *counter += 1;
+ unsafe { mutex.unlock() };
+ drop(counter);
+
+ let mut millis = dur.as_millis() as usize;
+ if millis == 0 {
+ millis = 1;
+ }
+
+ let result = blocking_scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis)
+ .into(),
+ );
+ unsafe { mutex.lock() };
+
+ let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0;
+
+ // If we awoke due to a timeout, decrement the wake count, as that would not have
+ // been done in the `notify()` call.
+ if !result {
+ *self.counter.lock().unwrap() -= 1;
+ }
+ result
+ }
+}
+
+impl Drop for Condvar {
+ fn drop(&mut self) {
+ scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(),
+ )
+ .ok();
+ }
+}
diff --git a/library/std/src/sys/xous/locks/mod.rs b/library/std/src/sys/xous/locks/mod.rs
new file mode 100644
index 0000000..f3c5c5d
--- /dev/null
+++ b/library/std/src/sys/xous/locks/mod.rs
@@ -0,0 +1,7 @@
+mod condvar;
+mod mutex;
+mod rwlock;
+
+pub use condvar::*;
+pub use mutex::*;
+pub use rwlock::*;
diff --git a/library/std/src/sys/xous/locks/mutex.rs b/library/std/src/sys/xous/locks/mutex.rs
new file mode 100644
index 0000000..ea51776
--- /dev/null
+++ b/library/std/src/sys/xous/locks/mutex.rs
@@ -0,0 +1,116 @@
+use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar};
+use crate::os::xous::services::ticktimer_server;
+use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst};
+
+pub struct Mutex {
+ /// The "locked" value indicates how many threads are waiting on this
+ /// Mutex. Possible values are:
+ /// 0: The lock is unlocked
+ /// 1: The lock is locked and uncontended
+ /// >=2: The lock is locked and contended
+ ///
+ /// A lock is "contended" when there is more than one thread waiting
+ /// for a lock, or it is locked for long periods of time. Rather than
+ /// spinning, these locks send a Message to the ticktimer server
+ /// requesting that they be woken up when a lock is unlocked.
+ locked: AtomicUsize,
+
+ /// Whether this Mutex ever was contended, and therefore made a trip
+ /// to the ticktimer server. If this was never set, then we were never
+ /// on the slow path and can skip deregistering the mutex.
+ contended: AtomicBool,
+}
+
+impl Mutex {
+ #[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+ pub const fn new() -> Mutex {
+ Mutex { locked: AtomicUsize::new(0), contended: AtomicBool::new(false) }
+ }
+
+ fn index(&self) -> usize {
+ self as *const Mutex as usize
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ // Try multiple times to acquire the lock without resorting to the ticktimer
+ // server. For locks that are held for a short amount of time, this will
+ // result in the ticktimer server never getting invoked. The `locked` value
+ // will be either 0 or 1.
+ for _attempts in 0..3 {
+ if unsafe { self.try_lock() } {
+ return;
+ }
+ do_yield();
+ }
+
+ // Try one more time to lock. If the lock is released between the previous code and
+ // here, then the inner `locked` value will be 1 at the end of this. If it was not
+ // locked, then the value will be more than 1, for example if there are multiple other
+ // threads waiting on this lock.
+ if unsafe { self.try_lock_or_poison() } {
+ return;
+ }
+
+ // When this mutex is dropped, we will need to deregister it with the server.
+ self.contended.store(true, Relaxed);
+
+ // The lock is now "contended". When the lock is released, a Message will get sent to the
+ // ticktimer server to wake it up. Note that this may already have happened, so the actual
+ // value of `lock` may be anything (0, 1, 2, ...).
+ blocking_scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::LockMutex(self.index()).into(),
+ )
+ .expect("failure to send LockMutex command");
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let prev = self.locked.fetch_sub(1, SeqCst);
+
+ // If the previous value was 1, then this was a "fast path" unlock, so no
+ // need to involve the Ticktimer server
+ if prev == 1 {
+ return;
+ }
+
+ // If it was 0, then something has gone seriously wrong and the counter
+ // has just wrapped around.
+ if prev == 0 {
+ panic!("mutex lock count underflowed");
+ }
+
+ // Unblock one thread that is waiting on this message.
+ scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(),
+ )
+ .expect("failure to send UnlockMutex command");
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn try_lock_or_poison(&self) -> bool {
+ self.locked.fetch_add(1, SeqCst) == 0
+ }
+}
+
+impl Drop for Mutex {
+ fn drop(&mut self) {
+ // If there was Mutex contention, then we involved the ticktimer. Free
+ // the resources associated with this Mutex as it is deallocated.
+ if self.contended.load(Relaxed) {
+ scalar(
+ ticktimer_server(),
+ crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(),
+ )
+ .ok();
+ }
+ }
+}
diff --git a/library/std/src/sys/xous/locks/rwlock.rs b/library/std/src/sys/xous/locks/rwlock.rs
new file mode 100644
index 0000000..618da75
--- /dev/null
+++ b/library/std/src/sys/xous/locks/rwlock.rs
@@ -0,0 +1,72 @@
+use crate::os::xous::ffi::do_yield;
+use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst};
+
+pub struct RwLock {
+ /// The "mode" value indicates how many threads are waiting on this
+ /// Mutex. Possible values are:
+ /// -1: The lock is locked for writing
+ /// 0: The lock is unlocked
+ /// >=1: The lock is locked for reading
+ ///
+ /// This currently spins waiting for the lock to be freed. An
+ /// optimization would be to involve the ticktimer server to
+ /// coordinate unlocks.
+ mode: AtomicIsize,
+}
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+impl RwLock {
+ #[inline]
+ #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+ pub const fn new() -> RwLock {
+ RwLock { mode: AtomicIsize::new(0) }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ while !unsafe { self.try_read() } {
+ do_yield();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ // Non-atomically determine the current value.
+ let current = self.mode.load(SeqCst);
+
+ // If it's currently locked for writing, then we cannot read.
+ if current < 0 {
+ return false;
+ }
+
+ // Attempt to lock. If the `current` value has changed, then this
+ // operation will fail and we will not obtain the lock even if we
+ // could potentially keep it.
+ let new = current + 1;
+ self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ while !unsafe { self.try_write() } {
+ do_yield();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.mode.fetch_sub(1, SeqCst);
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1));
+ }
+}
diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs
new file mode 100644
index 0000000..6d5c218
--- /dev/null
+++ b/library/std/src/sys/xous/mod.rs
@@ -0,0 +1,37 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+#[path = "../unsupported/env.rs"]
+pub mod env;
+#[path = "../unsupported/fs.rs"]
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+pub mod locks;
+#[path = "../unsupported/net.rs"]
+pub mod net;
+#[path = "../unsupported/once.rs"]
+pub mod once;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_key;
+#[path = "../unsupported/thread_parking.rs"]
+pub mod thread_parking;
+pub mod time;
+
+#[path = "../unsupported/common.rs"]
+mod common;
+pub use common::*;
diff --git a/library/std/src/sys/xous/os.rs b/library/std/src/sys/xous/os.rs
new file mode 100644
index 0000000..3d19fa4
--- /dev/null
+++ b/library/std/src/sys/xous/os.rs
@@ -0,0 +1,147 @@
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::xous::ffi::Error as XousError;
+use crate::path::{self, PathBuf};
+
+#[cfg(not(test))]
+mod c_compat {
+ use crate::os::xous::ffi::exit;
+ extern "C" {
+ fn main() -> u32;
+ }
+
+ #[no_mangle]
+ pub extern "C" fn abort() {
+ exit(1);
+ }
+
+ #[no_mangle]
+ pub extern "C" fn _start() {
+ exit(unsafe { main() });
+ }
+
+ // This function is needed by the panic runtime. The symbol is named in
+ // pre-link args for the target specification, so keep that in sync.
+ #[no_mangle]
+ // NB. used by both libunwind and libpanic_abort
+ pub extern "C" fn __rust_abort() -> ! {
+ exit(101);
+ }
+}
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(errno: i32) -> String {
+ Into::<XousError>::into(errno).to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+ I: Iterator<Item = T>,
+ T: AsRef<OsStr>,
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "not supported on this platform yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on this platform yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub struct Env(!);
+
+impl Env {
+ // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+ pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+ let Self(inner) = self;
+ match *inner {}
+ }
+}
+
+impl fmt::Debug for Env {
+ fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let Self(inner) = self;
+ match *inner {}
+ }
+}
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ self.0
+ }
+}
+
+pub fn env() -> Env {
+ panic!("not supported on this platform")
+}
+
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+ None
+}
+
+pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
+}
+
+pub fn unsetenv(_: &OsStr) -> io::Result<()> {
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ crate::os::xous::ffi::exit(code as u32);
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/xous/stdio.rs b/library/std/src/sys/xous/stdio.rs
new file mode 100644
index 0000000..2ac6946
--- /dev/null
+++ b/library/std/src/sys/xous/stdio.rs
@@ -0,0 +1,131 @@
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout {}
+pub struct Stderr;
+
+use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection};
+use crate::os::xous::services::{log_server, try_connect, LogScalar};
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout {}
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ #[repr(align(4096))]
+ struct LendBuffer([u8; 4096]);
+ let mut lend_buffer = LendBuffer([0u8; 4096]);
+ let connection = log_server();
+ for chunk in buf.chunks(lend_buffer.0.len()) {
+ for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
+ *dest = *src;
+ }
+ lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
+ }
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ #[repr(align(4096))]
+ struct LendBuffer([u8; 4096]);
+ let mut lend_buffer = LendBuffer([0u8; 4096]);
+ let connection = log_server();
+ for chunk in buf.chunks(lend_buffer.0.len()) {
+ for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
+ *dest = *src;
+ }
+ lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
+ }
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+#[derive(Copy, Clone)]
+pub struct PanicWriter {
+ log: Connection,
+ gfx: Option<Connection>,
+}
+
+impl io::Write for PanicWriter {
+ fn write(&mut self, s: &[u8]) -> core::result::Result<usize, io::Error> {
+ for c in s.chunks(core::mem::size_of::<usize>() * 4) {
+ // Text is grouped into 4x `usize` words. The id is 1100 plus
+ // the number of characters in this message.
+ // Ignore errors since we're already panicking.
+ try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok();
+ }
+
+ // Serialize the text to the graphics panic handler, only if we were able
+ // to acquire a connection to it. Text length is encoded in the `valid` field,
+ // the data itself in the buffer. Typically several messages are require to
+ // fully transmit the entire panic message.
+ if let Some(gfx) = self.gfx {
+ #[repr(C, align(4096))]
+ struct Request([u8; 4096]);
+ let mut request = Request([0u8; 4096]);
+ for (&s, d) in s.iter().zip(request.0.iter_mut()) {
+ *d = s;
+ }
+ try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok();
+ }
+ Ok(s.len())
+ }
+
+ // Tests show that this does not seem to be reliably called at the end of a panic
+ // print, so, we can't rely on this to e.g. trigger a graphics update.
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ // Generally this won't fail because every server has already connected, so
+ // this is likely to succeed.
+ let log = log_server();
+
+ // Send the "We're panicking" message (1000).
+ try_scalar(log, LogScalar::BeginPanic.into()).ok();
+
+ // This is will fail in the case that the connection table is full, or if the
+ // graphics server is not running. Most servers do not already have this connection.
+ let gfx = try_connect("panic-to-screen!");
+
+ Some(PanicWriter { log, gfx })
+}
diff --git a/library/std/src/sys/xous/thread.rs b/library/std/src/sys/xous/thread.rs
new file mode 100644
index 0000000..78c68de
--- /dev/null
+++ b/library/std/src/sys/xous/thread.rs
@@ -0,0 +1,144 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::os::xous::ffi::{
+ blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags,
+ MemoryFlags, Syscall, ThreadId,
+};
+use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
+use crate::time::Duration;
+use core::arch::asm;
+
+pub struct Thread {
+ tid: ThreadId,
+}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 131072;
+const MIN_STACK_SIZE: usize = 4096;
+pub const GUARD_PAGE_SIZE: usize = 4096;
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ let p = Box::into_raw(Box::new(p));
+ let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
+
+ if (stack_size & 4095) != 0 {
+ stack_size = (stack_size + 4095) & !4095;
+ }
+
+ // Allocate the whole thing, then divide it up after the fact. This ensures that
+ // even if there's a context switch during this function, the whole stack plus
+ // guard pages will remain contiguous.
+ let stack_plus_guard_pages: &mut [u8] = unsafe {
+ map_memory(
+ None,
+ None,
+ GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE,
+ MemoryFlags::R | MemoryFlags::W | MemoryFlags::X,
+ )
+ }
+ .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+ // No access to this page. Note: Write-only pages are illegal, and will
+ // cause an access violation.
+ unsafe {
+ update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W)
+ .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+ };
+
+ // No access to this page. Note: Write-only pages are illegal, and will
+ // cause an access violation.
+ unsafe {
+ update_memory_flags(
+ &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..],
+ MemoryFlags::W,
+ )
+ .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+ };
+
+ let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize;
+ let tid = create_thread(
+ thread_start as *mut usize,
+ &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)],
+ p as usize,
+ guard_page_pre,
+ stack_size,
+ 0,
+ )
+ .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+ extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) {
+ unsafe {
+ // Finally, let's run some code.
+ Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+ }
+
+ // Destroy TLS, which will free the TLS page and call the destructor for
+ // any thread local storage.
+ unsafe {
+ crate::sys::thread_local_key::destroy_tls();
+ }
+
+ // Deallocate the stack memory, along with the guard pages. Afterwards,
+ // exit the thread by returning to the magic address 0xff80_3000usize,
+ // which tells the kernel to deallocate this thread.
+ let mapped_memory_base = guard_page_pre;
+ let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE;
+ unsafe {
+ asm!(
+ "ecall",
+ "ret",
+ in("a0") Syscall::UnmapMemory as usize,
+ in("a1") mapped_memory_base,
+ in("a2") mapped_memory_length,
+ in("ra") 0xff80_3000usize,
+ options(nomem, nostack, noreturn)
+ );
+ }
+ }
+
+ Ok(Thread { tid })
+ }
+
+ pub fn yield_now() {
+ do_yield();
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(dur: Duration) {
+ // Because the sleep server works on units of `usized milliseconds`, split
+ // the messages up into these chunks. This means we may run into issues
+ // if you try to sleep a thread for more than 49 days on a 32-bit system.
+ let mut millis = dur.as_millis();
+ while millis > 0 {
+ let sleep_duration =
+ if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
+ blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
+ .expect("failed to send message to ticktimer server");
+ millis -= sleep_duration as u128;
+ }
+ }
+
+ pub fn join(self) {
+ join_thread(self.tid).unwrap();
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ // We're unicore right now.
+ Ok(unsafe { NonZeroUsize::new_unchecked(1) })
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/xous/thread_local_key.rs b/library/std/src/sys/xous/thread_local_key.rs
new file mode 100644
index 0000000..3771ea6
--- /dev/null
+++ b/library/std/src/sys/xous/thread_local_key.rs
@@ -0,0 +1,190 @@
+use crate::mem::ManuallyDrop;
+use crate::ptr;
+use crate::sync::atomic::AtomicPtr;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use core::arch::asm;
+
+use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags};
+
+/// Thread Local Storage
+///
+/// Currently, we are limited to 1023 TLS entries. The entries
+/// live in a page of memory that's unique per-process, and is
+/// stored in the `$tp` register. If this register is 0, then
+/// TLS has not been initialized and thread cleanup can be skipped.
+///
+/// The index into this register is the `key`. This key is identical
+/// between all threads, but indexes a different offset within this
+/// pointer.
+pub type Key = usize;
+
+pub type Dtor = unsafe extern "C" fn(*mut u8);
+
+const TLS_MEMORY_SIZE: usize = 4096;
+
+/// TLS keys start at `1` to mimic POSIX.
+static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1);
+
+fn tls_ptr_addr() -> *mut usize {
+ let mut tp: usize;
+ unsafe {
+ asm!(
+ "mv {}, tp",
+ out(reg) tp,
+ );
+ }
+ core::ptr::from_exposed_addr_mut::<usize>(tp)
+}
+
+/// Create an area of memory that's unique per thread. This area will
+/// contain all thread local pointers.
+fn tls_ptr() -> *mut usize {
+ let mut tp = tls_ptr_addr();
+
+ // If the TP register is `0`, then this thread hasn't initialized
+ // its TLS yet. Allocate a new page to store this memory.
+ if tp.is_null() {
+ tp = unsafe {
+ map_memory(
+ None,
+ None,
+ TLS_MEMORY_SIZE / core::mem::size_of::<usize>(),
+ MemoryFlags::R | MemoryFlags::W,
+ )
+ }
+ .expect("Unable to allocate memory for thread local storage")
+ .as_mut_ptr();
+
+ unsafe {
+ // Key #0 is currently unused.
+ (tp).write_volatile(0);
+
+ // Set the thread's `$tp` register
+ asm!(
+ "mv tp, {}",
+ in(reg) tp as usize,
+ );
+ }
+ }
+ tp
+}
+
+/// Allocate a new TLS key. These keys are shared among all threads.
+fn tls_alloc() -> usize {
+ TLS_KEY_INDEX.fetch_add(1, SeqCst)
+}
+
+#[inline]
+pub unsafe fn create(dtor: Option<Dtor>) -> Key {
+ let key = tls_alloc();
+ if let Some(f) = dtor {
+ unsafe { register_dtor(key, f) };
+ }
+ key
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+ assert!((key < 1022) && (key >= 1));
+ unsafe { tls_ptr().add(key).write_volatile(value as usize) };
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ assert!((key < 1022) && (key >= 1));
+ core::ptr::from_exposed_addr_mut::<u8>(unsafe { tls_ptr().add(key).read_volatile() })
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+ panic!("can't destroy keys on Xous");
+}
+
+// -------------------------------------------------------------------------
+// Dtor registration (stolen from Windows)
+//
+// Xous has no native support for running destructors so we manage our own
+// list of destructors to keep track of how to destroy keys. We then install a
+// callback later to get invoked whenever a thread exits, running all
+// appropriate destructors.
+//
+// Currently unregistration from this list is not supported. A destructor can be
+// registered but cannot be unregistered. There's various simplifying reasons
+// for doing this, the big ones being:
+//
+// 1. Currently we don't even support deallocating TLS keys, so normal operation
+// doesn't need to deallocate a destructor.
+// 2. There is no point in time where we know we can unregister a destructor
+// because it could always be getting run by some remote thread.
+//
+// Typically processes have a statically known set of TLS keys which is pretty
+// small, and we'd want to keep this memory alive for the whole process anyway
+// really.
+//
+// Perhaps one day we can fold the `Box` here into a static allocation,
+// expanding the `StaticKey` structure to contain not only a slot for the TLS
+// key but also a slot for the destructor queue on windows. An optimization for
+// another day!
+
+static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
+
+struct Node {
+ dtor: Dtor,
+ key: Key,
+ next: *mut Node,
+}
+
+unsafe fn register_dtor(key: Key, dtor: Dtor) {
+ let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() }));
+
+ let mut head = DTORS.load(SeqCst);
+ loop {
+ node.next = head;
+ match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) {
+ Ok(_) => return, // nothing to drop, we successfully added the node to the list
+ Err(cur) => head = cur,
+ }
+ }
+}
+
+pub unsafe fn destroy_tls() {
+ let tp = tls_ptr_addr();
+
+ // If the pointer address is 0, then this thread has no TLS.
+ if tp.is_null() {
+ return;
+ }
+ unsafe { run_dtors() };
+
+ // Finally, free the TLS array
+ unsafe {
+ unmap_memory(core::slice::from_raw_parts_mut(
+ tp,
+ TLS_MEMORY_SIZE / core::mem::size_of::<usize>(),
+ ))
+ .unwrap()
+ };
+}
+
+unsafe fn run_dtors() {
+ let mut any_run = true;
+ for _ in 0..5 {
+ if !any_run {
+ break;
+ }
+ any_run = false;
+ let mut cur = DTORS.load(SeqCst);
+ while !cur.is_null() {
+ let ptr = unsafe { get((*cur).key) };
+
+ if !ptr.is_null() {
+ unsafe { set((*cur).key, ptr::null_mut()) };
+ unsafe { ((*cur).dtor)(ptr as *mut _) };
+ any_run = true;
+ }
+
+ unsafe { cur = (*cur).next };
+ }
+ }
+}
diff --git a/library/std/src/sys/xous/time.rs b/library/std/src/sys/xous/time.rs
new file mode 100644
index 0000000..4e4ae67
--- /dev/null
+++ b/library/std/src/sys/xous/time.rs
@@ -0,0 +1,57 @@
+use crate::os::xous::ffi::blocking_scalar;
+use crate::os::xous::services::{
+ systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs,
+};
+use crate::time::Duration;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+impl Instant {
+ pub fn now() -> Instant {
+ let result = blocking_scalar(ticktimer_server(), ElapsedMs.into())
+ .expect("failed to request elapsed_ms");
+ let lower = result[0];
+ let upper = result[1];
+ Instant { 0: Duration::from_millis(lower as u64 | (upper as u64) << 32) }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ self.0.checked_add(*other).map(Instant)
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ self.0.checked_sub(*other).map(Instant)
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ let result = blocking_scalar(systime_server(), GetUtcTimeMs.into())
+ .expect("failed to request utc time in ms");
+ let lower = result[0];
+ let upper = result[1];
+ SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(*other)?))
+ }
+}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index e9c727c..f7d8217 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -46,6 +46,7 @@
if #[cfg(any(target_os = "l4re",
feature = "restricted-std",
all(target_family = "wasm", not(target_os = "emscripten")),
+ target_os = "xous",
all(target_vendor = "fortanix", target_env = "sgx")))] {
pub use crate::sys::net;
} else {
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index e4c7622..a9aa752 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -312,6 +312,14 @@
# non-standard string (e.g. gnuwin32 tools returns `windows32`). In
# these cases, fall back to using sys.platform.
return 'x86_64-pc-windows-msvc'
+ elif kernel == 'AIX':
+ # `uname -m` returns the machine ID rather than machine hardware on AIX,
+ # so we are unable to use cputype to form triple. AIX 7.2 and
+ # above supports 32-bit and 64-bit mode simultaneously and `uname -p`
+ # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in
+ # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper
+ # are not used to infer AIX's triple.
+ return 'powerpc64-ibm-aix'
else:
err = "unknown OS type: {}".format(kernel)
sys.exit(err)
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 50f1007e..46a62ee 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1640,7 +1640,10 @@
// flesh out rpath support more fully in the future.
rustflags.arg("-Zosx-rpath-install-name");
Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
- } else if !target.contains("windows") && !target.contains("aix") {
+ } else if !target.contains("windows")
+ && !target.contains("aix")
+ && !target.contains("xous")
+ {
rustflags.arg("-Clink-args=-Wl,-z,origin");
Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
} else {
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 2686a8c..4f19ffa 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -570,6 +570,9 @@
let dst = libdir.join(&runtime.name);
builder.copy(&runtime.path, &dst);
+ // The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for
+ // sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do
+ // not list them here to rename and sign the runtime library.
if target == "x86_64-apple-darwin"
|| target == "aarch64-apple-darwin"
|| target == "aarch64-apple-ios"
diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs
index 07288a1..7e5ade5 100644
--- a/src/bootstrap/llvm.rs
+++ b/src/bootstrap/llvm.rs
@@ -1063,6 +1063,7 @@
"aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan"]),
"aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan"]),
+ "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]),
"aarch64-unknown-linux-gnu" => {
common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
@@ -1073,6 +1074,7 @@
"x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
"x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
+ "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
"x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]),
"x86_64-unknown-netbsd" => {
common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"])
diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile
index b6b4fdc..db11700 100644
--- a/src/ci/docker/host-x86_64/arm-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.10
+FROM ubuntu:23.04
ARG DEBIAN_FRONTEND=noninteractive
COPY scripts/android-base-apt-get.sh /scripts/
diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile
index 9c6f648..b09b6ed 100644
--- a/src/ci/docker/host-x86_64/dist-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.10
+FROM ubuntu:23.04
COPY scripts/android-base-apt-get.sh /scripts/
RUN sh /scripts/android-base-apt-get.sh
diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
index dad9792..f86402b 100755
--- a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
+++ b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
@@ -2,5 +2,5 @@
set -ex
-curl https://static.redox-os.org/toolchain/x86_64-unknown-redox/relibc-install.tar.gz | \
+curl https://ci-mirrors.rust-lang.org/rustc/2022-11-27-relibc-install.tar.gz | \
tar --extract --gzip --directory /usr/local
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
index f695427..6f1b2a6 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
@@ -87,7 +87,7 @@
--set rust.lto=thin
ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \
- ./build/$HOSTS/stage0-tools-bin/opt-dist python3 ../x.py dist \
+ ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \
--host $HOSTS --target $HOSTS \
--include-default-paths \
build-manifest bootstrap
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
index b31629a..0d67319 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
@@ -1 +1 @@
-0.16.8
\ No newline at end of file
+0.16.9
\ No newline at end of file
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 89b82d5..973b9a0 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -114,7 +114,7 @@
run: git config --global core.autocrlf false
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -624,7 +624,7 @@
--target=x86_64-pc-windows-msvc
--enable-full-tools
--enable-profiler
- SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist python x.py dist bootstrap --include-default-paths
+ SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
<<: *job-windows-8c
@@ -707,7 +707,7 @@
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'
steps:
- name: checkout the source code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 2
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index 12a8b2b..3b6883c 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -4,6 +4,7 @@
- [Command-line arguments](command-line-arguments.md)
- [How to read rustdoc output](how-to-read-rustdoc.md)
- [In-doc settings](read-documentation/in-doc-settings.md)
+ - [Search](read-documentation/search.md)
- [How to write documentation](how-to-write-documentation.md)
- [What to include (and exclude)](write-documentation/what-to-include.md)
- [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md
index cd6e29f..dade62c 100644
--- a/src/doc/rustdoc/src/how-to-read-rustdoc.md
+++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md
@@ -75,56 +75,11 @@
## The Theme Picker and Search Interface
When viewing `rustdoc`'s output in a browser with JavaScript enabled,
-a dynamic interface appears at the top of the page composed of the search
-interface, help screen, and options.
+a dynamic interface appears at the top of the page composed of the [search]
+interface, help screen, and [options].
-### The Search Interface
-
-Typing in the search bar instantly searches the available documentation for
-the string entered with a fuzzy matching algorithm that is tolerant of minor
-typos.
-
-By default, the search results given are "In Names",
-meaning that the fuzzy match is made against the names of items.
-Matching names are shown on the left, and the first few words of their
-descriptions are given on the right.
-By clicking an item, you will navigate to its particular documentation.
-
-There are two other sets of results, shown as tabs in the search results pane.
-"In Parameters" shows matches for the string in the types of parameters to
-functions, and "In Return Types" shows matches in the return types of functions.
-Both are very useful when looking for a function whose name you can't quite
-bring to mind when you know the type you have or want.
-
-Names in the search interface can be prefixed with an item type followed by a
-colon (such as `mod:`) to restrict the results to just that kind of item. Also,
-searching for `println!` will search for a macro named `println`, just like
-searching for `macro:println` does.
-
-Function signature searches can query generics, wrapped in angle brackets, and
-traits are normalized like types in the search engine. For example, a function
-with the signature `fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
-can be matched with the following queries:
-
-* `Iterator<u32> -> usize`
-* `trait:Iterator<primitive:u32> -> primitive:usize`
-* `Iterator -> usize`
-
-Generics and function parameters are order-agnostic, but sensitive to nesting
-and number of matches. For example, a function with the signature
-`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
-will match these queries:
-
-* `Read -> Result<Vec<u8>, Error>`
-* `Read -> Result<Error, Vec>`
-* `Read -> Result<Vec<u8>>`
-
-But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
-
-Function signature searches also support arrays and slices. The explicit name
-`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
-or array of bytes, while square brackets `[u8]` will match either one. Empty
-square brackets, `[]`, will match any slice regardless of what it contains.
+[options]: read-documentation/in-doc-settings.html
+[search]: read-documentation/search.md
Paths are supported as well, you can look for `Vec::new` or `Option::Some` or
even `module::module_child::another_child::struct::field`. Whitespace characters
diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md
new file mode 100644
index 0000000..56a5016
--- /dev/null
+++ b/src/doc/rustdoc/src/read-documentation/search.md
@@ -0,0 +1,237 @@
+# Rustdoc search
+
+Typing in the search bar instantly searches the available documentation,
+matching either the name and path of an item, or a function's approximate
+type signature.
+
+## Search By Name
+
+To search by the name of an item (items include modules, types, traits,
+functions, and macros), write its name or path. As a special case, the parts
+of a path that normally get divided by `::` double colons can instead be
+separated by spaces. For example:
+
+ * [`vec new`] and [`vec::new`] both show the function `std::vec::Vec::new`
+ as a result.
+ * [`vec`], [`vec vec`], [`std::vec`], and [`std::vec::Vec`] all include the struct
+ `std::vec::Vec` itself in the results (and all but the last one also
+ include the module in the results).
+
+[`vec new`]: ../../std/vec/struct.Vec.html?search=vec%20new&filter-crate=std
+[`vec::new`]: ../../std/vec/struct.Vec.html?search=vec::new&filter-crate=std
+[`vec`]: ../../std/vec/struct.Vec.html?search=vec&filter-crate=std
+[`vec vec`]: ../../std/vec/struct.Vec.html?search=vec%20vec&filter-crate=std
+[`std::vec`]: ../../std/vec/struct.Vec.html?search=std::vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+
+As a quick way to trim down the list of results, there's a drop-down selector
+below the search input, labeled "Results in \[std\]". Clicking it can change
+which crate is being searched.
+
+Rustdoc uses a fuzzy matching function that can tolerate typos for this,
+though it's based on the length of the name that's typed in, so a good example
+of how this works would be [`HahsMap`]. To avoid this, wrap the item in quotes,
+searching for `"HahsMap"` (in this example, no results will be returned).
+
+[`HahsMap`]: ../../std/collections/struct.HashMap.html?search=HahsMap&filter-crate=std
+
+### Tabs in the Search By Name interface
+
+In fact, using [`HahsMap`] again as the example, it tells you that you're
+using "In Names" by default, but also lists two other tabs below the crate
+drop-down: "In Parameters" and "In Return Types".
+
+These two tabs are lists of functions, defined on the closest matching type
+to the search (for `HahsMap`, it loudly auto-corrects to `hashmap`). This
+auto-correct only kicks in if nothing is found that matches the literal.
+
+These tabs are not just methods. For example, searching the alloc crate for
+[`Layout`] also lists functions that accept layouts even though they're
+methods on the allocator or free functions.
+
+[`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc
+
+## Searching By Type Signature for functions
+
+If you know more specifically what the function you want to look at does,
+Rustdoc can search by more than one type at once in the parameters and return
+value. Multiple parameters are separated by `,` commas, and the return value
+is written with after a `->` arrow.
+
+Before describing the syntax in more detail, here's a few sample searches of
+the standard library and functions that are included in the results list:
+
+| Query | Results |
+|-------|--------|
+| [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` |
+| [`vec, vec -> bool`][] | `Vec::eq` |
+| [`option<T>, fnonce -> option<U>`][] | `Option::map` and `Option::and_then` |
+| [`option<T>, fnonce -> option<T>`][] | `Option::filter` and `Option::inspect` |
+| [`option -> default`][] | `Option::unwrap_or_default` |
+| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
+| [`any -> !`][] | `panic::panic_any` |
+| [`vec::intoiter<T> -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` |
+
+[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
+[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
+[`option<T>, fnonce -> option<U>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<U>&filter-crate=std
+[`option<T>, fnonce -> option<T>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<T>&filter-crate=std
+[`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std
+[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
+[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
+[iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std
+
+### How type-based search works
+
+In a complex type-based search, Rustdoc always treats every item's name as literal.
+If a name is used and nothing in the docs matches the individual item, such as
+a typo-ed [`uize -> vec`][] search, the item `uize` is treated as a generic
+type parameter (resulting in `vec::from` and other generic vec constructors).
+
+[`uize -> vec`]: ../../std/vec/struct.Vec.html?search=uize%20-%3E%20vec&filter-crate=std
+
+After deciding which items are type parameters and which are actual types, it
+then searches by matching up the function parameters (written before the `->`)
+and the return types (written after the `->`). Type matching is order-agnostic,
+and allows items to be left out of the query, but items that are present in the
+query must be present in the function for it to match.
+
+Function signature searches can query generics, wrapped in angle brackets, and
+traits will be normalized like types in the search engine if no type parameters
+match them. For example, a function with the signature
+`fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
+can be matched with the following queries:
+
+* `Iterator<u32> -> usize`
+* `Iterator -> usize`
+
+Generics and function parameters are order-agnostic, but sensitive to nesting
+and number of matches. For example, a function with the signature
+`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
+will match these queries:
+
+* `Read -> Result<Vec<u8>, Error>`
+* `Read -> Result<Error, Vec>`
+* `Read -> Result<Vec<u8>>`
+
+But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
+
+Function signature searches also support arrays and slices. The explicit name
+`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
+or array of bytes, while square brackets `[u8]` will match either one. Empty
+square brackets, `[]`, will match any slice or array regardless of what
+it contains, while a slice with a type parameter, like `[T]`, will only match
+functions that actually operate on generic slices.
+
+### Limitations and quirks of type-based search
+
+Type-based search is still a buggy, experimental, work-in-progress feature.
+Most of these limitations should be addressed in future version of Rustdoc.
+
+ * There's no way to write trait constraints on generic parameters.
+ You can name traits directly, and if there's a type parameter
+ with that bound, it'll match, but `option<T> -> T where T: Default`
+ cannot be precisely searched for (use `option<Default> -> Default`).
+
+ * Type parameters match type parameters, such that `Option<A>` matches
+ `Option<T>`, but never match concrete types in function signatures.
+ A trait named as if it were a type, such as `Option<Read>`, will match
+ a type parameter constrained by that trait, such as
+ `Option<T> where T: Read`, as well as matching `dyn Trait` and
+ `impl Trait`.
+
+ * `impl Trait` in argument position is treated exactly like a type
+ parameter, but in return position it will not match type parameters.
+
+ * Any type named in a complex type-based search will be assumed to be a
+ type parameter if nothing matching the name exactly is found. If you
+ want to force a type parameter, write `generic:T` and it will be used
+ as a type parameter even if a matching name is found. If you know
+ that you don't want a type parameter, you can force it to match
+ something else by giving it a different prefix like `struct:T`.
+
+ * It's impossible to search for references, pointers, or tuples. The
+ wrapped types can be searched for, so a function that takes `&File` can
+ be found with `File`, but you'll get a parse error when typing an `&`
+ into the search field. Similarly, `Option<(T, U)>` can be matched with
+ `Option<T, U>`, but `(` will give a parse error.
+
+ * Searching for lifetimes is not supported.
+
+ * It's impossible to search for closures based on their parameters or
+ return values.
+
+ * It's impossible to search based on the length of an array.
+
+## Item filtering
+
+Names in the search interface can be prefixed with an item type followed by a
+colon (such as `mod:`) to restrict the results to just that kind of item. Also,
+searching for `println!` will search for a macro named `println`, just like
+searching for `macro:println` does. The complete list of available filters is
+given under the <kbd>?</kbd> Help area, and in the detailed syntax below.
+
+Item filters can be used in both name-based and type signature-based searches.
+
+## Search query syntax
+
+```text
+ident = *(ALPHA / DIGIT / "_")
+path = ident *(DOUBLE-COLON ident) [!]
+slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
+arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!])
+type-sep = COMMA/WS *(COMMA/WS)
+nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
+generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
+ CLOSE-ANGLE-BRACKET
+return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
+
+exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
+type-search = [ nonempty-arg-list ] [ return-args ]
+
+query = *WS (exact-search / type-search) *WS
+
+type-filter = (
+ "mod" /
+ "externcrate" /
+ "import" /
+ "struct" /
+ "enum" /
+ "fn" /
+ "type" /
+ "static" /
+ "trait" /
+ "impl" /
+ "tymethod" /
+ "method" /
+ "structfield" /
+ "variant" /
+ "macro" /
+ "primitive" /
+ "associatedtype" /
+ "constant" /
+ "associatedconstant" /
+ "union" /
+ "foreigntype" /
+ "keyword" /
+ "existential" /
+ "attr" /
+ "derive" /
+ "traitalias" /
+ "generic")
+
+OPEN-ANGLE-BRACKET = "<"
+CLOSE-ANGLE-BRACKET = ">"
+OPEN-SQUARE-BRACKET = "["
+CLOSE-SQUARE-BRACKET = "]"
+COLON = ":"
+DOUBLE-COLON = "::"
+QUOTE = %x22
+COMMA = ","
+RETURN-ARROW = "->"
+
+ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+DIGIT = %x30-39
+WS = %x09 / " "
+```
diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md
index f4d7596..9a59d80 100644
--- a/src/doc/style-guide/src/README.md
+++ b/src/doc/style-guide/src/README.md
@@ -32,6 +32,19 @@
non-default style, or forbidding tools from adding any particular configuration
options.
+## Bugs
+
+If the style guide differs from rustfmt, that may represent a bug in rustfmt,
+or a bug in the style guide; either way, please report it to the style team or
+the rustfmt team or both, for investigation and fix.
+
+If implementing a new formatting tool based on the style guide and default Rust
+style, please test it on the corpus of existing Rust code, and avoid causing
+widespread breakage. The implementation and testing of such a tool may surface
+bugs in either the style guide or rustfmt, as well as bugs in the tool itself.
+
+We typically resolve bugs in a fashion that avoids widespread breakage.
+
## Formatting conventions
### Indentation and line width
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b276745..ef7794c 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1639,10 +1639,6 @@
matches!(self, Type::Generic(_))
}
- pub(crate) fn is_impl_trait(&self) -> bool {
- matches!(self, Type::ImplTrait(_))
- }
-
pub(crate) fn is_unit(&self) -> bool {
matches!(self, Type::Tuple(v) if v.is_empty())
}
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 7696ea0..8cddd5f 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -16,7 +16,6 @@
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_metadata::rendered_const;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
@@ -282,8 +281,8 @@
let ty = tcx.type_of(def_id).instantiate_identity();
match (val, ty.kind()) {
(_, &ty::Ref(..)) => None,
- (ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
- (ConstValue::Scalar(_), _) => {
+ (mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
+ (mir::ConstValue::Scalar(_), _) => {
let const_ = mir::ConstantKind::from_value(val, ty);
Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
}
@@ -326,14 +325,14 @@
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
- (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => {
+ (mir::ConstantKind::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
if underscores_and_type {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
} else {
int.to_string()
}
}
- (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
+ (mir::ConstantKind::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = ct.ty();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f70f59d..3e671a6 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
- pub(crate) parent_idx: Option<usize>,
+ pub(crate) parent_idx: Option<isize>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@
let id = match &self.id {
// 0 is a sentinel, everything else is one-indexed
None => 0,
- Some(RenderTypeId::Index(idx)) => idx + 1,
+ // concrete type
+ Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+ // generic type parameter
+ Some(RenderTypeId::Index(idx)) => *idx,
_ => panic!("must convert render types to indexes before serializing"),
};
if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@
pub(crate) enum RenderTypeId {
DefId(DefId),
Primitive(clean::PrimitiveType),
- Index(usize),
+ Index(isize),
}
/// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@
pub(crate) struct IndexItemFunctionType {
inputs: Vec<RenderType>,
output: Vec<RenderType>,
+ where_clause: Vec<Vec<RenderType>>,
}
impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@
_ => seq.serialize_element(&self.inputs)?,
}
match &self.output[..] {
- [] => {}
+ [] if self.where_clause.is_empty() => {}
[one] if one.generics.is_none() => seq.serialize_element(one)?,
_ => seq.serialize_element(&self.output)?,
}
+ for constraint in &self.where_clause {
+ if let [one] = &constraint[..] && one.generics.is_none() {
+ seq.serialize_element(one)?;
+ } else {
+ seq.serialize_element(constraint)?;
+ }
+ }
seq.end()
}
}
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 145c7d1..78c443b 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -68,16 +68,16 @@
// Reduce `DefId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
let mut lastpath = "";
- let mut lastpathid = 0usize;
+ let mut lastpathid = 0isize;
// First, on function signatures
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
for item in search_index.iter_mut() {
fn insert_into_map<F: std::hash::Hash + Eq>(
ty: &mut RenderType,
- map: &mut FxHashMap<F, usize>,
+ map: &mut FxHashMap<F, isize>,
itemid: F,
- lastpathid: &mut usize,
+ lastpathid: &mut isize,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
path: &[Symbol],
@@ -97,9 +97,9 @@
fn convert_render_type(
ty: &mut RenderType,
cache: &mut Cache,
- itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
- primitives: &mut FxHashMap<Symbol, usize>,
- lastpathid: &mut usize,
+ itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+ primitives: &mut FxHashMap<Symbol, isize>,
+ lastpathid: &mut isize,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
) {
if let Some(generics) = &mut ty.generics {
@@ -173,6 +173,18 @@
&mut crate_paths,
);
}
+ for constraint in &mut search_type.where_clause {
+ for trait_ in &mut constraint[..] {
+ convert_render_type(
+ trait_,
+ cache,
+ &mut itemid_to_pathid,
+ &mut primitives,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ }
}
}
@@ -402,7 +414,7 @@
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
- let (mut inputs, mut output) = match *item.kind {
+ let (mut inputs, mut output, where_clause) = match *item.kind {
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -412,7 +424,7 @@
inputs.retain(|a| a.id.is_some() || a.generics.is_some());
output.retain(|a| a.id.is_some() || a.generics.is_some());
- Some(IndexItemFunctionType { inputs, output })
+ Some(IndexItemFunctionType { inputs, output, where_clause })
}
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -432,96 +444,48 @@
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_id(type_)
}
- // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+ // The type parameters are converted to generics in `simplify_fn_type`
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+ clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
// Not supported yet
clean::BareFunction(_)
| clean::Generic(_)
| clean::ImplTrait(_)
- | clean::Tuple(_)
| clean::QPath { .. }
| clean::Infer => None,
}
}
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+ // other kinds of type parameters are identified by their name
+ Symbol(Symbol),
+ // every argument-position impl trait is its own type parameter
+ Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
self_: Option<&'a Type>,
generics: &Generics,
arg: &'a Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
res: &mut Vec<RenderType>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+ is_return: bool,
cache: &Cache,
) {
- fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
- // generics and impl trait are both identified by their generics,
- // rather than a type name itself
- let anonymous = ty.is_full_generic() || ty.is_impl_trait();
- let generics_empty = generics.is_empty();
-
- if anonymous {
- if generics_empty {
- // This is a type parameter with no trait bounds (for example: `T` in
- // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
- // with an empty type with an empty name. Let's just discard it.
- return;
- } else if generics.len() == 1 {
- // In this case, no need to go through an intermediate state if the type parameter
- // contains only one trait bound.
- //
- // For example:
- //
- // `fn foo<T: Display>(r: Option<T>) {}`
- //
- // In this case, it would contain:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "",
- // generics: [
- // name: "Display",
- // generics: []
- // }]
- // }]
- // }]
- // ```
- //
- // After removing the intermediate (unnecessary) type parameter, it'll become:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "Display",
- // generics: []
- // }]
- // }]
- // ```
- //
- // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
- // need to keep it as is!
- res.push(generics.pop().unwrap());
- return;
- }
- }
- let index_ty = get_index_type(&ty, generics);
- if index_ty.id.is_none() && generics_empty {
- return;
- }
- res.push(index_ty);
- }
-
if recurse >= 10 {
// FIXME: remove this whole recurse thing when the recursion bug is fixed
// See #59502 for the original issue.
@@ -548,88 +512,126 @@
// for its bounds.
if let Type::Generic(arg_s) = *arg {
// First we check if the bounds are in a `where` predicate...
+ let mut type_bounds = Vec::new();
for where_pred in generics.where_predicates.iter().filter(|g| match g {
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
_ => false,
}) {
- let mut ty_generics = Vec::new();
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
for bound in bounds.iter() {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
}
// Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
- let mut ty_generics = Vec::new();
for bound in bound.get_bounds().unwrap_or(&[]) {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ }
+ if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+ res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+ } else {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
}
} else if let Type::ImplTrait(ref bounds) = *arg {
- let mut ty_generics = Vec::new();
+ let mut type_bounds = Vec::new();
for bound in bounds {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ if is_return && !type_bounds.is_empty() {
+ // In parameter position, `impl Trait` is a unique thing.
+ res.push(RenderType { id: None, generics: Some(type_bounds) });
+ } else {
+ // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ }
} else if let Type::Slice(ref ty) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
} else if let Type::Array(ref ty, _) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
+ } else if let Type::Tuple(ref tys) = *arg {
+ let mut ty_generics = Vec::new();
+ for ty in tys {
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ res.push(get_index_type(arg, ty_generics));
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -639,18 +641,26 @@
let mut ty_generics = Vec::new();
if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() {
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
gen,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ let id = get_index_type_id(&arg);
+ if id.is_some() || !ty_generics.is_empty() {
+ res.push(RenderType {
+ id,
+ generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+ });
+ }
}
}
@@ -663,7 +673,7 @@
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
let decl = &func.decl;
let combined_generics;
@@ -689,21 +699,27 @@
(None, &func.generics)
};
- let mut all_types = Vec::new();
+ let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+ let mut arg_types = Vec::new();
for arg in decl.inputs.values.iter() {
- let mut args = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
- if !args.is_empty() {
- all_types.extend(args);
- } else {
- all_types.push(get_index_type(&arg.type_, vec![]));
- }
+ simplify_fn_type(
+ self_,
+ generics,
+ &arg.type_,
+ tcx,
+ 0,
+ &mut arg_types,
+ &mut rgen,
+ false,
+ cache,
+ );
}
let mut ret_types = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
- if ret_types.is_empty() {
- ret_types.push(get_index_type(&decl.output, vec![]));
- }
- (all_types, ret_types)
+ simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+ let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+ simplified_params.sort_by_key(|(idx, _)| -idx);
+ (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
}
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index f697abd..c7811b4 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,7 +9,7 @@
/**
* @typedef {{
* name: string,
- * id: integer,
+ * id: integer|null,
* fullPath: Array<string>,
* pathWithoutLast: Array<string>,
* pathLast: string,
@@ -37,6 +37,7 @@
* args: Array<QueryElement>,
* returned: Array<QueryElement>,
* foundElems: number,
+ * totalElems: number,
* literalSearch: boolean,
* corrections: Array<{from: string, to: integer}>,
* }}
@@ -103,7 +104,7 @@
*
* fn something() -> Result<usize, usize>
*
- * If output was allowed to be any RawFunctionType, it would look like this
+ * If output was allowed to be any RawFunctionType, it would look like thi
*
* [[], [50, [3, 3]]]
*
@@ -113,10 +114,56 @@
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
*
+ * The output can be skipped if it's actually unit and there's no type constraints. If thi
+ * function accepts constrained generics, then the output will be unconditionally emitted, and
+ * after it will come a list of trait constraints. The position of the item in the list will
+ * determine which type parameter it is. For example:
+ *
+ * [1, 2, 3, 4, 5]
+ * ^ ^ ^ ^ ^
+ * | | | | - generic parameter (-3) of trait 5
+ * | | | - generic parameter (-2) of trait 4
+ * | | - generic parameter (-1) of trait 3
+ * | - this function returns a single value (type 2)
+ * - this function takes a single input parameter (type 1)
+ *
+ * Or, for a less contrived version:
+ *
+ * [[[4, -1], 3], [[5, -1]], 11]
+ * -^^^^^^^---- ^^^^^^^ ^^
+ * | | | - generic parameter, roughly `where -1: 11`
+ * | | | since -1 is the type parameter and 11 the trait
+ * | | - function output 5<-1>
+ * | - the overall function signature is something like
+ * | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
+ * - function input, corresponds roughly to 4<-1>
+ * 4 is an index into the `p` array for a type
+ * -1 is the generic parameter, given by 11
+ *
+ * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
+ * function inputs and outputs:
+ *
+ * [-1, -1, [4, 3]]
+ * ^^^^^^ where -1: 4 + 3
+ *
+ * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
+ * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
+ * favor of `4 + 3`:
+ *
+ * [-1, -1, [[4, 3]]]
+ * ^^^^^^^^ where -1: 4 + 3
+ *
+ * [-1, -1, [5, [4, 3]]]
+ * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
+ *
+ * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
+ * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
+ *
* @typedef {(
* 0 |
* [(number|Array<RawFunctionType>)] |
- * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
+ * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
+ * Array<(number|Array<RawFunctionType>)>
* )}
*/
let RawFunctionSearchType;
@@ -136,6 +183,7 @@
* @typedef {{
* inputs: Array<FunctionType>,
* output: Array<FunctionType>,
+ * where_clause: Array<Array<FunctionType>>,
* }}
*/
let FunctionSearchType;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 097aa0b..eb25645 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -49,10 +49,12 @@
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
// but with the current code it's hard to get the right information in the right place.
- const mobileLocationTitle = document.querySelector(".mobile-topbar h2");
+ const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileLocationTitle && locationTitle) {
- mobileLocationTitle.innerHTML = locationTitle.innerHTML;
+ if (mobileTopbar && locationTitle) {
+ const mobileTitle = document.createElement("h2");
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTopbar.appendChild(mobileTitle);
}
}
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 0e270bb..407bf5f 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -3,6 +3,17 @@
"use strict";
+// polyfill
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
+if (!Array.prototype.toSpliced) {
+ // Can't use arrow functions, because we want `this`
+ Array.prototype.toSpliced = function() {
+ const me = this.slice();
+ Array.prototype.splice.apply(me, arguments);
+ return me;
+ };
+}
+
(function() {
// This mapping table should match the discriminants of
// `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -33,6 +44,7 @@
"attr",
"derive",
"traitalias",
+ "generic",
];
const longItemTypes = [
@@ -67,6 +79,7 @@
// used for special search precedence
const TY_PRIMITIVE = itemTypes.indexOf("primitive");
const TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_GENERIC = itemTypes.indexOf("generic");
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
function hasOwnPropertyRustdoc(obj, property) {
@@ -252,7 +265,7 @@
/**
* Add an item to the type Name->ID map, or, if one already exists, use it.
- * Returns the number. If name is "" or null, return -1 (pure generic).
+ * Returns the number. If name is "" or null, return null (pure generic).
*
* This is effectively string interning, so that function matching can be
* done more quickly. Two types with the same name but different item kinds
@@ -264,7 +277,7 @@
*/
function buildTypeMapIndex(name) {
if (name === "" || name === null) {
- return -1;
+ return null;
}
if (typeNameIdMap.has(name)) {
@@ -489,7 +502,7 @@
}
return {
name: "never",
- id: -1,
+ id: null,
fullPath: ["never"],
pathWithoutLast: [],
pathLast: "never",
@@ -531,7 +544,7 @@
}
return {
name: name.trim(),
- id: -1,
+ id: null,
fullPath: pathSegments,
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
pathLast: pathSegments[pathSegments.length - 1],
@@ -660,7 +673,7 @@
}
elems.push({
name: "[]",
- id: -1,
+ id: null,
fullPath: ["[]"],
pathWithoutLast: [],
pathLast: "[]",
@@ -971,9 +984,13 @@
returned: [],
// Total number of "top" elements (does not include generics).
foundElems: 0,
+ // Total number of elements (includes generics).
+ totalElems: 0,
literalSearch: false,
error: null,
correction: null,
+ proposeCorrectionFrom: null,
+ proposeCorrectionTo: null,
};
}
@@ -1014,64 +1031,10 @@
/**
* Parses the query.
*
- * The supported syntax by this parser is as follow:
+ * The supported syntax by this parser is given in the rustdoc book chapter
+ * /src/doc/rustdoc/src/read-documentation/search.md
*
- * ident = *(ALPHA / DIGIT / "_")
- * path = ident *(DOUBLE-COLON/{WS} ident) [!]
- * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
- * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
- * type-sep = *WS COMMA *(COMMA)
- * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
- * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
- * CLOSE-ANGLE-BRACKET
- * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
- *
- * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
- * type-search = [ nonempty-arg-list ] [ return-args ]
- *
- * query = *WS (exact-search / type-search) *WS
- *
- * type-filter = (
- * "mod" /
- * "externcrate" /
- * "import" /
- * "struct" /
- * "enum" /
- * "fn" /
- * "type" /
- * "static" /
- * "trait" /
- * "impl" /
- * "tymethod" /
- * "method" /
- * "structfield" /
- * "variant" /
- * "macro" /
- * "primitive" /
- * "associatedtype" /
- * "constant" /
- * "associatedconstant" /
- * "union" /
- * "foreigntype" /
- * "keyword" /
- * "existential" /
- * "attr" /
- * "derive" /
- * "traitalias")
- *
- * OPEN-ANGLE-BRACKET = "<"
- * CLOSE-ANGLE-BRACKET = ">"
- * OPEN-SQUARE-BRACKET = "["
- * CLOSE-SQUARE-BRACKET = "]"
- * COLON = ":"
- * DOUBLE-COLON = "::"
- * QUOTE = %x22
- * COMMA = ","
- * RETURN-ARROW = "->"
- *
- * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- * DIGIT = %x30-39
- * WS = %x09 / " "
+ * When adding new things to the parser, add them there, too!
*
* @param {string} val - The user query
*
@@ -1124,6 +1087,7 @@
query.literalSearch = parserState.totalElems > 1;
}
query.foundElems = query.elems.length + query.returned.length;
+ query.totalElems = parserState.totalElems;
return query;
}
@@ -1172,7 +1136,7 @@
const out = [];
for (const result of results) {
- if (result.id > -1) {
+ if (result.id !== -1) {
const obj = searchIndex[result.id];
obj.dist = result.dist;
const res = buildHrefAndPath(obj);
@@ -1348,192 +1312,311 @@
* This function checks generics in search query `queryElem` can all be found in the
* search index (`fnType`),
*
- * @param {FunctionType} fnType - The object to check.
- * @param {QueryElement} queryElem - The element from the parsed query.
+ * This function returns `true` if it matches, and also writes the results to mgensInout.
+ * It returns `false` if no match is found, and leaves mgensInout untouched.
+ *
+ * @param {FunctionType} fnType - The object to check.
+ * @param {QueryElement} queryElem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function checkGenerics(fnType, queryElem) {
- return unifyFunctionTypes(fnType.generics, queryElem.generics);
+ function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
+ return unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgensInout,
+ mgens => {
+ if (mgensInout) {
+ for (const [fid, qid] of mgens.entries()) {
+ mgensInout.set(fid, qid);
+ }
+ }
+ return true;
+ }
+ );
}
/**
* This function checks if a list of search query `queryElems` can all be found in the
* search index (`fnTypes`).
*
- * @param {Array<FunctionType>} fnTypes - The objects to check.
+ * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+ * supplied, it will call that function with mgens, and that callback can accept or
+ * reject the result bu returning `true` or `false`. If the callback returns false,
+ * then this function will try with a different solution, or bail with false if it
+ * runs out of candidates.
+ *
+ * @param {Array<FunctionType>} fnTypes - The objects to check.
* @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensIn
+ * - Map functions generics to query generics (never modified).
+ * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function unifyFunctionTypes(fnTypes, queryElems) {
- // This search engine implements order-agnostic unification. There
- // should be no missing duplicates (generics have "bag semantics"),
- // and the row is allowed to have extras.
+ function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
+ /**
+ * @type Map<integer, integer>
+ */
+ let mgens = new Map(mgensIn);
if (queryElems.length === 0) {
- return true;
+ return !solutionCb || solutionCb(mgens);
}
- if (!fnTypes || fnTypes.length === 0) {
+ if (!fnTypesIn || fnTypesIn.length === 0) {
return false;
}
+ const ql = queryElems.length;
+ let fl = fnTypesIn.length;
/**
- * @type Map<integer, QueryElement[]>
+ * @type Array<FunctionType>
*/
- const queryElemSet = new Map();
- const addQueryElemToQueryElemSet = queryElem => {
- let currentQueryElemList;
- if (queryElemSet.has(queryElem.id)) {
- currentQueryElemList = queryElemSet.get(queryElem.id);
- } else {
- currentQueryElemList = [];
- queryElemSet.set(queryElem.id, currentQueryElemList);
- }
- currentQueryElemList.push(queryElem);
- };
- for (const queryElem of queryElems) {
- addQueryElemToQueryElemSet(queryElem);
- }
+ let fnTypes = fnTypesIn.slice();
/**
- * @type Map<integer, FunctionType[]>
+ * loop works by building up a solution set in the working arrays
+ * fnTypes gets mutated in place to make this work, while queryElems
+ * is left alone
+ *
+ * vvvvvvv `i` points here
+ * queryElems = [ good, good, good, unknown, unknown ],
+ * fnTypes = [ good, good, good, unknown, unknown ],
+ * ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
+ * | looking for candidates
+ * everything before `i` is the
+ * current working solution
+ *
+ * Everything in the current working solution is known to be a good
+ * match, but it might not be the match we wind up going with, because
+ * there might be more than one candidate match, and we need to try them all
+ * before giving up. So, to handle this, it backtracks on failure.
+ *
+ * @type Array<{
+ * "fnTypesScratch": Array<FunctionType>,
+ * "queryElemsOffset": integer,
+ * "fnTypesOffset": integer
+ * }>
*/
- const fnTypeSet = new Map();
- const addFnTypeToFnTypeSet = fnType => {
- // Pure generic, or an item that's not matched by any query elems.
- // Try [unboxing] it.
- //
- // [unboxing]:
- // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
- if (fnType.id === -1 || !(
- queryElemSet.has(fnType.id) ||
- (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
- (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
- )) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
+ const backtracking = [];
+ let i = 0;
+ let j = 0;
+ const backtrack = () => {
+ while (backtracking.length !== 0) {
+ // this session failed, but there are other possible solutions
+ // to backtrack, reset to (a copy of) the old array, do the swap or unboxing
+ const {
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset,
+ fnTypesOffset,
+ unbox,
+ } = backtracking.pop();
+ mgens = new Map(mgensScratch);
+ const fnType = fnTypesScratch[fnTypesOffset];
+ const queryElem = queryElems[queryElemsOffset];
+ if (unbox) {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ continue;
+ }
+ mgens.set(fnType.id, 0);
+ }
+ const generics = fnType.id < 0 ?
+ whereClause[(-fnType.id) - 1] :
+ fnType.generics;
+ fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
+ fl = fnTypes.length;
+ // re-run the matching algorithm on this item
+ i = queryElemsOffset - 1;
+ } else {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ continue;
+ }
+ mgens.set(fnType.id, queryElem.id);
+ }
+ fnTypes = fnTypesScratch.slice();
+ fl = fnTypes.length;
+ const tmp = fnTypes[queryElemsOffset];
+ fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+ fnTypes[fnTypesOffset] = tmp;
+ // this is known as a good match; go to the next one
+ i = queryElemsOffset;
}
- return;
- }
- let currentQueryElemList = queryElemSet.get(fnType.id) || [];
- let matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- if (matchIdx === -1 &&
- (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
- queryContainsArrayOrSliceElem
- ) {
- currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
- matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- }
- // None of the query elems match the function type.
- // Try [unboxing] it.
- if (matchIdx === -1) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
- }
- return;
- }
- let currentFnTypeList;
- if (fnTypeSet.has(fnType.id)) {
- currentFnTypeList = fnTypeSet.get(fnType.id);
- } else {
- currentFnTypeList = [];
- fnTypeSet.set(fnType.id, currentFnTypeList);
- }
- currentFnTypeList.push(fnType);
- };
- for (const fnType of fnTypes) {
- addFnTypeToFnTypeSet(fnType);
- }
- const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
- if (queryElemList.length === 0) {
return true;
}
- // Multiple items in one list might match multiple items in another.
- // Since an item with fewer generics can match an item with more, we
- // need to check all combinations for a potential match.
- const queryElem = queryElemList.pop();
- const l = currentFnTypeList.length;
- for (let i = 0; i < l; i += 1) {
- const fnType = currentFnTypeList[i];
- if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
- continue;
- }
- const queryElemPathLength = queryElem.pathWithoutLast.length;
- // If the query element is a path (it contains `::`), we need to check if this
- // path is compatible with the target type.
- if (queryElemPathLength > 0) {
- const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
- fnType.path.split("::") : [];
- // If the path provided in the query element is longer than this type,
- // no need to check it since it won't match in any case.
- if (queryElemPathLength > fnTypePath.length) {
- continue;
- }
- let i = 0;
- for (const path of fnTypePath) {
- if (path === queryElem.pathWithoutLast[i]) {
- i += 1;
- if (i >= queryElemPathLength) {
- break;
- }
- }
- }
- if (i < queryElemPathLength) {
- // If we didn't find all parts of the path of the query element inside
- // the fn type, then it's not the right one.
- continue;
- }
- }
- if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
- currentFnTypeList.splice(i, 1);
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- return true;
- }
- currentFnTypeList.splice(i, 0, fnType);
- }
- }
return false;
};
- const handleQueryElemList = (id, queryElemList) => {
- if (!fnTypeSet.has(id)) {
- if (id === typeNameIdOfArrayOrSlice) {
- return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
- handleQueryElemList(typeNameIdOfArray, queryElemList);
+ for (i = 0; i !== ql; ++i) {
+ const queryElem = queryElems[i];
+ /**
+ * list of potential function types that go with the current query element.
+ * @type Array<integer>
+ */
+ const matchCandidates = [];
+ let fnTypesScratch = null;
+ let mgensScratch = null;
+ // don't try anything before `i`, because they've already been
+ // paired off with the other query elements
+ for (j = i; j !== fl; ++j) {
+ const fnType = fnTypes[j];
+ if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgens,
+ mgensScratch => {
+ matchCandidates.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: false,
+ });
+ return false; // "reject" all candidates to gather all of them
+ }
+ );
}
- return false;
- }
- const currentFnTypeList = fnTypeSet.get(id);
- if (currentFnTypeList.length < queryElemList.length) {
- // It's not possible for all the query elems to find a match.
- return false;
- }
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- // Found a solution.
- // Any items that weren't used for it can be unboxed, and might form
- // part of the solution for another item.
- for (const innerFnType of currentFnTypeList) {
- addFnTypeToFnTypeSet(innerFnType);
+ if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ if (!mgensScratch) {
+ mgensScratch = new Map(mgens);
+ }
+ backtracking.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: true,
+ });
}
- fnTypeSet.delete(id);
}
- return result;
- };
- let queryElemSetSize = -1;
- while (queryElemSetSize !== queryElemSet.size) {
- queryElemSetSize = queryElemSet.size;
- for (const [id, queryElemList] of queryElemSet) {
- if (handleQueryElemList(id, queryElemList)) {
- queryElemSet.delete(id);
+ if (matchCandidates.length === 0) {
+ if (backtrack()) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ // use the current candidate
+ const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+ if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
+ mgens.set(fnTypes[candidate].id, queryElems[i].id);
+ }
+ for (const [fid, qid] of mgensNew) {
+ mgens.set(fid, qid);
+ }
+ // `i` and `j` are paired off
+ // `queryElems[i]` is left in place
+ // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
+ const tmp = fnTypes[candidate];
+ fnTypes[candidate] = fnTypes[i];
+ fnTypes[i] = tmp;
+ // write other candidates to backtracking queue
+ for (const otherCandidate of matchCandidates) {
+ backtracking.push(otherCandidate);
+ }
+ // If we're on the last item, check the solution with the callback
+ // backtrack if the callback says its unsuitable
+ while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+ if (!backtrack()) {
+ return false;
}
}
}
- return queryElemSetSize === 0;
+ return true;
+ }
+ function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
+ // type filters look like `trait:Read` or `enum:Result`
+ if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
+ return false;
+ }
+ // fnType.id < 0 means generic
+ // queryElem.id < 0 does too
+ // mgens[fnType.id] = queryElem.id
+ // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
+ // and should make that same decision everywhere it appears
+ if (fnType.id < 0 && queryElem.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ return false;
+ }
+ for (const [fid, qid] of mgens.entries()) {
+ if (fnType.id !== fid && queryElem.id === qid) {
+ return false;
+ }
+ if (fnType.id === fid && queryElem.id !== qid) {
+ return false;
+ }
+ }
+ } else if (fnType.id !== null) {
+ if (queryElem.id === typeNameIdOfArrayOrSlice &&
+ (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+ ) {
+ // [] matches primitive:array or primitive:slice
+ // if it matches, then we're fine, and this is an appropriate match candidate
+ } else if (fnType.id !== queryElem.id) {
+ return false;
+ }
+ // If the query elem has generics, and the function doesn't,
+ // it can't match.
+ if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
+ return false;
+ }
+ // If the query element is a path (it contains `::`), we need to check if this
+ // path is compatible with the target type.
+ const queryElemPathLength = queryElem.pathWithoutLast.length;
+ if (queryElemPathLength > 0) {
+ const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+ fnType.path.split("::") : [];
+ // If the path provided in the query element is longer than this type,
+ // no need to check it since it won't match in any case.
+ if (queryElemPathLength > fnTypePath.length) {
+ return false;
+ }
+ let i = 0;
+ for (const path of fnTypePath) {
+ if (path === queryElem.pathWithoutLast[i]) {
+ i += 1;
+ if (i >= queryElemPathLength) {
+ break;
+ }
+ }
+ }
+ if (i < queryElemPathLength) {
+ // If we didn't find all parts of the path of the query element inside
+ // the fn type, then it's not the right one.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
+ if (fnType.id < 0 && queryElem.id >= 0) {
+ if (!whereClause) {
+ return false;
+ }
+ // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
+ // mgens[fnType.id] === null indicates that we haven't decided yet
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ return false;
+ }
+ // This is only a potential unbox if the search query appears in the where clause
+ // for example, searching `Read -> usize` should find
+ // `fn read_all<R: Read>(R) -> Result<usize>`
+ // generic `R` is considered "unboxed"
+ return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+ } else if (fnType.generics && fnType.generics.length > 0) {
+ return checkIfInList(fnType.generics, queryElem, whereClause);
+ }
+ return false;
}
/**
@@ -1541,13 +1624,14 @@
* generics (if any).
*
* @param {Array<FunctionType>} list
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if found, false otherwise.
*/
- function checkIfInList(list, elem) {
+ function checkIfInList(list, elem, whereClause) {
for (const entry of list) {
- if (checkType(entry, elem)) {
+ if (checkType(entry, elem, whereClause)) {
return true;
}
}
@@ -1559,14 +1643,26 @@
* generics (if any).
*
* @param {Row} row
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if the type matches, false otherwise.
*/
- function checkType(row, elem) {
- if (row.id === -1) {
+ function checkType(row, elem, whereClause) {
+ if (row.id === null) {
// This is a pure "generic" search, no need to run other checks.
- return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
+ return row.generics.length > 0
+ ? checkIfInList(row.generics, elem, whereClause)
+ : false;
+ }
+
+ if (row.id < 0 && elem.id >= 0) {
+ const gid = (-row.id) - 1;
+ return checkIfInList(whereClause[gid], elem, whereClause);
+ }
+
+ if (row.id < 0 && elem.id < 0) {
+ return true;
}
const matchesExact = row.id === elem.id;
@@ -1576,7 +1672,7 @@
if ((matchesExact || matchesArrayOrSlice) &&
typePassesFilter(elem.typeFilter, row.ty)) {
if (elem.generics.length > 0) {
- return checkGenerics(row, elem);
+ return checkGenerics(row, elem, whereClause, new Map());
}
return true;
}
@@ -1584,7 +1680,7 @@
// If the current item does not match, try [unboxing] the generic.
// [unboxing]:
// https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- return checkIfInList(row.generics, elem);
+ return checkIfInList(row.generics, elem, whereClause);
}
function checkPath(contains, ty, maxEditDistance) {
@@ -1785,13 +1881,15 @@
const fullId = row.id;
const searchWord = searchWords[pos];
- const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
+ const in_args = row.type && row.type.inputs
+ && checkIfInList(row.type.inputs, elem, row.type.where_clause);
if (in_args) {
// path_dist is 0 because no parent path information is currently stored
// in the search index
addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
}
- const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
+ const returned = row.type && row.type.output
+ && checkIfInList(row.type.output, elem, row.type.where_clause);
if (returned) {
addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
}
@@ -1853,10 +1951,20 @@
}
// If the result is too "bad", we return false and it ends this search.
- if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
- return;
- }
- if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
+ if (!unifyFunctionTypes(
+ row.type.inputs,
+ parsedQuery.elems,
+ row.type.where_clause,
+ null,
+ mgens => {
+ return unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause,
+ mgens
+ );
+ }
+ )) {
return;
}
@@ -1876,6 +1984,11 @@
const maxEditDistance = Math.floor(queryLen / 3);
/**
+ * @type {Map<string, integer>}
+ */
+ const genericSymbols = new Map();
+
+ /**
* Convert names to ids in parsed query elements.
* This is not used for the "In Names" tab, but is used for the
* "In Params", "In Returns", and "In Function Signature" tabs.
@@ -1891,7 +2004,7 @@
if (typeNameIdMap.has(elem.pathLast)) {
elem.id = typeNameIdMap.get(elem.pathLast);
} else if (!parsedQuery.literalSearch) {
- let match = -1;
+ let match = null;
let matchDist = maxEditDistance + 1;
let matchName = "";
for (const [name, id] of typeNameIdMap) {
@@ -1905,11 +2018,52 @@
matchName = name;
}
}
- if (match !== -1) {
+ if (match !== null) {
parsedQuery.correction = matchName;
}
elem.id = match;
}
+ if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
+ && elem.generics.length === 0)
+ || elem.typeFilter === TY_GENERIC) {
+ if (genericSymbols.has(elem.name)) {
+ elem.id = genericSymbols.get(elem.name);
+ } else {
+ elem.id = -(genericSymbols.size + 1);
+ genericSymbols.set(elem.name, elem.id);
+ }
+ if (elem.typeFilter === -1 && elem.name.length >= 3) {
+ // Silly heuristic to catch if the user probably meant
+ // to not write a generic parameter. We don't use it,
+ // just bring it up.
+ const maxPartDistance = Math.floor(elem.name.length / 3);
+ let matchDist = maxPartDistance + 1;
+ let matchName = "";
+ for (const name of typeNameIdMap.keys()) {
+ const dist = editDistance(name, elem.name, maxPartDistance);
+ if (dist <= matchDist && dist <= maxPartDistance) {
+ if (dist === matchDist && matchName > name) {
+ continue;
+ }
+ matchDist = dist;
+ matchName = name;
+ }
+ }
+ if (matchName !== "") {
+ parsedQuery.proposeCorrectionFrom = elem.name;
+ parsedQuery.proposeCorrectionTo = matchName;
+ }
+ }
+ elem.typeFilter = TY_GENERIC;
+ }
+ if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
+ // Rust does not have HKT
+ parsedQuery.error = [
+ "Generic type parameter ",
+ elem.name,
+ " does not accept generic parameters",
+ ];
+ }
for (const elem2 of elem.generics) {
convertNameToId(elem2);
}
@@ -1943,8 +2097,11 @@
elem = parsedQuery.returned[0];
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
- in_returned = row.type &&
- unifyFunctionTypes(row.type.output, parsedQuery.returned);
+ in_returned = row.type && unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause
+ );
if (in_returned) {
addIntoResults(
results_others,
@@ -2295,6 +2452,13 @@
"Showing results for closest type name " +
`"${results.query.correction}" instead.</h3>`;
}
+ if (results.query.proposeCorrectionFrom !== null) {
+ const orig = results.query.proposeCorrectionFrom;
+ const targ = results.query.proposeCorrectionTo;
+ output += "<h3 class=\"search-corrections\">" +
+ `Type "${orig}" not found and used as generic parameter. ` +
+ `Consider searching for "${targ}" instead.</h3>`;
+ }
const resultsElem = document.createElement("div");
resultsElem.id = "results";
@@ -2396,37 +2560,54 @@
* @return {Array<FunctionSearchType>}
*/
function buildItemSearchTypeAll(types, lowercasePaths) {
+ return types.map(type => buildItemSearchType(type, lowercasePaths));
+ }
+
+ /**
+ * Converts a single type.
+ *
+ * @param {RawFunctionType} type
+ */
+ function buildItemSearchType(type, lowercasePaths) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
- return types.map(type => {
- let pathIndex, generics;
- if (typeof type === "number") {
- pathIndex = type;
- generics = [];
- } else {
- pathIndex = type[PATH_INDEX_DATA];
- generics = buildItemSearchTypeAll(
- type[GENERICS_DATA],
- lowercasePaths
- );
- }
- // `0` is used as a sentinel because it's fewer bytes than `null`
- if (pathIndex === 0) {
- return {
- id: -1,
- ty: null,
- path: null,
- generics: generics,
- };
- }
- const item = lowercasePaths[pathIndex - 1];
+ let pathIndex, generics;
+ if (typeof type === "number") {
+ pathIndex = type;
+ generics = [];
+ } else {
+ pathIndex = type[PATH_INDEX_DATA];
+ generics = buildItemSearchTypeAll(
+ type[GENERICS_DATA],
+ lowercasePaths
+ );
+ }
+ if (pathIndex < 0) {
+ // types less than 0 are generic parameters
+ // the actual names of generic parameters aren't stored, since they aren't API
return {
- id: buildTypeMapIndex(item.name),
- ty: item.ty,
- path: item.path,
- generics: generics,
+ id: pathIndex,
+ ty: TY_GENERIC,
+ path: null,
+ generics,
};
- });
+ }
+ if (pathIndex === 0) {
+ // `0` is used as a sentinel because it's fewer bytes than `null`
+ return {
+ id: null,
+ ty: null,
+ path: null,
+ generics,
+ };
+ }
+ const item = lowercasePaths[pathIndex - 1];
+ return {
+ id: buildTypeMapIndex(item.name),
+ ty: item.ty,
+ path: item.path,
+ generics,
+ };
}
/**
@@ -2454,23 +2635,7 @@
}
let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") {
- const pathIndex = functionSearchType[INPUTS_DATA];
- if (pathIndex === 0) {
- inputs = [{
- id: -1,
- ty: null,
- path: null,
- generics: [],
- }];
- } else {
- const item = lowercasePaths[pathIndex - 1];
- inputs = [{
- id: buildTypeMapIndex(item.name),
- ty: item.ty,
- path: item.path,
- generics: [],
- }];
- }
+ inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
@@ -2479,23 +2644,7 @@
}
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
- const pathIndex = functionSearchType[OUTPUT_DATA];
- if (pathIndex === 0) {
- output = [{
- id: -1,
- ty: null,
- path: null,
- generics: [],
- }];
- } else {
- const item = lowercasePaths[pathIndex - 1];
- output = [{
- id: buildTypeMapIndex(item.name),
- ty: item.ty,
- path: item.path,
- generics: [],
- }];
- }
+ output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
@@ -2505,8 +2654,15 @@
} else {
output = [];
}
+ const where_clause = [];
+ const l = functionSearchType.length;
+ for (let i = 2; i < l; ++i) {
+ where_clause.push(typeof functionSearchType[i] === "number"
+ ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
+ : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+ }
return {
- inputs, output,
+ inputs, output, where_clause,
};
}
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index b4b9c31..579c782 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -84,8 +84,7 @@
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% endif %}
</a> {# #}
- <h2></h2> {# #}
- </nav> {# #}
+ </nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 2431923..8846633 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -204,7 +204,7 @@
// similar to 2., but with the a frozen variant) (e.g. borrowing
// `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
- err == ErrorHandled::TooGeneric
+ matches!(err, ErrorHandled::TooGeneric(..))
},
|val| val.map_or(true, |val| inner(cx, val, ty)),
)
@@ -244,8 +244,8 @@
};
tcx.const_eval_global_id_for_typeck(param_env, cid, span)
},
- Ok(None) => Err(ErrorHandled::TooGeneric),
- Err(err) => Err(ErrorHandled::Reported(err.into())),
+ Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))),
}
}
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index fcb90c63..c88fce2 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -656,7 +656,7 @@
}
pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant<'tcx>> {
- use rustc_middle::mir::interpret::ConstValue;
+ use rustc_middle::mir::ConstValue;
match result {
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index f0b4ede..9e25d97 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -13,7 +13,7 @@
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::{ConstValue, interpret::Scalar};
use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
diff --git a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
index d14974f..f8ace79 100644
--- a/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/suppress_lint_in_const/test.stderr
@@ -4,7 +4,7 @@
LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/test.rs:37:5
|
LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index 64facf2..1c34875 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -24,7 +24,7 @@
LL | const { &ARR[idx4()] };
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/indexing_slicing_index.rs:48:5
|
LL | const { &ARR[idx4()] };
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index ff1d5ce..bb1fa6e 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -24,6 +24,7 @@
glob = "0.3.0"
lazycell = "1.3.0"
anyhow = "1"
+home = "0.5.5"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 65af650..7b42d8e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -695,7 +695,7 @@
}
fn run_command_to_procres(&self, cmd: &mut Command) -> ProcRes {
- let output = cmd.output().unwrap_or_else(|_| panic!("failed to exec `{cmd:?}`"));
+ let output = cmd.output().unwrap_or_else(|e| panic!("failed to exec `{cmd:?}`: {e:?}"));
let proc_res = ProcRes {
status: output.status,
@@ -1216,12 +1216,12 @@
.arg(&exe_file)
.arg(&self.config.adb_test_dir)
.status()
- .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
+ .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
Command::new(adb_path)
.args(&["forward", "tcp:5039", "tcp:5039"])
.status()
- .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
+ .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
let adb_arg = format!(
"export LD_LIBRARY_PATH={}; \
@@ -1238,7 +1238,7 @@
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
- .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
+ .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
// Wait for the gdbserver to print out "Listening on port ..."
// at which point we know that it's started and then we can
@@ -1263,7 +1263,7 @@
let Output { status, stdout, stderr } = Command::new(&gdb_path)
.args(debugger_opts)
.output()
- .unwrap_or_else(|_| panic!("failed to exec `{:?}`", gdb_path));
+ .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
let cmdline = {
let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
gdb.args(debugger_opts);
@@ -2277,7 +2277,7 @@
add_dylib_path(&mut command, iter::once(lib_path).chain(aux_path));
let mut child = disable_error_reporting(|| command.spawn())
- .unwrap_or_else(|_| panic!("failed to exec `{:?}`", &command));
+ .unwrap_or_else(|e| panic!("failed to exec `{command:?}`: {e:?}"));
if let Some(input) = input {
child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
}
@@ -2335,6 +2335,15 @@
rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
+ // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
+ // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
+ // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
+ // source will be shown, causing a blessing hell.
+ rustc.arg("-Z").arg(format!(
+ "ignore-directory-in-diagnostics-source-blocks={}",
+ home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
+ ));
+
// Optionally prevent default --sysroot if specified in test compile-flags.
if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
&& !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
@@ -3838,8 +3847,8 @@
.open(coverage_file_path.as_path())
.expect("could not create or open file");
- if writeln!(file, "{}", self.testpaths.file.display()).is_err() {
- panic!("couldn't write to {}", coverage_file_path.display());
+ if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) {
+ panic!("couldn't write to {}: {e:?}", coverage_file_path.display());
}
}
} else if self.props.run_rustfix {
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 17bed38..02648fe 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -13,6 +13,7 @@
"aarch64-apple-darwin",
"aarch64-apple-ios",
"aarch64-apple-ios-sim",
+ "aarch64-apple-ios-macabi",
"aarch64-unknown-fuchsia",
"aarch64-linux-android",
"aarch64-unknown-linux-gnu",
@@ -22,6 +23,7 @@
"i686-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-apple-ios",
+ "x86_64-apple-ios-macabi",
"x86_64-unknown-fuchsia",
"x86_64-linux-android",
"x86_64-unknown-freebsd",
@@ -60,6 +62,7 @@
// "aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
+ "x86_64-apple-ios-macabi",
"x86_64-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
];
@@ -75,9 +78,11 @@
"aarch64-apple-darwin",
"aarch64-apple-ios",
"aarch64-apple-ios-sim",
+ "aarch64-apple-ios-macabi",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-apple-ios",
+ "x86_64-apple-ios-macabi",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 0c7e827..72d9dbd 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -142,9 +142,8 @@
fn eval_path_scalar(&self, path: &[&str]) -> Scalar<Provenance> {
let this = self.eval_context_ref();
let instance = this.resolve_path(path, Namespace::ValueNS);
- let cid = GlobalId { instance, promoted: None };
// We don't give a span -- this isn't actually used directly by the program anyway.
- let const_val = this.eval_global(cid, None).unwrap_or_else(|err| {
+ let const_val = this.eval_global(instance).unwrap_or_else(|err| {
panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
});
this.read_scalar(&const_val)
diff --git a/src/tools/miri/tests/fail/const-ub-checks.stderr b/src/tools/miri/tests/fail/const-ub-checks.stderr
index 596a6bb..d2b9018 100644
--- a/src/tools/miri/tests/fail/const-ub-checks.stderr
+++ b/src/tools/miri/tests/fail/const-ub-checks.stderr
@@ -4,7 +4,7 @@
LL | ptr.read();
| ^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-ub-checks.rs:LL:CC
|
LL | let _x = UNALIGNED_READ;
diff --git a/src/tools/miri/tests/fail/erroneous_const.stderr b/src/tools/miri/tests/fail/erroneous_const.stderr
index 209c4a9..cacf866 100644
--- a/src/tools/miri/tests/fail/erroneous_const.stderr
+++ b/src/tools/miri/tests/fail/erroneous_const.stderr
@@ -6,7 +6,7 @@
|
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/erroneous_const.rs:LL:CC
|
LL | let _ = PrintName::<T>::VOID;
diff --git a/src/tools/miri/tests/fail/erroneous_const2.stderr b/src/tools/miri/tests/fail/erroneous_const2.stderr
index 9aad1fc..36e83b8 100644
--- a/src/tools/miri/tests/fail/erroneous_const2.stderr
+++ b/src/tools/miri/tests/fail/erroneous_const2.stderr
@@ -4,13 +4,13 @@
LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
| ^^^^^ attempt to compute `5_u32 - 6_u32`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/erroneous_const2.rs:LL:CC
|
LL | println!("{}", FOO);
| ^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/erroneous_const2.rs:LL:CC
|
LL | println!("{}", FOO);
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index 3f7dba8..f1c3dd6 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -21,3 +21,5 @@
serde_json = "1"
glob = "0.3"
tempfile = "3.5"
+derive_builder = "0.12"
+clap = { version = "4", features = ["derive"] }
diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs
new file mode 100644
index 0000000..ff782a1
--- /dev/null
+++ b/src/tools/opt-dist/src/environment.rs
@@ -0,0 +1,108 @@
+use camino::Utf8PathBuf;
+use derive_builder::Builder;
+
+#[derive(Builder)]
+pub struct Environment {
+ host_triple: String,
+ python_binary: String,
+ /// The rustc checkout, where the compiler source is located.
+ checkout_dir: Utf8PathBuf,
+ /// The main directory where the build occurs. Stage0 rustc and cargo have to be available in
+ /// this directory before `opt-dist` is started.
+ build_dir: Utf8PathBuf,
+ /// Directory where the optimization artifacts (PGO/BOLT profiles, etc.)
+ /// will be stored.
+ artifact_dir: Utf8PathBuf,
+ /// Path to the host LLVM used to compile LLVM in `src/llvm-project`.
+ host_llvm_dir: Utf8PathBuf,
+ /// List of test paths that should be skipped when testing the optimized artifacts.
+ skipped_tests: Vec<String>,
+ /// Directory containing a pre-built rustc-perf checkout.
+ #[builder(default)]
+ prebuilt_rustc_perf: Option<Utf8PathBuf>,
+ use_bolt: bool,
+ shared_llvm: bool,
+}
+
+impl Environment {
+ pub fn host_triple(&self) -> &str {
+ &self.host_triple
+ }
+
+ pub fn python_binary(&self) -> &str {
+ &self.python_binary
+ }
+
+ pub fn checkout_path(&self) -> Utf8PathBuf {
+ self.checkout_dir.clone()
+ }
+
+ pub fn build_root(&self) -> Utf8PathBuf {
+ self.build_dir.clone()
+ }
+
+ pub fn build_artifacts(&self) -> Utf8PathBuf {
+ self.build_root().join("build").join(&self.host_triple)
+ }
+
+ pub fn artifact_dir(&self) -> Utf8PathBuf {
+ self.artifact_dir.clone()
+ }
+
+ pub fn cargo_stage_0(&self) -> Utf8PathBuf {
+ self.build_artifacts()
+ .join("stage0")
+ .join("bin")
+ .join(format!("cargo{}", executable_extension()))
+ }
+
+ pub fn rustc_stage_0(&self) -> Utf8PathBuf {
+ self.build_artifacts()
+ .join("stage0")
+ .join("bin")
+ .join(format!("rustc{}", executable_extension()))
+ }
+
+ pub fn rustc_stage_2(&self) -> Utf8PathBuf {
+ self.build_artifacts()
+ .join("stage2")
+ .join("bin")
+ .join(format!("rustc{}", executable_extension()))
+ }
+
+ pub fn prebuilt_rustc_perf(&self) -> Option<Utf8PathBuf> {
+ self.prebuilt_rustc_perf.clone()
+ }
+
+ /// Path to the built rustc-perf benchmark suite.
+ pub fn rustc_perf_dir(&self) -> Utf8PathBuf {
+ self.artifact_dir.join("rustc-perf")
+ }
+
+ pub fn host_llvm_dir(&self) -> Utf8PathBuf {
+ self.host_llvm_dir.clone()
+ }
+
+ pub fn use_bolt(&self) -> bool {
+ self.use_bolt
+ }
+
+ pub fn supports_shared_llvm(&self) -> bool {
+ self.shared_llvm
+ }
+
+ pub fn skipped_tests(&self) -> &[String] {
+ &self.skipped_tests
+ }
+}
+
+/// What is the extension of binary executables on this platform?
+#[cfg(target_family = "unix")]
+pub fn executable_extension() -> &'static str {
+ ""
+}
+
+#[cfg(target_family = "windows")]
+pub fn executable_extension() -> &'static str {
+ ".exe"
+}
diff --git a/src/tools/opt-dist/src/environment/linux.rs b/src/tools/opt-dist/src/environment/linux.rs
deleted file mode 100644
index 58b7e6d..0000000
--- a/src/tools/opt-dist/src/environment/linux.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use crate::environment::Environment;
-use crate::exec::cmd;
-use crate::utils::io::copy_directory;
-use camino::{Utf8Path, Utf8PathBuf};
-
-pub(super) struct LinuxEnvironment;
-
-impl Environment for LinuxEnvironment {
- fn python_binary(&self) -> &'static str {
- "python3"
- }
-
- fn checkout_path(&self) -> Utf8PathBuf {
- Utf8PathBuf::from("/checkout")
- }
-
- fn host_llvm_dir(&self) -> Utf8PathBuf {
- Utf8PathBuf::from("/rustroot")
- }
-
- fn opt_artifacts(&self) -> Utf8PathBuf {
- Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")
- }
-
- fn build_root(&self) -> Utf8PathBuf {
- self.checkout_path().join("obj")
- }
-
- fn prepare_rustc_perf(&self) -> anyhow::Result<()> {
- // /tmp/rustc-perf comes from the x64 dist Dockerfile
- copy_directory(Utf8Path::new("/tmp/rustc-perf"), &self.rustc_perf_dir())?;
- cmd(&[self.cargo_stage_0().as_str(), "build", "-p", "collector"])
- .workdir(&self.rustc_perf_dir())
- .env("RUSTC", &self.rustc_stage_0().into_string())
- .env("RUSTC_BOOTSTRAP", "1")
- .run()?;
- Ok(())
- }
-
- fn supports_bolt(&self) -> bool {
- true
- }
-
- fn supports_shared_llvm(&self) -> bool {
- true
- }
-
- fn executable_extension(&self) -> &'static str {
- ""
- }
-
- fn skipped_tests(&self) -> &'static [&'static str] {
- &[
- // Fails because of linker errors, as of June 2023.
- "tests/ui/process/nofile-limit.rs",
- ]
- }
-}
diff --git a/src/tools/opt-dist/src/environment/mod.rs b/src/tools/opt-dist/src/environment/mod.rs
deleted file mode 100644
index a8650fa..0000000
--- a/src/tools/opt-dist/src/environment/mod.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use camino::Utf8PathBuf;
-
-#[cfg(target_family = "unix")]
-mod linux;
-#[cfg(target_family = "windows")]
-mod windows;
-
-pub trait Environment {
- fn host_triple(&self) -> String {
- std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing")
- }
-
- fn python_binary(&self) -> &'static str;
-
- /// The rustc checkout, where the compiler source is located.
- fn checkout_path(&self) -> Utf8PathBuf;
-
- /// Path to the host LLVM used to compile LLVM in `src/llvm-project`.
- fn host_llvm_dir(&self) -> Utf8PathBuf;
-
- /// Directory where the optimization artifacts (PGO/BOLT profiles, etc.)
- /// will be stored.
- fn opt_artifacts(&self) -> Utf8PathBuf;
-
- /// The main directory where the build occurs.
- fn build_root(&self) -> Utf8PathBuf;
-
- fn build_artifacts(&self) -> Utf8PathBuf {
- self.build_root().join("build").join(self.host_triple())
- }
-
- fn cargo_stage_0(&self) -> Utf8PathBuf {
- self.build_artifacts()
- .join("stage0")
- .join("bin")
- .join(format!("cargo{}", self.executable_extension()))
- }
-
- fn rustc_stage_0(&self) -> Utf8PathBuf {
- self.build_artifacts()
- .join("stage0")
- .join("bin")
- .join(format!("rustc{}", self.executable_extension()))
- }
-
- fn rustc_stage_2(&self) -> Utf8PathBuf {
- self.build_artifacts()
- .join("stage2")
- .join("bin")
- .join(format!("rustc{}", self.executable_extension()))
- }
-
- /// Path to the built rustc-perf benchmark suite.
- fn rustc_perf_dir(&self) -> Utf8PathBuf {
- self.opt_artifacts().join("rustc-perf")
- }
-
- /// Download and/or compile rustc-perf.
- fn prepare_rustc_perf(&self) -> anyhow::Result<()>;
-
- fn supports_bolt(&self) -> bool;
-
- fn supports_shared_llvm(&self) -> bool;
-
- /// What is the extension of binary executables in this environment?
- fn executable_extension(&self) -> &'static str;
-
- /// List of test paths that should be skipped when testing the optimized artifacts.
- fn skipped_tests(&self) -> &'static [&'static str];
-}
-
-pub fn create_environment() -> Box<dyn Environment> {
- #[cfg(target_family = "unix")]
- return Box::new(linux::LinuxEnvironment);
- #[cfg(target_family = "windows")]
- return Box::new(windows::WindowsEnvironment::new());
-}
diff --git a/src/tools/opt-dist/src/environment/windows.rs b/src/tools/opt-dist/src/environment/windows.rs
deleted file mode 100644
index 7939939..0000000
--- a/src/tools/opt-dist/src/environment/windows.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use crate::environment::Environment;
-use crate::exec::cmd;
-use crate::utils::io::move_directory;
-use crate::utils::retry_action;
-use camino::Utf8PathBuf;
-use std::io::Cursor;
-use std::time::Duration;
-use zip::ZipArchive;
-
-pub(super) struct WindowsEnvironment {
- checkout_dir: Utf8PathBuf,
-}
-
-impl WindowsEnvironment {
- pub fn new() -> Self {
- Self { checkout_dir: std::env::current_dir().unwrap().try_into().unwrap() }
- }
-}
-
-impl Environment for WindowsEnvironment {
- fn python_binary(&self) -> &'static str {
- "python"
- }
-
- fn checkout_path(&self) -> Utf8PathBuf {
- self.checkout_dir.clone()
- }
-
- fn host_llvm_dir(&self) -> Utf8PathBuf {
- self.checkout_path().join("citools").join("clang-rust")
- }
-
- fn opt_artifacts(&self) -> Utf8PathBuf {
- self.checkout_path().join("opt-artifacts")
- }
-
- fn build_root(&self) -> Utf8PathBuf {
- self.checkout_path()
- }
-
- fn prepare_rustc_perf(&self) -> anyhow::Result<()> {
- // FIXME: add some mechanism for synchronization of this commit SHA with
- // Linux (which builds rustc-perf in a Dockerfile)
- // rustc-perf version from 2023-05-30
- const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1";
-
- let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip");
- let client = reqwest::blocking::Client::builder()
- .timeout(Duration::from_secs(60 * 2))
- .connect_timeout(Duration::from_secs(60 * 2))
- .build()?;
- let response = retry_action(
- || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()),
- "Download rustc-perf archive",
- 5,
- )?;
-
- let mut archive = ZipArchive::new(Cursor::new(response))?;
- archive.extract(self.rustc_perf_dir())?;
- move_directory(
- &self.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")),
- &self.rustc_perf_dir(),
- )?;
-
- cmd(&[self.cargo_stage_0().as_str(), "build", "-p", "collector"])
- .workdir(&self.rustc_perf_dir())
- .env("RUSTC", &self.rustc_stage_0().into_string())
- .env("RUSTC_BOOTSTRAP", "1")
- .run()?;
-
- Ok(())
- }
-
- fn supports_bolt(&self) -> bool {
- false
- }
-
- fn supports_shared_llvm(&self) -> bool {
- false
- }
-
- fn executable_extension(&self) -> &'static str {
- ".exe"
- }
-
- fn skipped_tests(&self) -> &'static [&'static str] {
- &[
- // Fails as of June 2023.
- "tests\\codegen\\vec-shrink-panik.rs",
- ]
- }
-}
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index 4765dce..04e0184 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -96,7 +96,7 @@
}
impl Bootstrap {
- pub fn build(env: &dyn Environment) -> Self {
+ pub fn build(env: &Environment) -> Self {
let metrics_path = env.build_root().join("build").join("metrics.json");
let cmd = cmd(&[
env.python_binary(),
@@ -114,7 +114,7 @@
Self { cmd, metrics_path }
}
- pub fn dist(env: &dyn Environment, dist_args: &[String]) -> Self {
+ pub fn dist(env: &Environment, dist_args: &[String]) -> Self {
let metrics_path = env.build_root().join("build").join("metrics.json");
let cmd = cmd(&dist_args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>())
.env("RUST_BACKTRACE", "full");
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 8ab1967..978e2df 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -1,17 +1,22 @@
use crate::bolt::{bolt_optimize, with_bolt_instrumented};
use anyhow::Context;
+use camino::{Utf8Path, Utf8PathBuf};
+use clap::Parser;
use log::LevelFilter;
+use std::io::Cursor;
+use std::time::Duration;
use utils::io;
+use zip::ZipArchive;
-use crate::environment::{create_environment, Environment};
-use crate::exec::Bootstrap;
+use crate::environment::{Environment, EnvironmentBuilder};
+use crate::exec::{cmd, Bootstrap};
use crate::tests::run_tests;
use crate::timer::Timer;
use crate::training::{gather_llvm_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles};
-use crate::utils::io::reset_directory;
+use crate::utils::io::{copy_directory, move_directory, reset_directory};
use crate::utils::{
clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space,
- with_log_group,
+ retry_action, with_log_group,
};
mod bolt;
@@ -23,24 +28,169 @@
mod training;
mod utils;
+#[derive(clap::Parser, Debug)]
+struct Args {
+ #[clap(subcommand)]
+ env: EnvironmentCmd,
+}
+
+#[derive(clap::Parser, Clone, Debug)]
+struct SharedArgs {
+ // Arguments passed to `x` to perform the final (dist) build.
+ build_args: Vec<String>,
+}
+
+#[derive(clap::Parser, Clone, Debug)]
+enum EnvironmentCmd {
+ /// Perform a custom local PGO/BOLT optimized build.
+ Local {
+ /// Target triple of the host.
+ #[arg(long)]
+ target_triple: String,
+
+ /// Checkout directory of `rustc`.
+ #[arg(long)]
+ checkout_dir: Utf8PathBuf,
+
+ /// Host LLVM installation directory.
+ #[arg(long)]
+ llvm_dir: Utf8PathBuf,
+
+ /// Python binary to use in bootstrap invocations.
+ #[arg(long, default_value = "python3")]
+ python: String,
+
+ /// Directory where artifacts (like PGO profiles or rustc-perf) of this workflow
+ /// will be stored.
+ #[arg(long, default_value = "opt-artifacts")]
+ artifact_dir: Utf8PathBuf,
+
+ /// Is LLVM for `rustc` built in shared library mode?
+ #[arg(long, default_value_t = true)]
+ llvm_shared: bool,
+
+ /// Should BOLT optimization be used? If yes, host LLVM must have BOLT binaries
+ /// (`llvm-bolt` and `merge-fdata`) available.
+ #[arg(long, default_value_t = false)]
+ use_bolt: bool,
+
+ /// Tests that should be skipped when testing the optimized compiler.
+ #[arg(long)]
+ skipped_tests: Vec<String>,
+
+ #[clap(flatten)]
+ shared: SharedArgs,
+ },
+ /// Perform an optimized build on Linux CI, from inside Docker.
+ LinuxCi {
+ #[clap(flatten)]
+ shared: SharedArgs,
+ },
+ /// Perform an optimized build on Windows CI, directly inside Github Actions.
+ WindowsCi {
+ #[clap(flatten)]
+ shared: SharedArgs,
+ },
+}
+
fn is_try_build() -> bool {
std::env::var("DIST_TRY_BUILD").unwrap_or_else(|_| "0".to_string()) != "0"
}
+fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)> {
+ let (env, args) = match args.env {
+ EnvironmentCmd::Local {
+ target_triple,
+ checkout_dir,
+ llvm_dir,
+ python,
+ artifact_dir,
+ llvm_shared,
+ use_bolt,
+ skipped_tests,
+ shared,
+ } => {
+ let env = EnvironmentBuilder::default()
+ .host_triple(target_triple)
+ .python_binary(python)
+ .checkout_dir(checkout_dir.clone())
+ .host_llvm_dir(llvm_dir)
+ .artifact_dir(artifact_dir)
+ .build_dir(checkout_dir)
+ .shared_llvm(llvm_shared)
+ .use_bolt(use_bolt)
+ .skipped_tests(skipped_tests)
+ .build()?;
+
+ (env, shared.build_args)
+ }
+ EnvironmentCmd::LinuxCi { shared } => {
+ let target_triple =
+ std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing");
+
+ let checkout_dir = Utf8PathBuf::from("/checkout");
+ let env = EnvironmentBuilder::default()
+ .host_triple(target_triple)
+ .python_binary("python3".to_string())
+ .checkout_dir(checkout_dir.clone())
+ .host_llvm_dir(Utf8PathBuf::from("/rustroot"))
+ .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts"))
+ .build_dir(checkout_dir.join("obj"))
+ // /tmp/rustc-perf comes from the x64 dist Dockerfile
+ .prebuilt_rustc_perf(Some(Utf8PathBuf::from("/tmp/rustc-perf")))
+ .shared_llvm(true)
+ .use_bolt(true)
+ .skipped_tests(vec![
+ // Fails because of linker errors, as of June 2023.
+ "tests/ui/process/nofile-limit.rs".to_string(),
+ ])
+ .build()?;
+
+ (env, shared.build_args)
+ }
+ EnvironmentCmd::WindowsCi { shared } => {
+ let target_triple =
+ std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing");
+
+ let checkout_dir: Utf8PathBuf = std::env::current_dir()?.try_into()?;
+ let env = EnvironmentBuilder::default()
+ .host_triple(target_triple)
+ .python_binary("python".to_string())
+ .checkout_dir(checkout_dir.clone())
+ .host_llvm_dir(checkout_dir.join("citools").join("clang-rust"))
+ .artifact_dir(checkout_dir.join("opt-artifacts"))
+ .build_dir(checkout_dir)
+ .shared_llvm(false)
+ .use_bolt(false)
+ .skipped_tests(vec![
+ // Fails as of June 2023.
+ "tests\\codegen\\vec-shrink-panik.rs".to_string(),
+ ])
+ .build()?;
+
+ (env, shared.build_args)
+ }
+ };
+ Ok((env, args))
+}
+
fn execute_pipeline(
- env: &dyn Environment,
+ env: &Environment,
timer: &mut Timer,
dist_args: Vec<String>,
) -> anyhow::Result<()> {
- reset_directory(&env.opt_artifacts())?;
+ reset_directory(&env.artifact_dir())?;
- with_log_group("Building rustc-perf", || env.prepare_rustc_perf())?;
+ with_log_group("Building rustc-perf", || match env.prebuilt_rustc_perf() {
+ Some(dir) => copy_rustc_perf(env, &dir),
+ None => download_rustc_perf(env),
+ })?;
// Stage 1: Build PGO instrumented rustc
// We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the
// same time can cause issues, because the host and in-tree LLVM versions can diverge.
let rustc_pgo_profile = timer.section("Stage 1 (Rustc PGO)", |stage| {
- let rustc_profile_dir_root = env.opt_artifacts().join("rustc-pgo");
+ let rustc_profile_dir_root = env.artifact_dir().join("rustc-pgo");
stage.section("Build PGO instrumented rustc and LLVM", |section| {
let mut builder = Bootstrap::build(env).rustc_pgo_instrument(&rustc_profile_dir_root);
@@ -74,7 +224,7 @@
// Remove the previous, uninstrumented build of LLVM.
clear_llvm_files(env)?;
- let llvm_profile_dir_root = env.opt_artifacts().join("llvm-pgo");
+ let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo");
stage.section("Build PGO instrumented LLVM", |section| {
Bootstrap::build(env)
@@ -95,7 +245,7 @@
Ok(profile)
})?;
- let llvm_bolt_profile = if env.supports_bolt() {
+ let llvm_bolt_profile = if env.use_bolt() {
// Stage 3: Build BOLT instrumented LLVM
// We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles.
// Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
@@ -171,8 +321,9 @@
.parse_default_env()
.init();
- let mut build_args: Vec<String> = std::env::args().skip(1).collect();
- println!("Running optimized build pipeline with args `{}`", build_args.join(" "));
+ let args = Args::parse();
+
+ println!("Running optimized build pipeline with args `{:?}`", args);
with_log_group("Environment values", || {
println!("Environment values\n{}", format_env_variables());
@@ -184,6 +335,8 @@
}
});
+ let (env, mut build_args) = create_environment(args).context("Cannot create environment")?;
+
// Skip components that are not needed for try builds to speed them up
if is_try_build() {
log::info!("Skipping building of unimportant components for a try build");
@@ -202,14 +355,58 @@
}
let mut timer = Timer::new();
- let env = create_environment();
- let result = execute_pipeline(env.as_ref(), &mut timer, build_args);
+ let result = execute_pipeline(&env, &mut timer, build_args);
log::info!("Timer results\n{}", timer.format_stats());
print_free_disk_space()?;
result.context("Optimized build pipeline has failed")?;
- print_binary_sizes(env.as_ref())?;
+ print_binary_sizes(&env)?;
Ok(())
}
+
+// Copy rustc-perf from the given path into the environment and build it.
+fn copy_rustc_perf(env: &Environment, dir: &Utf8Path) -> anyhow::Result<()> {
+ copy_directory(dir, &env.rustc_perf_dir())?;
+ build_rustc_perf(env)
+}
+
+// Download and build rustc-perf into the given environment.
+fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> {
+ reset_directory(&env.rustc_perf_dir())?;
+
+ // FIXME: add some mechanism for synchronization of this commit SHA with
+ // Linux (which builds rustc-perf in a Dockerfile)
+ // rustc-perf version from 2023-05-30
+ const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1";
+
+ let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip");
+ let client = reqwest::blocking::Client::builder()
+ .timeout(Duration::from_secs(60 * 2))
+ .connect_timeout(Duration::from_secs(60 * 2))
+ .build()?;
+ let response = retry_action(
+ || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()),
+ "Download rustc-perf archive",
+ 5,
+ )?;
+
+ let mut archive = ZipArchive::new(Cursor::new(response))?;
+ archive.extract(env.rustc_perf_dir())?;
+ move_directory(
+ &env.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")),
+ &env.rustc_perf_dir(),
+ )?;
+
+ build_rustc_perf(env)
+}
+
+fn build_rustc_perf(env: &Environment) -> anyhow::Result<()> {
+ cmd(&[env.cargo_stage_0().as_str(), "build", "-p", "collector"])
+ .workdir(&env.rustc_perf_dir())
+ .env("RUSTC", &env.rustc_stage_0().into_string())
+ .env("RUSTC_BOOTSTRAP", "1")
+ .run()?;
+ Ok(())
+}
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index 3dd1a32..31aabca 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -1,11 +1,11 @@
-use crate::environment::Environment;
+use crate::environment::{executable_extension, Environment};
use crate::exec::cmd;
use crate::utils::io::{copy_directory, find_file_in_dir, unpack_archive};
use anyhow::Context;
use camino::{Utf8Path, Utf8PathBuf};
/// Run tests on optimized dist artifacts.
-pub fn run_tests(env: &dyn Environment) -> anyhow::Result<()> {
+pub fn run_tests(env: &Environment) -> anyhow::Result<()> {
// After `dist` is executed, we extract its archived components into a sysroot directory,
// and then use that extracted rustc as a stage0 compiler.
// Then we run a subset of tests using that compiler, to have a basic smoke test which checks
@@ -33,8 +33,8 @@
// We need to manually copy libstd to the extracted rustc sysroot
copy_directory(
- &libstd_dir.join("lib").join("rustlib").join(&host_triple).join("lib"),
- &rustc_dir.join("lib").join("rustlib").join(&host_triple).join("lib"),
+ &libstd_dir.join("lib").join("rustlib").join(host_triple).join("lib"),
+ &rustc_dir.join("lib").join("rustlib").join(host_triple).join("lib"),
)?;
// Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd
@@ -45,9 +45,9 @@
&rustc_dir.join("lib").join("rustlib").join("src"),
)?;
- let rustc_path = rustc_dir.join("bin").join(format!("rustc{}", env.executable_extension()));
+ let rustc_path = rustc_dir.join("bin").join(format!("rustc{}", executable_extension()));
assert!(rustc_path.is_file());
- let cargo_path = cargo_dir.join("bin").join(format!("cargo{}", env.executable_extension()));
+ let cargo_path = cargo_dir.join("bin").join(format!("cargo{}", executable_extension()));
assert!(cargo_path.is_file());
// Specify path to a LLVM config so that LLVM is not rebuilt.
@@ -56,7 +56,7 @@
.build_artifacts()
.join("llvm")
.join("bin")
- .join(format!("llvm-config{}", env.executable_extension()));
+ .join(format!("llvm-config{}", executable_extension()));
assert!(llvm_config.is_file());
let config_content = format!(
@@ -109,6 +109,6 @@
.unwrap()
.to_string();
let (version, _) =
- archive.strip_prefix("reproducible-artifacts-").unwrap().split_once("-").unwrap();
+ archive.strip_prefix("reproducible-artifacts-").unwrap().split_once('-').unwrap();
Ok(version.to_string())
}
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index 59c73fb..274f4ce 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -1,4 +1,4 @@
-use crate::environment::Environment;
+use crate::environment::{executable_extension, Environment};
use crate::exec::{cmd, CmdBuilder};
use crate::utils::io::{count_files, delete_directory};
use crate::utils::with_log_group;
@@ -30,7 +30,7 @@
const LLVM_BOLT_CRATES: &[&str] = LLVM_PGO_CRATES;
fn init_compiler_benchmarks(
- env: &dyn Environment,
+ env: &Environment,
profiles: &[&str],
scenarios: &[&str],
crates: &[&str],
@@ -75,7 +75,7 @@
}
fn merge_llvm_profiles(
- env: &dyn Environment,
+ env: &Environment,
merged_path: &Utf8Path,
profile_dir: &Utf8Path,
profdata: LlvmProfdata,
@@ -86,7 +86,7 @@
.build_artifacts()
.join("llvm")
.join("build")
- .join(format!("bin/llvm-profdata{}", env.executable_extension())),
+ .join(format!("bin/llvm-profdata{}", executable_extension())),
};
cmd(&[llvm_profdata.as_str(), "merge", "-o", merged_path.as_str(), profile_dir.as_str()])
@@ -116,7 +116,7 @@
pub struct LlvmPGOProfile(pub Utf8PathBuf);
pub fn gather_llvm_profiles(
- env: &dyn Environment,
+ env: &Environment,
profile_root: &Utf8Path,
) -> anyhow::Result<LlvmPGOProfile> {
log::info!("Running benchmarks with PGO instrumented LLVM");
@@ -127,7 +127,7 @@
.context("Cannot gather LLVM PGO profiles")
})?;
- let merged_profile = env.opt_artifacts().join("llvm-pgo.profdata");
+ let merged_profile = env.artifact_dir().join("llvm-pgo.profdata");
log::info!("Merging LLVM PGO profiles to {merged_profile}");
merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Host)?;
@@ -143,7 +143,7 @@
pub struct RustcPGOProfile(pub Utf8PathBuf);
pub fn gather_rustc_profiles(
- env: &dyn Environment,
+ env: &Environment,
profile_root: &Utf8Path,
) -> anyhow::Result<RustcPGOProfile> {
log::info!("Running benchmarks with PGO instrumented rustc");
@@ -163,7 +163,7 @@
.context("Cannot gather rustc PGO profiles")
})?;
- let merged_profile = env.opt_artifacts().join("rustc-pgo.profdata");
+ let merged_profile = env.artifact_dir().join("rustc-pgo.profdata");
log::info!("Merging Rustc PGO profiles to {merged_profile}");
merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Target)?;
@@ -178,7 +178,7 @@
pub struct LlvmBoltProfile(pub Utf8PathBuf);
-pub fn gather_llvm_bolt_profiles(env: &dyn Environment) -> anyhow::Result<LlvmBoltProfile> {
+pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltProfile> {
log::info!("Running benchmarks with BOLT instrumented LLVM");
with_log_group("Running benchmarks", || {
@@ -187,12 +187,12 @@
.context("Cannot gather LLVM BOLT profiles")
})?;
- let merged_profile = env.opt_artifacts().join("llvm-bolt.profdata");
+ let merged_profile = env.artifact_dir().join("llvm-bolt.profdata");
let profile_root = Utf8PathBuf::from("/tmp/prof.fdata");
log::info!("Merging LLVM BOLT profiles to {merged_profile}");
let profiles: Vec<_> =
- glob::glob(&format!("{profile_root}*"))?.into_iter().collect::<Result<Vec<_>, _>>()?;
+ glob::glob(&format!("{profile_root}*"))?.collect::<Result<Vec<_>, _>>()?;
let mut merge_args = vec!["merge-fdata"];
merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
diff --git a/src/tools/opt-dist/src/utils/io.rs b/src/tools/opt-dist/src/utils/io.rs
index 8bd516f..d6bd5cb 100644
--- a/src/tools/opt-dist/src/utils/io.rs
+++ b/src/tools/opt-dist/src/utils/io.rs
@@ -7,7 +7,7 @@
/// Delete and re-create the directory.
pub fn reset_directory(path: &Utf8Path) -> anyhow::Result<()> {
log::info!("Resetting directory {path}");
- let _ = std::fs::remove_dir(path);
+ let _ = std::fs::remove_dir_all(path);
std::fs::create_dir_all(path)?;
Ok(())
}
@@ -63,7 +63,6 @@
let path = format!("{dir}/*{}", suffix.unwrap_or(""));
Ok(glob::glob(&path)?
- .into_iter()
.map(|p| p.map(|p| Utf8PathBuf::from_path_buf(p).unwrap()))
.collect::<Result<Vec<_>, _>>()?)
}
@@ -74,9 +73,8 @@
prefix: &str,
suffix: &str,
) -> anyhow::Result<Utf8PathBuf> {
- let files = glob::glob(&format!("{directory}/{prefix}*{suffix}"))?
- .into_iter()
- .collect::<Result<Vec<_>, _>>()?;
+ let files =
+ glob::glob(&format!("{directory}/{prefix}*{suffix}"))?.collect::<Result<Vec<_>, _>>()?;
match files.len() {
0 => Err(anyhow::anyhow!("No file with prefix {prefix} found in {directory}")),
1 => Ok(Utf8PathBuf::from_path_buf(files[0].clone()).unwrap()),
diff --git a/src/tools/opt-dist/src/utils/mod.rs b/src/tools/opt-dist/src/utils/mod.rs
index 2af2807..6fc9659 100644
--- a/src/tools/opt-dist/src/utils/mod.rs
+++ b/src/tools/opt-dist/src/utils/mod.rs
@@ -26,7 +26,7 @@
Ok(())
}
-pub fn print_binary_sizes(env: &dyn Environment) -> anyhow::Result<()> {
+pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> {
use std::fmt::Write;
let root = env.build_artifacts().join("stage2");
@@ -48,7 +48,7 @@
Ok(())
}
-pub fn clear_llvm_files(env: &dyn Environment) -> anyhow::Result<()> {
+pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> {
// Bootstrap currently doesn't support rebuilding LLVM when PGO options
// change (or any other llvm-related options); so just clear out the relevant
// directories ourselves.
diff --git a/src/tools/rust-analyzer/.cargo/config.toml b/src/tools/rust-analyzer/.cargo/config.toml
index 24745d1..c9ad780 100644
--- a/src/tools/rust-analyzer/.cargo/config.toml
+++ b/src/tools/rust-analyzer/.cargo/config.toml
@@ -8,4 +8,4 @@
linker = "rust-lld"
[env]
-CARGO_WORKSPACE_DIR = { value = "", relative = true }
\ No newline at end of file
+CARGO_WORKSPACE_DIR = { value = "", relative = true }
diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml
index 9f24609..fb7b4b0 100644
--- a/src/tools/rust-analyzer/.github/workflows/ci.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml
@@ -86,12 +86,20 @@
- name: Test
run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
+ - name: Switch to stable toolchain
+ run: |
+ rustup update --no-self-update stable
+ rustup component add --toolchain stable rust-src
+ rustup default stable
+
- name: Run analysis-stats on rust-analyzer
if: matrix.os == 'ubuntu-latest'
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats .
- name: Run analysis-stats on rust std library
if: matrix.os == 'ubuntu-latest'
+ env:
+ RUSTC_BOOTSTRAP: 1
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
# Weird targets to catch non-portable code
diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
index 260e45f..bbeccd1 100644
--- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
@@ -67,7 +67,7 @@
other_metrics:
strategy:
matrix:
- names: [self, ripgrep, webrender, diesel]
+ names: [self, ripgrep-13.0.0, webrender-2022, diesel-1.4.8, hyper-0.14.18]
runs-on: ubuntu-latest
needs: [setup_cargo, build_metrics]
@@ -92,7 +92,7 @@
key: ${{ runner.os }}-target-${{ github.sha }}
- name: Collect metrics
- run: cargo xtask metrics ${{ matrix.names }}
+ run: cargo xtask metrics "${{ matrix.names }}"
- name: Upload metrics
uses: actions/upload-artifact@v3
@@ -118,25 +118,30 @@
with:
name: self-${{ github.sha }}
- - name: Download ripgrep metrics
+ - name: Download ripgrep-13.0.0 metrics
uses: actions/download-artifact@v3
with:
- name: ripgrep-${{ github.sha }}
+ name: ripgrep-13.0.0-${{ github.sha }}
- - name: Download webrender metrics
+ - name: Download webrender-2022 metrics
uses: actions/download-artifact@v3
with:
- name: webrender-${{ github.sha }}
+ name: webrender-2022-${{ github.sha }}
- - name: Download diesel metrics
+ - name: Download diesel-1.4.8 metrics
uses: actions/download-artifact@v3
with:
- name: diesel-${{ github.sha }}
+ name: diesel-1.4.8-${{ github.sha }}
+
+ - name: Download hyper-0.14.18 metrics
+ uses: actions/download-artifact@v3
+ with:
+ name: hyper-0.14.18-${{ github.sha }}
- name: Combine json
run: |
git clone --depth 1 https://$METRICS_TOKEN@github.com/rust-analyzer/metrics.git
- jq -s ".[0] * .[1] * .[2] * .[3] * .[4]" build.json self.json ripgrep.json webrender.json diesel.json -c >> metrics/metrics.json
+ jq -s ".[0] * .[1] * .[2] * .[3] * .[4] * .[5]" build.json self.json ripgrep-13.0.0.json webrender-2022.json diesel-1.4.8.json hyper-0.14.18.json -c >> metrics/metrics.json
cd metrics
git add .
git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈
diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml
index 43681c7..b5dbe30 100644
--- a/src/tools/rust-analyzer/.github/workflows/release.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/release.yaml
@@ -128,6 +128,8 @@
- name: Run analysis-stats on rust std library
if: matrix.target == 'x86_64-unknown-linux-gnu'
+ env:
+ RUSTC_BOOTSTRAP: 1
run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
- name: Upload artifacts
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 49e9623..bd6554b 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -381,14 +381,14 @@
[[package]]
name = "filetime"
-version = "0.2.19"
+version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
- "windows-sys 0.42.0",
+ "redox_syscall 0.3.5",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -541,6 +541,7 @@
"mbe",
"once_cell",
"profile",
+ "ra-ap-rustc_parse_format",
"rustc-hash",
"smallvec",
"stdx",
@@ -854,7 +855,6 @@
dependencies = [
"dashmap",
"hashbrown 0.12.3",
- "once_cell",
"rustc-hash",
"triomphe",
]
@@ -999,7 +999,7 @@
[[package]]
name = "lsp-server"
-version = "0.7.3"
+version = "0.7.4"
dependencies = [
"crossbeam-channel",
"log",
@@ -1010,9 +1010,9 @@
[[package]]
name = "lsp-server"
-version = "0.7.3"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2"
+checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
dependencies = [
"crossbeam-channel",
"log",
@@ -1149,20 +1149,21 @@
[[package]]
name = "notify"
-version = "5.1.0"
+version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
+checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.3.2",
"crossbeam-channel",
"filetime",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
+ "log",
"mio",
"walkdir",
- "windows-sys 0.42.0",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1251,7 +1252,7 @@
"cfg-if",
"instant",
"libc",
- "redox_syscall",
+ "redox_syscall 0.2.16",
"smallvec",
"winapi",
]
@@ -1264,7 +1265,7 @@
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.2.16",
"smallvec",
"windows-sys 0.42.0",
]
@@ -1482,16 +1483,36 @@
]
[[package]]
-name = "ra-ap-rustc_lexer"
-version = "0.1.0"
+name = "ra-ap-rustc_index"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3"
+checksum = "07b5fa61d34da18e148dc3a81f654488ea07f40938d8aefb17f8b64bb78c6120"
dependencies = [
- "unic-emoji-char",
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
+name = "ra-ap-rustc_lexer"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2e2f6b48422e4eed5218277ab7cc9733e60dd8f3167f4f36a49a0cafe4dc195"
+dependencies = [
+ "unicode-properties",
"unicode-xid",
]
[[package]]
+name = "ra-ap-rustc_parse_format"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3c7369ad01cc79f9e3513c9f6a6326f6b980100e4862a7ac71b9991c88108bb"
+dependencies = [
+ "ra-ap-rustc_index",
+ "ra-ap-rustc_lexer",
+]
+
+[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1523,6 +1544,15 @@
]
[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
name = "rowan"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1545,7 +1575,6 @@
"crossbeam-channel",
"dissimilar",
"expect-test",
- "filetime",
"flycheck",
"hir",
"hir-def",
@@ -1555,7 +1584,7 @@
"ide-ssr",
"itertools",
"load-cargo",
- "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lsp-types",
"mbe",
"mimalloc",
@@ -2057,47 +2086,6 @@
checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"
[[package]]
-name = "unic-char-property"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
-dependencies = [
- "unic-char-range",
-]
-
-[[package]]
-name = "unic-char-range"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
-
-[[package]]
-name = "unic-common"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
-
-[[package]]
-name = "unic-emoji-char"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d"
-dependencies = [
- "unic-char-property",
- "unic-char-range",
- "unic-ucd-version",
-]
-
-[[package]]
-name = "unic-ucd-version"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
-dependencies = [
- "unic-common",
-]
-
-[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2128,6 +2116,12 @@
]
[[package]]
+name = "unicode-properties"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0"
+
+[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 5eb59d6..cab88fc 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -86,7 +86,7 @@
# In-tree crates that are published separately and follow semver. See lib/README.md
line-index = { version = "0.1.0-pre.1" }
la-arena = { version = "0.3.1" }
-lsp-server = { version = "0.7.3" }
+lsp-server = { version = "0.7.4" }
# non-local crates
smallvec = { version = "1.10.0", features = [
@@ -97,11 +97,15 @@
smol_str = "0.2.0"
nohash-hasher = "0.2.0"
text-size = "1.1.0"
-# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
-serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
+serde = { version = "1.0.156", features = ["derive"] }
serde_json = "1.0.96"
triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
# can't upgrade due to dashmap depending on 0.12.3 currently
hashbrown = { version = "0.12.3", features = ["inline-more"], default-features = false }
-rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
+rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" }
+rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false }
+
+# Upstream broke this for us so we can't update it
+rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
+rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index aaac0fc..3f5ccb6 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -179,8 +179,8 @@
meta.edition,
Some(crate_name.clone().into()),
version,
- meta.cfg,
- Default::default(),
+ meta.cfg.clone(),
+ Some(meta.cfg),
meta.env,
false,
origin,
@@ -200,7 +200,7 @@
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
- default_cfg = meta.cfg;
+ default_cfg.extend(meta.cfg.into_iter());
default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout;
}
@@ -220,8 +220,8 @@
Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()),
None,
- default_cfg,
- Default::default(),
+ default_cfg.clone(),
+ Some(default_cfg),
default_env,
false,
CrateOrigin::Local { repo: None, name: None },
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 183b9b7..0aeb0b0 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -86,6 +86,32 @@
}
}
+impl Extend<CfgAtom> for CfgOptions {
+ fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
+ iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag));
+ }
+}
+
+impl IntoIterator for CfgOptions {
+ type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
+
+ type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
+ }
+}
+
+impl<'a> IntoIterator for &'a CfgOptions {
+ type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
+
+ type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
+ }
+}
+
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index fbb943c..2de719a 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -5,7 +5,9 @@
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
use std::{
+ ffi::OsString,
fmt, io,
+ path::PathBuf,
process::{ChildStderr, ChildStdout, Command, Stdio},
time::Duration,
};
@@ -168,7 +170,7 @@
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
- cargo_handle: Option<CargoHandle>,
+ command_handle: Option<CommandHandle>,
}
enum Event {
@@ -184,7 +186,7 @@
workspace_root: AbsPathBuf,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
- FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
+ FlycheckActor { id, sender, config, root: workspace_root, command_handle: None }
}
fn report_progress(&self, progress: Progress) {
@@ -192,7 +194,7 @@
}
fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
- let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
+ let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
if let Ok(msg) = inbox.try_recv() {
// give restarts a preference so check outputs don't block a restart or stop
return Some(Event::RequestStateChange(msg));
@@ -221,21 +223,19 @@
}
let command = self.check_command();
+ let formatted_command = format!("{:?}", command);
+
tracing::debug!(?command, "will restart flycheck");
- match CargoHandle::spawn(command) {
- Ok(cargo_handle) => {
- tracing::debug!(
- command = ?self.check_command(),
- "did restart flycheck"
- );
- self.cargo_handle = Some(cargo_handle);
+ match CommandHandle::spawn(command) {
+ Ok(command_handle) => {
+ tracing::debug!(command = formatted_command, "did restart flycheck");
+ self.command_handle = Some(command_handle);
self.report_progress(Progress::DidStart);
}
Err(error) => {
self.report_progress(Progress::DidFailToRestart(format!(
- "Failed to run the following command: {:?} error={}",
- self.check_command(),
- error
+ "Failed to run the following command: {} error={}",
+ formatted_command, error
)));
}
}
@@ -244,12 +244,14 @@
tracing::debug!(flycheck_id = self.id, "flycheck finished");
// Watcher finished
- let cargo_handle = self.cargo_handle.take().unwrap();
- let res = cargo_handle.join();
+ let command_handle = self.command_handle.take().unwrap();
+ let formatted_handle = format!("{:?}", command_handle);
+
+ let res = command_handle.join();
if res.is_err() {
tracing::error!(
- "Flycheck failed to run the following command: {:?}",
- self.check_command()
+ "Flycheck failed to run the following command: {}",
+ formatted_handle
);
}
self.report_progress(Progress::DidFinish(res));
@@ -284,12 +286,12 @@
}
fn cancel_check_process(&mut self) {
- if let Some(cargo_handle) = self.cargo_handle.take() {
+ if let Some(command_handle) = self.command_handle.take() {
tracing::debug!(
- command = ?self.check_command(),
+ command = ?command_handle,
"did cancel flycheck"
);
- cargo_handle.cancel();
+ command_handle.cancel();
self.report_progress(Progress::DidCancel);
}
}
@@ -391,19 +393,36 @@
}
/// A handle to a cargo process used for fly-checking.
-struct CargoHandle {
+struct CommandHandle {
/// The handle to the actual cargo process. As we cannot cancel directly from with
/// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild,
thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>,
+ program: OsString,
+ arguments: Vec<OsString>,
+ current_dir: Option<PathBuf>,
}
-impl CargoHandle {
- fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
+impl fmt::Debug for CommandHandle {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CommandHandle")
+ .field("program", &self.program)
+ .field("arguments", &self.arguments)
+ .field("current_dir", &self.current_dir)
+ .finish()
+ }
+}
+
+impl CommandHandle {
+ fn spawn(mut command: Command) -> std::io::Result<CommandHandle> {
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
let mut child = command.group_spawn().map(JodGroupChild)?;
+ let program = command.get_program().into();
+ let arguments = command.get_args().map(|arg| arg.into()).collect::<Vec<OsString>>();
+ let current_dir = command.get_current_dir().map(|arg| arg.to_path_buf());
+
let stdout = child.0.inner().stdout.take().unwrap();
let stderr = child.0.inner().stderr.take().unwrap();
@@ -413,7 +432,7 @@
.name("CargoHandle".to_owned())
.spawn(move || actor.run())
.expect("failed to spawn thread");
- Ok(CargoHandle { child, thread, receiver })
+ Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
}
fn cancel(mut self) {
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 30307de..8cf61ee 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -31,8 +31,10 @@
hashbrown.workspace = true
triomphe.workspace = true
-rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
-rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
+rustc_abi.workspace = true
+rustc_index.workspace = true
+rustc_parse_format.workspace = true
+
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index a5db75a..c6454eb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -5,7 +5,7 @@
#[cfg(test)]
mod tests;
-use std::{hash::Hash, ops};
+use std::{hash::Hash, ops, slice::Iter as SliceIter};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -14,12 +14,11 @@
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
HirFileId, InFile,
};
-use itertools::Itertools;
use la_arena::{ArenaMap, Idx, RawIdx};
use mbe::DelimiterKind;
use syntax::{
- ast::{self, HasAttrs, IsString},
- AstPtr, AstToken, SmolStr, TextRange, TextSize,
+ ast::{self, HasAttrs},
+ AstPtr, SmolStr,
};
use triomphe::Arc;
@@ -33,26 +32,6 @@
LocalFieldId, Lookup, MacroId, VariantId,
};
-/// Holds documentation
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Documentation(String);
-
-impl Documentation {
- pub fn new(s: String) -> Self {
- Documentation(s)
- }
-
- pub fn as_str(&self) -> &str {
- &self.0
- }
-}
-
-impl From<Documentation> for String {
- fn from(Documentation(string): Documentation) -> Self {
- string
- }
-}
-
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Attrs(RawAttrs);
@@ -221,33 +200,6 @@
self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
}
- pub fn docs(&self) -> Option<Documentation> {
- let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
- let indent = doc_indent(self);
- let mut buf = String::new();
- for doc in docs {
- // str::lines doesn't yield anything for the empty string
- if !doc.is_empty() {
- buf.extend(Itertools::intersperse(
- doc.lines().map(|line| {
- line.char_indices()
- .nth(indent)
- .map_or(line, |(offset, _)| &line[offset..])
- .trim_end()
- }),
- "\n",
- ));
- }
- buf.push('\n');
- }
- buf.pop();
- if buf.is_empty() {
- None
- } else {
- Some(Documentation(buf))
- }
- }
-
pub fn has_doc_hidden(&self) -> bool {
self.by_key("doc").tt_values().any(|tt| {
tt.delimiter.kind == DelimiterKind::Parenthesis &&
@@ -299,7 +251,6 @@
}
}
-use std::slice::Iter as SliceIter;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum DocAtom {
/// eg. `#[doc(hidden)]`
@@ -313,7 +264,6 @@
// Adapted from `CfgExpr` parsing code
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
pub enum DocExpr {
Invalid,
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
@@ -574,62 +524,6 @@
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
}
-
- pub fn docs_with_rangemap(
- &self,
- db: &dyn DefDatabase,
- ) -> Option<(Documentation, DocsRangeMap)> {
- let docs =
- self.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
- let indent = doc_indent(self);
- let mut buf = String::new();
- let mut mapping = Vec::new();
- for (doc, idx) in docs {
- if !doc.is_empty() {
- let mut base_offset = 0;
- for raw_line in doc.split('\n') {
- let line = raw_line.trim_end();
- let line_len = line.len();
- let (offset, line) = match line.char_indices().nth(indent) {
- Some((offset, _)) => (offset, &line[offset..]),
- None => (0, line),
- };
- let buf_offset = buf.len();
- buf.push_str(line);
- mapping.push((
- TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
- idx,
- TextRange::at(
- (base_offset + offset).try_into().ok()?,
- line_len.try_into().ok()?,
- ),
- ));
- buf.push('\n');
- base_offset += raw_line.len() + 1;
- }
- } else {
- buf.push('\n');
- }
- }
- buf.pop();
- if buf.is_empty() {
- None
- } else {
- Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
- }
- }
-}
-
-fn doc_indent(attrs: &Attrs) -> usize {
- attrs
- .by_key("doc")
- .attrs()
- .filter_map(|attr| attr.string_value())
- .flat_map(|s| s.lines())
- .filter(|line| !line.chars().all(|c| c.is_whitespace()))
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
- .min()
- .unwrap_or(0)
}
#[derive(Debug)]
@@ -673,7 +567,7 @@
self.source_of_id(attr.id)
}
- fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
+ pub fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
let ast_idx = id.ast_index();
let file_id = match self.mod_def_site_file_id {
Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
@@ -687,69 +581,6 @@
}
}
-/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
-#[derive(Debug)]
-pub struct DocsRangeMap {
- source_map: AttrSourceMap,
- // (docstring-line-range, attr_index, attr-string-range)
- // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
- // the original (untrimmed) syntax doc line
- mapping: Vec<(TextRange, AttrId, TextRange)>,
-}
-
-impl DocsRangeMap {
- /// Maps a [`TextRange`] relative to the documentation string back to its AST range
- pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
- let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
- let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
- if !line_docs_range.contains_range(range) {
- return None;
- }
-
- let relative_range = range - line_docs_range.start();
-
- let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
- match source {
- Either::Left(attr) => {
- let string = get_doc_string_in_attr(attr)?;
- let text_range = string.open_quote_text_range()?;
- let range = TextRange::at(
- text_range.end() + original_line_src_range.start() + relative_range.start(),
- string.syntax().text_range().len().min(range.len()),
- );
- Some(InFile { file_id, value: range })
- }
- Either::Right(comment) => {
- let text_range = comment.syntax().text_range();
- let range = TextRange::at(
- text_range.start()
- + TextSize::try_from(comment.prefix().len()).ok()?
- + original_line_src_range.start()
- + relative_range.start(),
- text_range.len().min(range.len()),
- );
- Some(InFile { file_id, value: range })
- }
- }
- }
-}
-
-fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
- match it.expr() {
- // #[doc = lit]
- Some(ast::Expr::Literal(lit)) => match lit.kind() {
- ast::LiteralKind::String(it) => Some(it),
- _ => None,
- },
- // #[cfg_attr(..., doc = "", ...)]
- None => {
- // FIXME: See highlight injection for what to do here
- None
- }
- _ => None,
- }
-}
-
#[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'attr> {
attrs: &'attr Attrs,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 9bd0c30..152f05b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -8,7 +8,8 @@
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
//! ease updating.
-use once_cell::sync::OnceCell;
+use std::sync::OnceLock;
+
use rustc_hash::FxHashMap;
/// Ignored attribute namespaces used by tools.
@@ -29,7 +30,7 @@
}
pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
- static BUILTIN_LOOKUP_TABLE: OnceCell<FxHashMap<&'static str, usize>> = OnceCell::new();
+ static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<&'static str, usize>> = OnceLock::new();
BUILTIN_LOOKUP_TABLE
.get_or_init(|| {
INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index f8d492d..c0baf60 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -65,6 +65,8 @@
pub type FieldPtr = AstPtr<ast::RecordExprField>;
pub type FieldSource = InFile<FieldPtr>;
+pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
+pub type PatFieldSource = InFile<PatFieldPtr>;
/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
@@ -90,8 +92,8 @@
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
/// Instead, we use id of expression (`92`) to identify the field.
- field_map: FxHashMap<FieldSource, ExprId>,
field_map_back: FxHashMap<ExprId, FieldSource>,
+ pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
@@ -164,9 +166,10 @@
};
let module = def.module(db);
let expander = Expander::new(db, file_id, module);
- let (mut body, source_map) =
+ let (mut body, mut source_map) =
Body::new(db, def, expander, params, body, module.krate, is_async_fn);
body.shrink_to_fit();
+ source_map.shrink_to_fit();
(Arc::new(body), Arc::new(source_map))
}
@@ -375,9 +378,8 @@
self.field_map_back[&expr].clone()
}
- pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
- let src = node.map(AstPtr::new);
- self.field_map.get(&src).cloned()
+ pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
+ self.pat_field_map_back[&pat].clone()
}
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
@@ -389,4 +391,29 @@
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
&self.diagnostics
}
+
+ fn shrink_to_fit(&mut self) {
+ let Self {
+ expr_map,
+ expr_map_back,
+ pat_map,
+ pat_map_back,
+ label_map,
+ label_map_back,
+ field_map_back,
+ pat_field_map_back,
+ expansions,
+ diagnostics,
+ } = self;
+ expr_map.shrink_to_fit();
+ expr_map_back.shrink_to_fit();
+ pat_map.shrink_to_fit();
+ pat_map_back.shrink_to_fit();
+ label_map.shrink_to_fit();
+ label_map_back.shrink_to_fit();
+ field_map_back.shrink_to_fit();
+ pat_field_map_back.shrink_to_fit();
+ expansions.shrink_to_fit();
+ diagnostics.shrink_to_fit();
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 7071fcb..cc02df8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -25,13 +25,20 @@
use crate::{
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ builtin_type::BuiltinUint,
data::adt::StructKind,
db::DefDatabase,
expander::Expander,
hir::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
- ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
- Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+ dummy_expr_id,
+ format_args::{
+ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
+ FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
+ FormatPlaceholder, FormatSign, FormatTrait,
+ },
+ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
+ Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
lang_item::LangItem,
@@ -42,6 +49,8 @@
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
+type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
pub(super) fn lower(
db: &dyn DefDatabase,
owner: DefWithBodyId,
@@ -437,7 +446,6 @@
None => self.missing_expr(),
};
let src = self.expander.to_source(AstPtr::new(&field));
- self.source_map.field_map.insert(src.clone(), expr);
self.source_map.field_map_back.insert(expr, src);
Some(RecordLitField { name, expr })
})
@@ -579,11 +587,6 @@
syntax_ptr,
)
}
- ast::Expr::BoxExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Box { expr }, syntax_ptr)
- }
-
ast::Expr::ArrayExpr(e) => {
let kind = e.kind();
@@ -653,6 +656,16 @@
}
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
+ ast::Expr::AsmExpr(e) => {
+ let e = self.collect_expr_opt(e.expr());
+ self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
+ }
+ ast::Expr::OffsetOfExpr(e) => {
+ let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
+ let fields = e.fields().map(|it| it.as_name()).collect();
+ self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
+ }
+ ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr),
})
}
@@ -663,6 +676,7 @@
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
let prev_binding_owner = self.current_binding_owner.take();
self.current_binding_owner = Some(result_expr_id);
+
(result_expr_id, prev_binding_owner)
}
@@ -744,7 +758,27 @@
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_labelled_block_opt(label, e.loop_body());
- let condition = self.collect_expr_opt(e.condition());
+
+ // Labels can also be used in the condition expression, like this:
+ // ```
+ // fn main() {
+ // let mut optional = Some(0);
+ // 'my_label: while let Some(a) = match optional {
+ // None => break 'my_label,
+ // Some(val) => Some(val),
+ // } {
+ // println!("{}", a);
+ // optional = None;
+ // }
+ // }
+ // ```
+ let condition = match label {
+ Some(label) => {
+ self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition()))
+ }
+ None => self.collect_expr_opt(e.condition()),
+ };
+
let break_expr =
self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone());
let if_expr = self.alloc_expr(
@@ -1295,23 +1329,21 @@
ast::Pat::RecordPat(p) => {
let path =
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let args = p
- .record_pat_field_list()
- .expect("every struct should have a field list")
+ let record_pat_field_list =
+ &p.record_pat_field_list().expect("every struct should have a field list");
+ let args = record_pat_field_list
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
+ let src = self.expander.to_source(AstPtr::new(&f));
+ self.source_map.pat_field_map_back.insert(pat, src);
Some(RecordFieldPat { name, pat })
})
.collect();
- let ellipsis = p
- .record_pat_field_list()
- .expect("every struct should have a field list")
- .rest_pat()
- .is_some();
+ let ellipsis = record_pat_field_list.rest_pat().is_some();
Pat::Record { path, args, ellipsis }
}
@@ -1531,6 +1563,401 @@
}
}
// endregion: labels
+
+ // region: format
+ fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
+ let m = match expr {
+ ast::Expr::MacroExpr(m) => m,
+ ast::Expr::Literal(l) => {
+ return match l.kind() {
+ ast::LiteralKind::String(s) => Some((s, true)),
+ _ => None,
+ }
+ }
+ _ => return None,
+ };
+ let e = m.macro_call()?;
+ let macro_ptr = AstPtr::new(&e);
+ let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
+ expansion.and_then(|it| this.expand_macros_to_string(it))
+ })?;
+ Some((exp, false))
+ }
+
+ fn collect_format_args(
+ &mut self,
+ f: ast::FormatArgsExpr,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let mut args = FormatArgumentsCollector::new();
+ f.args().for_each(|arg| {
+ args.add(FormatArgument {
+ kind: match arg.name() {
+ Some(name) => FormatArgumentKind::Named(name.as_name()),
+ None => FormatArgumentKind::Normal,
+ },
+ expr: self.collect_expr_opt(arg.expr()),
+ });
+ });
+ let template = f.template();
+ let fmt_snippet = template.as_ref().map(ToString::to_string);
+ let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
+ Some((s, is_direct_literal)) => {
+ format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| {
+ self.alloc_expr_desugared(Expr::Path(Path::from(name)))
+ })
+ }
+ None => FormatArgs { template: Default::default(), arguments: args.finish() },
+ };
+
+ // Create a list of all _unique_ (argument, format trait) combinations.
+ // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
+ let mut argmap = FxIndexSet::default();
+ for piece in fmt.template.iter() {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
+ if let Ok(index) = placeholder.argument.index {
+ argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
+ }
+ }
+
+ let lit_pieces =
+ fmt.template
+ .iter()
+ .enumerate()
+ .filter_map(|(i, piece)| {
+ match piece {
+ FormatArgsPiece::Literal(s) => Some(
+ self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
+ ),
+ &FormatArgsPiece::Placeholder(_) => {
+ // Inject empty string before placeholders when not already preceded by a literal piece.
+ if i == 0
+ || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
+ {
+ Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
+ "".into(),
+ ))))
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .collect();
+ let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: lit_pieces,
+ is_assignee_expr: false,
+ }));
+ let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
+ expr: lit_pieces,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ });
+ let format_options = {
+ // Generate:
+ // &[format_spec_0, format_spec_1, format_spec_2]
+ let elements = fmt
+ .template
+ .iter()
+ .filter_map(|piece| {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
+ Some(self.make_format_spec(placeholder, &mut argmap))
+ })
+ .collect();
+ let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements,
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr: array,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ };
+ let arguments = &*fmt.arguments.arguments;
+
+ let args = if arguments.is_empty() {
+ let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: Box::default(),
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ } else {
+ // Generate:
+ // &match (&arg0, &arg1, &…) {
+ // args => [
+ // <core::fmt::Argument>::new_display(args.0),
+ // <core::fmt::Argument>::new_lower_hex(args.1),
+ // <core::fmt::Argument>::new_debug(args.0),
+ // …
+ // ]
+ // }
+ let args = argmap
+ .iter()
+ .map(|&(arg_index, ty)| {
+ let arg = self.alloc_expr_desugared(Expr::Ref {
+ expr: arguments[arg_index].expr,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ });
+ self.make_argument(arg, ty)
+ })
+ .collect();
+ let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: args,
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr: array,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ };
+
+ // Generate:
+ // <core::fmt::Arguments>::new_v1_formatted(
+ // lit_pieces,
+ // args,
+ // format_options,
+ // unsafe { ::core::fmt::UnsafeArg::new() }
+ // )
+
+ let Some(new_v1_formatted) =
+ LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
+ else {
+ return self.missing_expr();
+ };
+ let Some(unsafe_arg_new) =
+ LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
+ else {
+ return self.missing_expr();
+ };
+ let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
+
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
+ callee: unsafe_arg_new,
+ args: Box::default(),
+ is_assignee_expr: false,
+ });
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
+ id: None,
+ statements: Box::default(),
+ tail: Some(unsafe_arg_new),
+ });
+
+ self.alloc_expr(
+ Expr::Call {
+ callee: new_v1_formatted,
+ args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr,
+ )
+ }
+
+ /// Generate a hir expression for a format_args placeholder specification.
+ ///
+ /// Generates
+ ///
+ /// ```text
+ /// <core::fmt::rt::Placeholder::new(
+ /// …usize, // position
+ /// '…', // fill
+ /// <core::fmt::rt::Alignment>::…, // alignment
+ /// …u32, // flags
+ /// <core::fmt::rt::Count::…>, // width
+ /// <core::fmt::rt::Count::…>, // precision
+ /// )
+ /// ```
+ fn make_format_spec(
+ &mut self,
+ placeholder: &FormatPlaceholder,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> ExprId {
+ let position = match placeholder.argument.index {
+ Ok(arg_index) => {
+ let (i, _) =
+ argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
+ self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ i as u128,
+ Some(BuiltinUint::Usize),
+ )))
+ }
+ Err(_) => self.missing_expr(),
+ };
+ let &FormatOptions {
+ ref width,
+ ref precision,
+ alignment,
+ fill,
+ sign,
+ alternate,
+ zero_pad,
+ debug_hex,
+ } = &placeholder.format_options;
+ let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
+
+ let align = {
+ let align = LangItem::FormatAlignment.ty_rel_path(
+ self.db,
+ self.krate,
+ match alignment {
+ Some(FormatAlignment::Left) => name![Left],
+ Some(FormatAlignment::Right) => name![Right],
+ Some(FormatAlignment::Center) => name![Center],
+ None => name![Unknown],
+ },
+ );
+ match align {
+ Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
+ None => self.missing_expr(),
+ }
+ };
+ // This needs to match `Flag` in library/core/src/fmt/rt.rs.
+ let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
+ | ((sign == Some(FormatSign::Minus)) as u32) << 1
+ | (alternate as u32) << 2
+ | (zero_pad as u32) << 3
+ | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
+ | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
+ let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ flags as u128,
+ Some(BuiltinUint::U32),
+ )));
+ let precision = self.make_count(&precision, argmap);
+ let width = self.make_count(&width, argmap);
+
+ let format_placeholder_new = {
+ let format_placeholder_new =
+ LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]);
+ match format_placeholder_new {
+ Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
+ None => self.missing_expr(),
+ }
+ };
+
+ self.alloc_expr_desugared(Expr::Call {
+ callee: format_placeholder_new,
+ args: Box::new([position, fill, align, flags, precision, width]),
+ is_assignee_expr: false,
+ })
+ }
+
+ /// Generate a hir expression for a format_args Count.
+ ///
+ /// Generates:
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Is(…)
+ /// ```
+ ///
+ /// or
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Param(…)
+ /// ```
+ ///
+ /// or
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Implied
+ /// ```
+ fn make_count(
+ &mut self,
+ count: &Option<FormatCount>,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> ExprId {
+ match count {
+ Some(FormatCount::Literal(n)) => {
+ match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
+ Some(count_is) => {
+ let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
+ let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ *n as u128,
+ Some(BuiltinUint::Usize),
+ )));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: count_is,
+ args: Box::new([args]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ }
+ Some(FormatCount::Argument(arg)) => {
+ if let Ok(arg_index) = arg.index {
+ let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
+
+ match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
+ Some(count_param) => {
+ let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
+ let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ i as u128,
+ Some(BuiltinUint::Usize),
+ )));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: count_param,
+ args: Box::new([args]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ } else {
+ self.missing_expr()
+ }
+ }
+ None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) {
+ Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
+ None => self.missing_expr(),
+ },
+ }
+ }
+
+ /// Generate a hir expression representing an argument to a format_args invocation.
+ ///
+ /// Generates:
+ ///
+ /// ```text
+ /// <core::fmt::Argument>::new_…(arg)
+ /// ```
+ fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
+ use ArgumentType::*;
+ use FormatTrait::*;
+ match LangItem::FormatArgument.ty_rel_path(
+ self.db,
+ self.krate,
+ match ty {
+ Format(Display) => name![new_display],
+ Format(Debug) => name![new_debug],
+ Format(LowerExp) => name![new_lower_exp],
+ Format(UpperExp) => name![new_upper_exp],
+ Format(Octal) => name![new_octal],
+ Format(Pointer) => name![new_pointer],
+ Format(Binary) => name![new_binary],
+ Format(LowerHex) => name![new_lower_hex],
+ Format(UpperHex) => name![new_upper_hex],
+ Usize => name![from_usize],
+ },
+ ) {
+ Some(new_fn) => {
+ let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: new_fn,
+ args: Box::new([arg]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ }
+ // endregion: format
}
fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
@@ -1606,3 +2033,9 @@
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
.map_or(false, |it| it.kind() == syntax::T![,])
}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+ Format(FormatTrait),
+ Usize,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5d71abe..fad4d7a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,7 +2,7 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
+use itertools::Itertools;
use syntax::ast::HasName;
use crate::{
@@ -51,8 +51,7 @@
}
};
- let mut p =
- Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('(');
body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| {
@@ -76,8 +75,7 @@
_owner: DefWithBodyId,
expr: ExprId,
) -> String {
- let mut p =
- Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
p.print_expr(expr);
p.buf
}
@@ -98,7 +96,7 @@
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -142,9 +140,14 @@
}
fn newline(&mut self) {
- match self.buf.chars().rev().find(|ch| *ch != ' ') {
- Some('\n') | None => {}
- _ => writeln!(self).unwrap(),
+ match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
+ Some((_, '\n')) | None => {}
+ Some((idx, _)) => {
+ if idx != 0 {
+ self.buf.drain(self.buf.len() - idx..);
+ }
+ writeln!(self).unwrap()
+ }
}
}
@@ -154,6 +157,19 @@
match expr {
Expr::Missing => w!(self, "�"),
Expr::Underscore => w!(self, "_"),
+ Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
+ Expr::OffsetOf(offset_of) => {
+ w!(self, "builtin#offset_of(");
+ self.print_type_ref(&offset_of.container);
+ w!(
+ self,
+ ", {})",
+ offset_of
+ .fields
+ .iter()
+ .format_with(".", |field, f| f(&field.display(self.db.upcast())))
+ );
+ }
Expr::Path(path) => self.print_path(path),
Expr::If { condition, then_branch, else_branch } => {
w!(self, "if ");
@@ -173,7 +189,7 @@
}
Expr::Loop { body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name.display(self.db));
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
}
w!(self, "loop ");
self.print_expr(*body);
@@ -193,7 +209,7 @@
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver);
- w!(self, ".{}", method_name.display(self.db));
+ w!(self, ".{}", method_name.display(self.db.upcast()));
if let Some(args) = generic_args {
w!(self, "::<");
print_generic_args(self.db, args, self).unwrap();
@@ -231,13 +247,13 @@
Expr::Continue { label } => {
w!(self, "continue");
if let Some(lbl) = label {
- w!(self, " {}", self.body[*lbl].name.display(self.db));
+ w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
}
}
Expr::Break { expr, label } => {
w!(self, "break");
if let Some(lbl) = label {
- w!(self, " {}", self.body[*lbl].name.display(self.db));
+ w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
}
if let Some(expr) = expr {
self.whitespace();
@@ -276,7 +292,7 @@
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name.display(self.db));
+ w!(p, "{}: ", field.name.display(self.db.upcast()));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -293,7 +309,7 @@
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name.display(self.db));
+ w!(self, ".{}", name.display(self.db.upcast()));
}
Expr::Await { expr } => {
self.print_expr(*expr);
@@ -431,7 +447,8 @@
}
Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => {
- let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
+ let label =
+ label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
self.print_block(label.as_deref(), statements, tail);
}
Expr::Unsafe { id: _, statements, tail } => {
@@ -507,7 +524,7 @@
w!(self, " {{");
self.indented(|p| {
for arg in args.iter() {
- w!(p, "{}: ", arg.name.display(self.db));
+ w!(p, "{}: ", arg.name.display(self.db.upcast()));
p.print_pat(arg.pat);
wln!(p, ",");
}
@@ -666,6 +683,6 @@
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name.display(self.db));
+ w!(self, "{}{}", mode, name.display(self.db.upcast()));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
index d558201..1658757 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
@@ -1,13 +1,13 @@
mod block;
use base_db::{fixture::WithFixture, SourceDatabase};
-use expect_test::Expect;
+use expect_test::{expect, Expect};
use crate::{test_db::TestDB, ModuleDefId};
use super::*;
-fn lower(ra_fixture: &str) -> Arc<Body> {
+fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
let db = TestDB::with_files(ra_fixture);
let krate = db.crate_graph().iter().next().unwrap();
@@ -21,8 +21,10 @@
}
}
}
+ let fn_def = fn_def.unwrap().into();
- db.body(fn_def.unwrap().into())
+ let body = db.body(fn_def);
+ (db, body, fn_def)
}
fn def_map_at(ra_fixture: &str) -> String {
@@ -138,3 +140,84 @@
"#,
);
}
+
+#[test]
+fn desugar_builtin_format_args() {
+ // Regression test for a path resolution bug introduced with inner item handling.
+ let (db, body, def) = lower(
+ r#"
+//- minicore: fmt
+fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+}
+"#,
+ );
+
+ expect![[r#"
+ fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#lang(Arguments::new_v1_formatted)(
+ &[
+ "\"hello ", " ", " friends, we ", " ", "", "\"",
+ ],
+ &[
+ builtin#lang(Argument::new_display)(
+ &count,
+ ), builtin#lang(Argument::new_display)(
+ &"fancy",
+ ), builtin#lang(Argument::new_debug)(
+ &are,
+ ), builtin#lang(Argument::new_display)(
+ &"!",
+ ),
+ ],
+ &[
+ builtin#lang(Placeholder::new)(
+ 0usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 8u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Is)(
+ 2usize,
+ ),
+ ), builtin#lang(Placeholder::new)(
+ 1usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 2usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 1usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 3usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ),
+ ],
+ unsafe {
+ builtin#lang(UnsafeArg::new)()
+ },
+ );
+ }"#]]
+ .assert_eq(&body.pretty_print(&db, def))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index c8df3f3..224f732 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -447,6 +447,7 @@
}
}
+ // FIXME: Linear lookup
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index b60e790..b9c5ff7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -37,6 +37,20 @@
find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
}
+#[derive(Copy, Clone, Debug)]
+enum Stability {
+ Unstable,
+ Stable,
+}
+use Stability::*;
+
+fn zip_stability(a: Stability, b: Stability) -> Stability {
+ match (a, b) {
+ (Stable, Stable) => Stable,
+ _ => Unstable,
+ }
+}
+
const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -95,7 +109,8 @@
MAX_PATH_LEN,
prefixed,
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
- );
+ )
+ .map(|(item, _)| item);
}
// - if the item is already in scope, return the name under which it is
@@ -143,6 +158,7 @@
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
scope_name,
)
+ .map(|(item, _)| item)
}
fn find_path_for_module(
@@ -155,7 +171,7 @@
max_len: usize,
prefixed: Option<PrefixKind>,
prefer_no_std: bool,
-) -> Option<ModPath> {
+) -> Option<(ModPath, Stability)> {
if max_len == 0 {
return None;
}
@@ -165,19 +181,19 @@
let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
if prefixed.is_none() {
if let Some(scope_name) = scope_name {
- return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
+ return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable));
}
}
// - if the item is the crate root, return `crate`
if module_id == crate_root {
- return Some(ModPath::from_segments(PathKind::Crate, None));
+ return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
}
// - if relative paths are fine, check if we are searching for a parent
if prefixed.filter(PrefixKind::is_absolute).is_none() {
if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
- return modpath;
+ return modpath.zip(Some(Stable));
}
}
@@ -201,14 +217,14 @@
} else {
PathKind::Plain
};
- return Some(ModPath::from_segments(kind, Some(name)));
+ return Some((ModPath::from_segments(kind, Some(name)), Stable));
}
}
if let value @ Some(_) =
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
{
- return value;
+ return value.zip(Some(Stable));
}
calculate_best_path(
db,
@@ -301,11 +317,19 @@
mut prefixed: Option<PrefixKind>,
prefer_no_std: bool,
scope_name: Option<Name>,
-) -> Option<ModPath> {
+) -> Option<(ModPath, Stability)> {
if max_len <= 1 {
return None;
}
let mut best_path = None;
+ let update_best_path =
+ |best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
+ Some((old_path, old_stability)) => {
+ *old_path = new_path.0;
+ *old_stability = zip_stability(*old_stability, new_path.1);
+ }
+ None => *best_path = Some(new_path),
+ };
// Recursive case:
// - otherwise, look for modules containing (reexporting) it and import it from one of those
if item.krate(db) == Some(from.krate) {
@@ -328,14 +352,14 @@
prefixed,
prefer_no_std,
) {
- path.push_segment(name);
+ path.0.push_segment(name);
- let new_path = match best_path {
+ let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
- best_path_len = new_path.len();
- best_path = Some(new_path);
+ best_path_len = new_path.0.len();
+ update_best_path(&mut best_path, new_path);
}
}
} else {
@@ -354,7 +378,7 @@
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
- let mut path = find_path_for_module(
+ let (mut path, path_stability) = find_path_for_module(
db,
def_map,
visited_modules,
@@ -367,16 +391,19 @@
)?;
cov_mark::hit!(partially_imported);
path.push_segment(info.name.clone());
- Some(path)
+ Some((
+ path,
+ zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
+ ))
})
});
for path in extern_paths {
- let new_path = match best_path {
+ let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
- best_path = Some(new_path);
+ update_best_path(&mut best_path, new_path);
}
}
if let Some(module) = item.module(db) {
@@ -387,15 +414,24 @@
}
match prefixed.map(PrefixKind::prefix) {
Some(prefix) => best_path.or_else(|| {
- scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
+ scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable))
}),
None => best_path,
}
}
-fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
+fn select_best_path(
+ old_path: (ModPath, Stability),
+ new_path: (ModPath, Stability),
+ prefer_no_std: bool,
+) -> (ModPath, Stability) {
+ match (old_path.1, new_path.1) {
+ (Stable, Unstable) => return old_path,
+ (Unstable, Stable) => return new_path,
+ _ => {}
+ }
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
- match (old_path.segments().first(), new_path.segments().first()) {
+ match (old_path.0.segments().first(), new_path.0.segments().first()) {
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
let rank = match prefer_no_std {
false => |name: &Name| match name {
@@ -416,7 +452,7 @@
match nrank.cmp(&orank) {
Ordering::Less => old_path,
Ordering::Equal => {
- if new_path.len() < old_path.len() {
+ if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@@ -426,7 +462,7 @@
}
}
_ => {
- if new_path.len() < old_path.len() {
+ if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@@ -1360,4 +1396,29 @@
"std::ops::Deref",
);
}
+
+ #[test]
+ fn respect_unstable_modules() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std,core
+#![no_std]
+extern crate std;
+$0
+//- /longer.rs crate:std deps:core
+pub mod error {
+ pub use core::error::Error;
+}
+//- /core.rs crate:core
+pub mod error {
+ #![unstable(feature = "error_in_core", issue = "103765")]
+ pub trait Error {}
+}
+"#,
+ "std::error::Error",
+ "std::error::Error",
+ "std::error::Error",
+ "std::error::Error",
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 6591c92..591ee77 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -13,6 +13,7 @@
//! See also a neighboring `body` module.
pub mod type_ref;
+pub mod format_args;
use std::fmt;
@@ -117,7 +118,6 @@
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
use ast::LiteralKind;
match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float(
@@ -281,6 +281,19 @@
Array(Array),
Literal(Literal),
Underscore,
+ OffsetOf(OffsetOf),
+ InlineAsm(InlineAsm),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct OffsetOf {
+ pub container: Interned<TypeRef>,
+ pub fields: Box<[Name]>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct InlineAsm {
+ pub e: ExprId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -341,7 +354,8 @@
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
match self {
Expr::Missing => {}
- Expr::Path(_) => {}
+ Expr::Path(_) | Expr::OffsetOf(_) => {}
+ Expr::InlineAsm(it) => f(it.e),
Expr::If { condition, then_branch, else_branch } => {
f(*condition);
f(*then_branch);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
new file mode 100644
index 0000000..75025a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
@@ -0,0 +1,502 @@
+//! Parses `format_args` input.
+use std::mem;
+
+use hir_expand::name::Name;
+use rustc_parse_format as parse;
+use syntax::{
+ ast::{self, IsString},
+ AstToken, SmolStr, TextRange,
+};
+
+use crate::hir::ExprId;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgs {
+ pub template: Box<[FormatArgsPiece]>,
+ pub arguments: FormatArguments,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArguments {
+ pub arguments: Box<[FormatArgument]>,
+ pub num_unnamed_args: usize,
+ pub num_explicit_args: usize,
+ pub names: Box<[(Name, usize)]>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FormatArgsPiece {
+ Literal(Box<str>),
+ Placeholder(FormatPlaceholder),
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub struct FormatPlaceholder {
+ /// Index into [`FormatArgs::arguments`].
+ pub argument: FormatArgPosition,
+ /// The span inside the format string for the full `{…}` placeholder.
+ pub span: Option<TextRange>,
+ /// `{}`, `{:?}`, or `{:x}`, etc.
+ pub format_trait: FormatTrait,
+ /// `{}` or `{:.5}` or `{:-^20}`, etc.
+ pub format_options: FormatOptions,
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgPosition {
+ /// Which argument this position refers to (Ok),
+ /// or would've referred to if it existed (Err).
+ pub index: Result<usize, usize>,
+ /// What kind of position this is. See [`FormatArgPositionKind`].
+ pub kind: FormatArgPositionKind,
+ /// The span of the name or number.
+ pub span: Option<TextRange>,
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub enum FormatArgPositionKind {
+ /// `{}` or `{:.*}`
+ Implicit,
+ /// `{1}` or `{:1$}` or `{:.1$}`
+ Number,
+ /// `{a}` or `{:a$}` or `{:.a$}`
+ Named,
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub enum FormatTrait {
+ /// `{}`
+ Display,
+ /// `{:?}`
+ Debug,
+ /// `{:e}`
+ LowerExp,
+ /// `{:E}`
+ UpperExp,
+ /// `{:o}`
+ Octal,
+ /// `{:p}`
+ Pointer,
+ /// `{:b}`
+ Binary,
+ /// `{:x}`
+ LowerHex,
+ /// `{:X}`
+ UpperHex,
+}
+
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
+pub struct FormatOptions {
+ /// The width. E.g. `{:5}` or `{:width$}`.
+ pub width: Option<FormatCount>,
+ /// The precision. E.g. `{:.5}` or `{:.precision$}`.
+ pub precision: Option<FormatCount>,
+ /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
+ pub alignment: Option<FormatAlignment>,
+ /// The fill character. E.g. the `.` in `{:.>10}`.
+ pub fill: Option<char>,
+ /// The `+` or `-` flag.
+ pub sign: Option<FormatSign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag. E.g. the `0` in `{:02x}`.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
+ pub debug_hex: Option<FormatDebugHex>,
+}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatSign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatDebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatAlignment {
+ /// `{:<}`
+ Left,
+ /// `{:>}`
+ Right,
+ /// `{:^}`
+ Center,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatCount {
+ /// `{:5}` or `{:.5}`
+ Literal(usize),
+ /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
+ Argument(FormatArgPosition),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgument {
+ pub kind: FormatArgumentKind,
+ pub expr: ExprId,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum FormatArgumentKind {
+ /// `format_args(…, arg)`
+ Normal,
+ /// `format_args(…, arg = 1)`
+ Named(Name),
+ /// `format_args("… {arg} …")`
+ Captured(Name),
+}
+
+// Only used in parse_args and report_invalid_references,
+// to indicate how a referred argument was used.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum PositionUsedAs {
+ Placeholder(Option<TextRange>),
+ Precision,
+ Width,
+}
+use PositionUsedAs::*;
+
+pub(crate) fn parse(
+ s: &ast::String,
+ fmt_snippet: Option<String>,
+ mut args: FormatArgumentsCollector,
+ is_direct_literal: bool,
+ mut synth: impl FnMut(Name) -> ExprId,
+) -> FormatArgs {
+ let text = s.text();
+ let str_style = match s.quote_offsets() {
+ Some(offsets) => {
+ let raw = u32::from(offsets.quotes.0.len()) - 1;
+ (raw != 0).then_some(raw as usize)
+ }
+ None => None,
+ };
+ let mut parser =
+ parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format);
+
+ let mut pieces = Vec::new();
+ while let Some(piece) = parser.next() {
+ if !parser.errors.is_empty() {
+ break;
+ } else {
+ pieces.push(piece);
+ }
+ }
+ let is_source_literal = parser.is_source_literal;
+ if !parser.errors.is_empty() {
+ // FIXME: Diagnose
+ return FormatArgs { template: Default::default(), arguments: args.finish() };
+ }
+
+ let to_span = |inner_span: parse::InnerSpan| {
+ is_source_literal.then(|| {
+ TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
+ })
+ };
+
+ let mut used = vec![false; args.explicit_args().len()];
+ let mut invalid_refs = Vec::new();
+ let mut numeric_refences_to_named_arg = Vec::new();
+
+ enum ArgRef<'a> {
+ Index(usize),
+ Name(&'a str, Option<TextRange>),
+ }
+ let mut lookup_arg = |arg: ArgRef<'_>,
+ span: Option<TextRange>,
+ used_as: PositionUsedAs,
+ kind: FormatArgPositionKind|
+ -> FormatArgPosition {
+ let index = match arg {
+ ArgRef::Index(index) => {
+ if let Some(arg) = args.by_index(index) {
+ used[index] = true;
+ if arg.kind.ident().is_some() {
+ // This was a named argument, but it was used as a positional argument.
+ numeric_refences_to_named_arg.push((index, span, used_as));
+ }
+ Ok(index)
+ } else {
+ // Doesn't exist as an explicit argument.
+ invalid_refs.push((index, span, used_as, kind));
+ Err(index)
+ }
+ }
+ ArgRef::Name(name, _span) => {
+ let name = Name::new_text_dont_use(SmolStr::new(name));
+ if let Some((index, _)) = args.by_name(&name) {
+ // Name found in `args`, so we resolve it to its index.
+ if index < args.explicit_args().len() {
+ // Mark it as used, if it was an explicit argument.
+ used[index] = true;
+ }
+ Ok(index)
+ } else {
+ // Name not found in `args`, so we add it as an implicitly captured argument.
+ if !is_direct_literal {
+ // For the moment capturing variables from format strings expanded from macros is
+ // disabled (see RFC #2795)
+ // FIXME: Diagnose
+ }
+ Ok(args.add(FormatArgument {
+ kind: FormatArgumentKind::Captured(name.clone()),
+ // FIXME: This is problematic, we might want to synthesize a dummy
+ // expression proper and/or desugar these.
+ expr: synth(name),
+ }))
+ }
+ }
+ };
+ FormatArgPosition { index, kind, span }
+ };
+
+ let mut template = Vec::new();
+ let mut unfinished_literal = String::new();
+ let mut placeholder_index = 0;
+
+ for piece in pieces {
+ match piece {
+ parse::Piece::String(s) => {
+ unfinished_literal.push_str(s);
+ }
+ parse::Piece::NextArgument(arg) => {
+ let parse::Argument { position, position_span, format } = *arg;
+ if !unfinished_literal.is_empty() {
+ template.push(FormatArgsPiece::Literal(
+ mem::take(&mut unfinished_literal).into_boxed_str(),
+ ));
+ }
+
+ let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
+ placeholder_index += 1;
+
+ let position_span = to_span(position_span);
+ let argument = match position {
+ parse::ArgumentImplicitlyIs(i) => lookup_arg(
+ ArgRef::Index(i),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Implicit,
+ ),
+ parse::ArgumentIs(i) => lookup_arg(
+ ArgRef::Index(i),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Number,
+ ),
+ parse::ArgumentNamed(name) => lookup_arg(
+ ArgRef::Name(name, position_span),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Named,
+ ),
+ };
+
+ let alignment = match format.align {
+ parse::AlignUnknown => None,
+ parse::AlignLeft => Some(FormatAlignment::Left),
+ parse::AlignRight => Some(FormatAlignment::Right),
+ parse::AlignCenter => Some(FormatAlignment::Center),
+ };
+
+ let format_trait = match format.ty {
+ "" => FormatTrait::Display,
+ "?" => FormatTrait::Debug,
+ "e" => FormatTrait::LowerExp,
+ "E" => FormatTrait::UpperExp,
+ "o" => FormatTrait::Octal,
+ "p" => FormatTrait::Pointer,
+ "b" => FormatTrait::Binary,
+ "x" => FormatTrait::LowerHex,
+ "X" => FormatTrait::UpperHex,
+ _ => {
+ // FIXME: Diagnose
+ FormatTrait::Display
+ }
+ };
+
+ let precision_span = format.precision_span.and_then(to_span);
+ let precision = match format.precision {
+ parse::CountIs(n) => Some(FormatCount::Literal(n)),
+ parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Name(name, to_span(name_span)),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Named,
+ ))),
+ parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Number,
+ ))),
+ parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Implicit,
+ ))),
+ parse::CountImplied => None,
+ };
+
+ let width_span = format.width_span.and_then(to_span);
+ let width = match format.width {
+ parse::CountIs(n) => Some(FormatCount::Literal(n)),
+ parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Name(name, to_span(name_span)),
+ width_span,
+ Width,
+ FormatArgPositionKind::Named,
+ ))),
+ parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ width_span,
+ Width,
+ FormatArgPositionKind::Number,
+ ))),
+ parse::CountIsStar(_) => unreachable!(),
+ parse::CountImplied => None,
+ };
+
+ template.push(FormatArgsPiece::Placeholder(FormatPlaceholder {
+ argument,
+ span,
+ format_trait,
+ format_options: FormatOptions {
+ fill: format.fill,
+ alignment,
+ sign: format.sign.map(|s| match s {
+ parse::Sign::Plus => FormatSign::Plus,
+ parse::Sign::Minus => FormatSign::Minus,
+ }),
+ alternate: format.alternate,
+ zero_pad: format.zero_pad,
+ debug_hex: format.debug_hex.map(|s| match s {
+ parse::DebugHex::Lower => FormatDebugHex::Lower,
+ parse::DebugHex::Upper => FormatDebugHex::Upper,
+ }),
+ precision,
+ width,
+ },
+ }));
+ }
+ }
+ }
+
+ if !unfinished_literal.is_empty() {
+ template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str()));
+ }
+
+ if !invalid_refs.is_empty() {
+ // FIXME: Diagnose
+ }
+
+ let unused = used
+ .iter()
+ .enumerate()
+ .filter(|&(_, used)| !used)
+ .map(|(i, _)| {
+ let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
+ (args.explicit_args()[i].expr, named)
+ })
+ .collect::<Vec<_>>();
+
+ if !unused.is_empty() {
+ // FIXME: Diagnose
+ }
+
+ FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgumentsCollector {
+ arguments: Vec<FormatArgument>,
+ num_unnamed_args: usize,
+ num_explicit_args: usize,
+ names: Vec<(Name, usize)>,
+}
+
+impl FormatArgumentsCollector {
+ pub(crate) fn finish(self) -> FormatArguments {
+ FormatArguments {
+ arguments: self.arguments.into_boxed_slice(),
+ num_unnamed_args: self.num_unnamed_args,
+ num_explicit_args: self.num_explicit_args,
+ names: self.names.into_boxed_slice(),
+ }
+ }
+
+ pub fn new() -> Self {
+ Self { arguments: vec![], names: vec![], num_unnamed_args: 0, num_explicit_args: 0 }
+ }
+
+ pub fn add(&mut self, arg: FormatArgument) -> usize {
+ let index = self.arguments.len();
+ if let Some(name) = arg.kind.ident() {
+ self.names.push((name.clone(), index));
+ } else if self.names.is_empty() {
+ // Only count the unnamed args before the first named arg.
+ // (Any later ones are errors.)
+ self.num_unnamed_args += 1;
+ }
+ if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
+ // This is an explicit argument.
+ // Make sure that all arguments so far are explicit.
+ assert_eq!(
+ self.num_explicit_args,
+ self.arguments.len(),
+ "captured arguments must be added last"
+ );
+ self.num_explicit_args += 1;
+ }
+ self.arguments.push(arg);
+ index
+ }
+
+ pub fn by_name(&self, name: &Name) -> Option<(usize, &FormatArgument)> {
+ let &(_, i) = self.names.iter().find(|(n, _)| n == name)?;
+ Some((i, &self.arguments[i]))
+ }
+
+ pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
+ (i < self.num_explicit_args).then(|| &self.arguments[i])
+ }
+
+ pub fn unnamed_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_unnamed_args]
+ }
+
+ pub fn named_args(&self) -> &[FormatArgument] {
+ &self.arguments[self.num_unnamed_args..self.num_explicit_args]
+ }
+
+ pub fn explicit_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_explicit_args]
+ }
+
+ pub fn all_args(&self) -> &[FormatArgument] {
+ &self.arguments[..]
+ }
+
+ pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
+ &mut self.arguments
+ }
+}
+
+impl FormatArgumentKind {
+ pub fn ident(&self) -> Option<&Name> {
+ match self {
+ Self::Normal => None,
+ Self::Named(id) => Some(id),
+ Self::Captured(id) => Some(id),
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 6038caa..44b7f1b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -32,6 +32,8 @@
pub is_trait_assoc_item: bool,
/// Whether this item is annotated with `#[doc(hidden)]`.
pub is_doc_hidden: bool,
+ /// Whether this item is annotated with `#[unstable(..)]`.
+ pub is_unstable: bool,
}
/// A map from publicly exported items to its name.
@@ -113,7 +115,6 @@
for (name, per_ns) in visible_items {
for (item, import) in per_ns.iter_items() {
- // FIXME: Not yet used, but will be once we handle doc(hidden) import sources
let attr_id = if let Some(import) = import {
match import {
ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
@@ -125,28 +126,59 @@
ItemInNs::Macros(id) => Some(id.into()),
}
};
- let is_doc_hidden =
- attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden());
+ let status @ (is_doc_hidden, is_unstable) =
+ attr_id.map_or((false, false), |attr_id| {
+ let attrs = db.attrs(attr_id);
+ (attrs.has_doc_hidden(), attrs.is_unstable())
+ });
let import_info = ImportInfo {
name: name.clone(),
container: module,
is_trait_assoc_item: false,
is_doc_hidden,
+ is_unstable,
};
match depth_map.entry(item) {
- Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)),
+ Entry::Vacant(entry) => _ = entry.insert((depth, status)),
Entry::Occupied(mut entry) => {
- let &(occ_depth, occ_is_doc_hidden) = entry.get();
- // Prefer the one that is not doc(hidden),
- // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one.
- let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden
- || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth;
- if !overwrite_entry {
+ let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get();
+ (depth, occ_depth);
+ let overwrite = match (
+ is_doc_hidden,
+ occ_is_doc_hidden,
+ is_unstable,
+ occ_is_unstable,
+ ) {
+ // no change of hiddeness or unstableness
+ (true, true, true, true)
+ | (true, true, false, false)
+ | (false, false, true, true)
+ | (false, false, false, false) => depth < occ_depth,
+
+ // either less hidden or less unstable, accept
+ (true, true, false, true)
+ | (false, true, true, true)
+ | (false, true, false, true)
+ | (false, true, false, false)
+ | (false, false, false, true) => true,
+ // more hidden or unstable, discard
+ (true, true, true, false)
+ | (true, false, true, true)
+ | (true, false, true, false)
+ | (true, false, false, false)
+ | (false, false, true, false) => false,
+
+ // exchanges doc(hidden) for unstable (and vice-versa),
+ (true, false, false, true) | (false, true, true, false) => {
+ depth < occ_depth
+ }
+ };
+ if !overwrite {
continue;
}
- entry.insert((depth, is_doc_hidden));
+ entry.insert((depth, status));
}
}
@@ -171,7 +203,7 @@
}
}
}
-
+ map.shrink_to_fit();
map
}
@@ -200,11 +232,13 @@
ItemInNs::Values(module_def_id)
};
+ let attrs = &db.attrs(item.into());
let assoc_item_info = ImportInfo {
container: trait_import_info.container,
name: assoc_item_name.clone(),
is_trait_assoc_item: true,
- is_doc_hidden: db.attrs(item.into()).has_doc_hidden(),
+ is_doc_hidden: attrs.has_doc_hidden(),
+ is_unstable: attrs.is_unstable(),
};
map.insert(assoc_item, assoc_item_info);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 3e19227..4c812b6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -177,7 +177,7 @@
}
pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
- pretty::print_item_tree(db.upcast(), self)
+ pretty::print_item_tree(db, self)
}
fn data(&self) -> &ItemTreeData {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 4b852dd..417bd37 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,8 +2,6 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
-
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -12,7 +10,7 @@
use super::*;
-pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
@@ -45,7 +43,7 @@
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -91,7 +89,7 @@
self,
"#{}[{}{}]{}",
inner,
- attr.path.display(self.db),
+ attr.path.display(self.db.upcast()),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
separated_by,
);
@@ -106,7 +104,7 @@
fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] {
- RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
+ RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())),
RawVisibility::Public => w!(self, "pub "),
};
}
@@ -121,7 +119,7 @@
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -135,7 +133,7 @@
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -168,20 +166,20 @@
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path.display(self.db));
+ w!(self, "{}", path.display(self.db.upcast()));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path.display(self.db));
+ w!(self, "{}::", path.display(self.db.upcast()));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix.display(self.db));
+ w!(self, "{}::", prefix.display(self.db.upcast()));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -209,7 +207,7 @@
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "extern crate {}", name.display(self.db));
+ w!(self, "extern crate {}", name.display(self.db.upcast()));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
@@ -256,7 +254,7 @@
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
- w!(self, "fn {}", name.display(self.db));
+ w!(self, "fn {}", name.display(self.db.upcast()));
self.print_generic_params(explicit_generic_params);
w!(self, "(");
if !params.is_empty() {
@@ -290,7 +288,7 @@
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "struct {}", name.display(self.db));
+ w!(self, "struct {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -302,7 +300,7 @@
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "union {}", name.display(self.db));
+ w!(self, "union {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -314,14 +312,14 @@
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "enum {}", name.display(self.db));
+ w!(self, "enum {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant, "\n");
- w!(this, "{}", name.display(self.db));
+ w!(this, "{}", name.display(self.db.upcast()));
this.print_fields(fields);
wln!(this, ",");
}
@@ -333,7 +331,7 @@
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name.display(self.db)),
+ Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -347,7 +345,7 @@
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name.display(self.db));
+ w!(self, "{}: ", name.display(self.db.upcast()));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -369,7 +367,7 @@
if *is_auto {
w!(self, "auto ");
}
- w!(self, "trait {}", name.display(self.db));
+ w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -382,7 +380,7 @@
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "trait {}", name.display(self.db));
+ w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -415,7 +413,7 @@
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
self.print_visibility(*visibility);
- w!(self, "type {}", name.display(self.db));
+ w!(self, "type {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
if !bounds.is_empty() {
w!(self, ": ");
@@ -432,7 +430,7 @@
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name.display(self.db));
+ w!(self, "mod {}", name.display(self.db.upcast()));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -450,16 +448,16 @@
}
ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
- wln!(self, "{}!(...);", path.display(self.db));
+ wln!(self, "{}!(...);", path.display(self.db.upcast()));
}
ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it];
- wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
+ wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
}
ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- wln!(self, "macro {} {{ ... }}", name.display(self.db));
+ wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
}
}
@@ -491,7 +489,7 @@
}
first = false;
self.print_attrs_of(idx, " ");
- w!(self, "{}", lt.name.display(self.db));
+ w!(self, "{}", lt.name.display(self.db.upcast()));
}
for (idx, x) in params.type_or_consts.iter() {
if !first {
@@ -501,11 +499,11 @@
self.print_attrs_of(idx, " ");
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
- Some(name) => w!(self, "{}", name.display(self.db)),
+ Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_anon_{}", idx.into_raw()),
},
TypeOrConstParamData::ConstParamData(konst) => {
- w!(self, "const {}: ", konst.name.display(self.db));
+ w!(self, "const {}: ", konst.name.display(self.db.upcast()));
self.print_type_ref(&konst.ty);
}
}
@@ -540,8 +538,8 @@
wln!(
this,
"{}: {},",
- target.name.display(self.db),
- bound.name.display(self.db)
+ target.name.display(self.db.upcast()),
+ bound.name.display(self.db.upcast())
);
continue;
}
@@ -551,7 +549,7 @@
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt.display(self.db));
+ w!(this, "{}", lt.display(self.db.upcast()));
}
w!(this, "> ");
(target, bound)
@@ -562,7 +560,7 @@
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match ¶ms.type_or_consts[*id].name() {
- Some(name) => w!(this, "{}", name.display(self.db)),
+ Some(name) => w!(this, "{}", name.display(self.db.upcast())),
None => w!(this, "_anon_{}", id.into_raw()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 627479bb..1ae6bd4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,6 +2,7 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
+use hir_expand::name::Name;
use rustc_hash::FxHashMap;
use syntax::SmolStr;
use triomphe::Arc;
@@ -238,7 +239,17 @@
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
- Some(Path::LangItem(t))
+ Some(Path::LangItem(t, None))
+ }
+
+ pub fn ty_rel_path(
+ &self,
+ db: &dyn DefDatabase,
+ start_crate: CrateId,
+ seg: Name,
+ ) -> Option<Path> {
+ let t = db.lang_item(start_crate, *self)?;
+ Some(Path::LangItem(t, Some(seg)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
index e523c22..52781d98 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -1,10 +1,11 @@
//! Context for lowering paths.
+use std::cell::OnceCell;
+
use hir_expand::{
ast_id_map::{AstIdMap, AstIdNode},
hygiene::Hygiene,
AstId, HirFileId, InFile,
};
-use once_cell::unsync::OnceCell;
use syntax::ast;
use triomphe::Arc;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index b232651..4aedb22 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -23,6 +23,45 @@
}
#[test]
+fn test_asm_expand() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! asm {() => {}}
+
+fn main() {
+ let i: u64 = 3;
+ let o: u64;
+ unsafe {
+ asm!(
+ "mov {0}, {1}",
+ "add {0}, 5",
+ out(reg) o,
+ in(reg) i,
+ );
+ }
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! asm {() => {}}
+
+fn main() {
+ let i: u64 = 3;
+ let o: u64;
+ unsafe {
+ builtin #asm ( {
+ $crate::format_args!("mov {0}, {1}");
+ $crate::format_args!("add {0}, 5");
+ }
+ );
+ }
+}
+"##]],
+ );
+}
+
+#[test]
fn test_line_expand() {
check(
r#"
@@ -201,7 +240,7 @@
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args ("{} {:?}", arg1(a, b, c), arg2);
}
"##]],
);
@@ -219,10 +258,10 @@
fn main() {
format_args!(x = 2);
- format_args!(x =);
- format_args!(x =, x = 2);
- format_args!("{}", x =);
- format_args!(=, "{}", x =);
+ format_args!/*+errors*/(x =);
+ format_args!/*+errors*/(x =, x = 2);
+ format_args!/*+errors*/("{}", x =);
+ format_args!/*+errors*/(=, "{}", x =);
format_args!(x = 2, "{}", 5);
}
"#,
@@ -234,12 +273,19 @@
}
fn main() {
- /* error: no rule matches input tokens */;
- /* error: expected expression */;
- /* error: expected expression, expected COMMA */;
- /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
- /* error: expected expression, expected R_PAREN */;
- ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
+ builtin #format_args (x = 2);
+ /* parse error: expected expression */
+builtin #format_args (x = );
+ /* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected expression, item or let statement */
+builtin #format_args (x = , x = 2);
+ /* parse error: expected expression */
+builtin #format_args ("{}", x = );
+ /* parse error: expected expression */
+/* parse error: expected expression */
+builtin #format_args ( = , "{}", x = );
+ builtin #format_args (x = 2, "{}", 5);
}
"##]],
);
@@ -267,7 +313,7 @@
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args ("{} {:?}", a::<A, B>(), b);
}
"##]],
);
@@ -300,7 +346,7 @@
}
fn main() {
- ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
+ builtin #format_args (r#"{},mismatch,"{}","{}""#, location_csv_pat(db, &analysis, vfs, &sm, pat_id), mismatch.expected.display(db), mismatch.actual.display(db));
}
"##]],
);
@@ -334,7 +380,7 @@
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args (concat!("xxx{}y", "{:?}zzz"), 2, b);
}
"##]],
);
@@ -364,8 +410,8 @@
fn main() {
let _ =
- /* error: expected field name or number *//* parse error: expected field name or number */
-::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]);
+ /* parse error: expected field name or number */
+builtin #format_args ("{} {:?}", a.);
}
"##]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 2170cad..d090621 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -117,7 +117,7 @@
macro_rules! format_args {}
fn main(foo: ()) {
- /* error: unresolved macro identity */::core::fmt::Arguments::new_v1(&["", " ", " ", ], &[::core::fmt::ArgumentV1::new(&(::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(0), ::core::fmt::Display::fmt), ])), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(foo), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(identity!(10)), ::core::fmt::Display::fmt), ])
+ builtin #format_args ("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
}
"##]],
);
@@ -150,8 +150,8 @@
}
fn main(foo: ()) {
- // format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22)
-::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295
+ // format_args/*+tokenids*/!("{} {} {}"#1,#2 format_args#3!#4("{}"#6,#7 0#8),#9 foo#10,#11 identity#12!#13(10#15),#16 "bar"#17)
+builtin#4294967295 ##4294967295format_args#4294967295 (#0"{} {} {}"#1,#2 format_args#3!#4(#5"{}"#6,#7 0#8)#5,#9 foo#10,#11 identity#12!#13(#1410#15)#14,#16 "bar"#17)#0
}
"##]],
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 97554f9..b416f45 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -929,8 +929,8 @@
macro_rules! format_args {}
fn main() {
- /* error: expected field name or number *//* parse error: expected field name or number */
-::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]);
+ /* parse error: expected field name or number */
+builtin #format_args ("{}", line.1.);
}
"##]],
@@ -956,19 +956,15 @@
macro_rules! format_args {}
fn main() {
- /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */
+ /* parse error: expected COMMA */
/* parse error: expected R_BRACK */
/* parse error: expected COMMA */
/* parse error: expected COMMA */
/* parse error: expected expression */
/* parse error: expected R_PAREN */
-/* parse error: expected R_PAREN */
/* parse error: expected expression, item or let statement */
/* parse error: expected expression, item or let statement */
-/* parse error: expected expression, item or let statement */
-/* parse error: expected expression, item or let statement */
-/* parse error: expected expression, item or let statement */
-::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
+builtin #format_args ("{}", &[0 2]);
}
"##]],
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index f211041..9a9fa0e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -93,7 +93,7 @@
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
_c: Count<Self>,
- /// When this is a block def map, this will hold the block id of the the block and module that
+ /// When this is a block def map, this will hold the block id of the block and module that
/// contains this block.
block: Option<BlockInfo>,
/// The modules and their data declared in this crate.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index e9e71a8..2d45861 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -1828,7 +1828,11 @@
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
// `#[macro_use]` (without any paths) found, forget collected names and just import
// all visible macros.
- self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id));
+ self.def_collector.import_macros_from_extern_crate(
+ target_crate,
+ None,
+ Some(extern_crate_id),
+ );
return;
};
for path in paths {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index 06530cc..3894172 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -47,7 +47,7 @@
},
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
/// links via a normal path since they might be private and not accessible in the usage place.
- LangItem(LangItemTarget),
+ LangItem(LangItemTarget, Option<Name>),
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -122,33 +122,40 @@
pub fn kind(&self) -> &PathKind {
match self {
Path::Normal { mod_path, .. } => &mod_path.kind,
- Path::LangItem(_) => &PathKind::Abs,
+ Path::LangItem(..) => &PathKind::Abs,
}
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
match self {
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
pub fn segments(&self) -> PathSegments<'_> {
- let Path::Normal { mod_path, generic_args, .. } = self else {
- return PathSegments { segments: &[], generic_args: None };
- };
- let s =
- PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
- if let Some(generic_args) = s.generic_args {
- assert_eq!(s.segments.len(), generic_args.len());
+ match self {
+ Path::Normal { mod_path, generic_args, .. } => {
+ let s = PathSegments {
+ segments: mod_path.segments(),
+ generic_args: generic_args.as_deref(),
+ };
+ if let Some(generic_args) = s.generic_args {
+ assert_eq!(s.segments.len(), generic_args.len());
+ }
+ s
+ }
+ Path::LangItem(_, seg) => PathSegments {
+ segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
+ generic_args: None,
+ },
}
- s
}
pub fn mod_path(&self) -> Option<&ModPath> {
match self {
Path::Normal { mod_path, .. } => Some(&mod_path),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 11d58a6..f4f5541 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,18 +2,54 @@
use std::fmt::{self, Write};
-use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
+use hir_expand::mod_path::PathKind;
use intern::Interned;
use itertools::Itertools;
use crate::{
+ db::DefDatabase,
+ lang_item::LangItemTarget,
path::{GenericArg, GenericArgs, Path},
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
- if let Path::LangItem(it) = path {
- return write!(buf, "$lang_item::{it:?}");
+pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+ if let Path::LangItem(it, s) = path {
+ write!(buf, "builtin#lang(")?;
+ match *it {
+ LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
+ LangItemTarget::EnumId(it) => {
+ write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Function(it) => {
+ write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Static(it) => {
+ write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Struct(it) => {
+ write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Union(it) => {
+ write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::TypeAlias(it) => {
+ write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Trait(it) => {
+ write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::EnumVariant(it) => write!(
+ buf,
+ "{}",
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast())
+ )?,
+ }
+
+ if let Some(s) = s {
+ write!(buf, "::{}", s.display(db.upcast()))?;
+ }
+ return write!(buf, ")");
}
match path.type_anchor() {
Some(anchor) => {
@@ -44,7 +80,7 @@
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name.display(db))?;
+ write!(buf, "{}", segment.name.display(db.upcast()))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
print_generic_args(db, generics, buf)?;
@@ -57,7 +93,7 @@
}
pub(crate) fn print_generic_args(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
generics: &GenericArgs,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -83,7 +119,7 @@
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name.display(db))?;
+ write!(buf, "{}", binding.name.display(db.upcast()))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
print_type_bounds(db, &binding.bounds, buf)?;
@@ -97,19 +133,19 @@
}
pub(crate) fn print_generic_arg(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
arg: &GenericArg,
buf: &mut dyn Write,
) -> fmt::Result {
match arg {
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
- GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
}
}
pub(crate) fn print_type_ref(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
type_ref: &TypeRef,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -143,7 +179,7 @@
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name.display(db))?;
+ write!(buf, "{} ", lt.name.display(db.upcast()))?;
}
write!(buf, "{mtbl}")?;
print_type_ref(db, pointee, buf)?;
@@ -151,7 +187,7 @@
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
print_type_ref(db, elem, buf)?;
- write!(buf, "; {}]", len.display(db))?;
+ write!(buf, "; {}]", len.display(db.upcast()))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
@@ -198,7 +234,7 @@
}
pub(crate) fn print_type_bounds(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -216,10 +252,14 @@
print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
- write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+ write!(
+ buf,
+ "for<{}> ",
+ lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
+ )?;
print_path(db, path, buf)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 2f91870..50da9ed 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -156,22 +156,19 @@
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
- return Some((
- match *l {
- LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
- LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
- LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::Trait(it) => TypeNs::TraitId(it),
- LangItemTarget::Function(_)
- | LangItemTarget::ImplDef(_)
- | LangItemTarget::Static(_) => return None,
- },
- None,
- None,
- ))
+ Path::LangItem(l, seg) => {
+ let type_ns = match *l {
+ LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+ LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+ LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+ LangItemTarget::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ };
+ return Some((type_ns, seg.as_ref().map(|_| 1), None));
}
};
let first_name = path.segments().first()?;
@@ -256,7 +253,7 @@
) -> Option<ResolveValueResult> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
+ Path::LangItem(l, None) => {
return Some(ResolveValueResult::ValueNs(
match *l {
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
@@ -272,6 +269,20 @@
None,
))
}
+ Path::LangItem(l, Some(_)) => {
+ let type_ns = match *l {
+ LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+ LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+ LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+ LangItemTarget::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ };
+ return Some(ResolveValueResult::Partial(type_ns, 1, None));
+ }
};
let n_segments = path.segments().len();
let tmp = name![self];
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index 95c6baf..30b19b6 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,13 +1,9 @@
//! Builtin macro
-use std::mem;
-
-use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
-use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
@@ -97,11 +93,11 @@
(unreachable, Unreachable) => unreachable_expand,
(log_syntax, LogSyntax) => log_syntax_expand,
(trace_macros, TraceMacros) => trace_macros_expand,
-
- EAGER:
(format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand,
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
+
+ EAGER:
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -247,151 +243,15 @@
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
- end_string: &str,
+ // FIXME: Make use of this so that mir interpretation works properly
+ _end_string: &str,
) -> ExpandResult<tt::Subtree> {
- let args = parse_exprs_with_sep(tt, ',');
-
- let expand_error =
- ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
-
- let mut key_args = FxHashMap::default();
- let mut args = args.into_iter().filter_map(|mut arg| {
- // Remove `key =`.
- if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
- {
- // but not with `==`
- if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
- {
- let key = arg.token_trees.drain(..2).next().unwrap();
- key_args.insert(key.to_string(), arg);
- return None;
- }
- }
- Some(arg)
- }).collect::<Vec<_>>().into_iter();
- // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
- let Some(format_subtree) = args.next() else {
- return expand_error;
- };
- let format_string = (|| {
- let token_tree = format_subtree.token_trees.get(0)?;
- match token_tree {
- tt::TokenTree::Leaf(l) => match l {
- tt::Leaf::Literal(l) => {
- if let Some(mut text) = l.text.strip_prefix('r') {
- let mut raw_sharps = String::new();
- while let Some(t) = text.strip_prefix('#') {
- text = t;
- raw_sharps.push('#');
- }
- text =
- text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
- Some((text, l.span, Some(raw_sharps)))
- } else {
- let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
- let span = l.span;
- Some((text, span, None))
- }
- }
- _ => None,
- },
- tt::TokenTree::Subtree(_) => None,
- }
- })();
- let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
- return expand_error;
- };
- let mut format_iter = format_string.chars().peekable();
- let mut parts = vec![];
- let mut last_part = String::new();
- let mut arg_tts = vec![];
- let mut err = None;
- while let Some(c) = format_iter.next() {
- // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
- match c {
- '{' => {
- if format_iter.peek() == Some(&'{') {
- format_iter.next();
- last_part.push('{');
- continue;
- }
- let mut argument = String::new();
- while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
- argument.push(match format_iter.next() {
- Some(c) => c,
- None => return expand_error,
- });
- }
- let format_spec = match format_iter.next().unwrap() {
- '}' => "".to_owned(),
- ':' => {
- let mut s = String::new();
- while let Some(c) = format_iter.next() {
- if c == '}' {
- break;
- }
- s.push(c);
- }
- s
- }
- _ => unreachable!(),
- };
- parts.push(mem::take(&mut last_part));
- let arg_tree = if argument.is_empty() {
- match args.next() {
- Some(it) => it,
- None => {
- err = Some(mbe::ExpandError::NoMatchingRule.into());
- tt::Subtree::empty()
- }
- }
- } else if let Some(tree) = key_args.get(&argument) {
- tree.clone()
- } else {
- // FIXME: we should pick the related substring of the `_format_string_span` as the span. You
- // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
- let ident = Ident::new(argument, tt::TokenId::unspecified());
- quote!(#ident)
- };
- let formatter = match &*format_spec {
- "?" => quote!(::core::fmt::Debug::fmt),
- "" => quote!(::core::fmt::Display::fmt),
- _ => {
- // FIXME: implement the rest and return expand error here
- quote!(::core::fmt::Display::fmt)
- }
- };
- arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), });
- }
- '}' => {
- if format_iter.peek() == Some(&'}') {
- format_iter.next();
- last_part.push('}');
- } else {
- return expand_error;
- }
- }
- _ => last_part.push(c),
- }
- }
- last_part += end_string;
- if !last_part.is_empty() {
- parts.push(last_part);
- }
- let part_tts = parts.into_iter().map(|it| {
- let text = if let Some(raw) = &raw_sharps {
- format!("r{raw}\"{}\"{raw}", it).into()
- } else {
- format!("\"{}\"", it).into()
- };
- let l = tt::Literal { span: tt::TokenId::unspecified(), text };
- quote!(#l ,)
+ let pound = quote! {@PUNCT '#'};
+ let mut tt = tt.clone();
+ tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
+ return ExpandResult::ok(quote! {
+ builtin #pound format_args #tt
});
- let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
- let expanded = quote! {
- ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
- };
- ExpandResult { value: expanded, err }
}
fn asm_expand(
@@ -415,10 +275,12 @@
}
}
- let expanded = quote! {{
- ##literals
- loop {}
- }};
+ let pound = quote! {@PUNCT '#'};
+ let expanded = quote! {
+ builtin #pound asm (
+ {##literals}
+ )
+ };
ExpandResult::ok(expanded)
}
@@ -692,7 +554,7 @@
arg_id: MacroCallId,
) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
let loc = db.lookup_intern_macro_call(arg_id);
- let Some(EagerCallInfo { arg,arg_id, .. }) = loc.eager.as_deref() else {
+ let Some(EagerCallInfo { arg, arg_id, .. }) = loc.eager.as_deref() else {
panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
};
let path = parse_string(&arg.0)?;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index ade4a59..ca65db1 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -242,7 +242,7 @@
krate,
call_site: None,
def_site: None,
- }
+ };
};
let def_site = info.attr_input_or_mac_def_start.map(|it| db.hygiene_frame(it.file_id));
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 7c179c0..a876f48 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -54,6 +54,12 @@
Name(Repr::Text(text))
}
+ // FIXME: See above, unfortunately some places really need this right now
+ #[doc(hidden)]
+ pub const fn new_text_dont_use(text: SmolStr) -> Name {
+ Name(Repr::Text(text))
+ }
+
pub fn new_tuple_field(idx: usize) -> Name {
Name(Repr::TupleField(idx))
}
@@ -302,6 +308,16 @@
rust_2018,
rust_2021,
v1,
+ new_display,
+ new_debug,
+ new_lower_exp,
+ new_upper_exp,
+ new_octal,
+ new_pointer,
+ new_binary,
+ new_lower_hex,
+ new_upper_hex,
+ from_usize,
// Components of known path (type name)
Iterator,
IntoIterator,
@@ -327,6 +343,13 @@
Not,
None,
Index,
+ Left,
+ Right,
+ Center,
+ Unknown,
+ Is,
+ Param,
+ Implied,
// Components of known path (function name)
filter_map,
next,
@@ -335,6 +358,8 @@
is_empty,
as_str,
new,
+ new_v1_formatted,
+ none,
// Builtin macros
asm,
assert,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index abc19d6..b95ae05 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -32,7 +32,8 @@
triomphe.workspace = true
nohash-hasher.workspace = true
typed-arena = "2.0.1"
-rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
+
+rustc_index.workspace = true
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 2855f78..44a4ac2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -499,24 +499,26 @@
r#"
//- minicore: coerce_unsized, index, slice
extern "rust-intrinsic" {
- pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+ pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
}
- const GOAL: u8 = unsafe {
- let ar: &[(u8, u8, u8)] = &[
+ const GOAL: i32 = unsafe {
+ let ar: &[(i32, i32, i32)] = &[
(10, 11, 12),
(20, 21, 22),
(30, 31, 32),
(40, 41, 42),
(50, 51, 52),
];
- let ar: *const [(u8, u8, u8)] = ar;
- let ar = ar as *const (u8, u8, u8);
- let element = *offset(ar, 2);
- element.1
+ let ar: *const [(i32, i32, i32)] = ar;
+ let ar = ar as *const (i32, i32, i32);
+ let element3 = *offset(ar, 2usize);
+ let element4 = *arith_offset(ar, 3);
+ element3.1 * 100 + element4.0
};
"#,
- 31,
+ 3140,
);
}
@@ -585,6 +587,24 @@
}
#[test]
+fn write_via_move() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ fn write_via_move<T>(ptr: *mut T, value: T);
+ }
+
+ const GOAL: i32 = unsafe {
+ let mut x = 2;
+ write_via_move(&mut x, 100);
+ x
+ };
+ "#,
+ 100,
+ );
+}
+
+#[test]
fn copy() {
check_number(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index a94a962..36d69ed 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -163,25 +163,56 @@
|| allows.contains(allow::NONSTANDARD_STYLE)
})
};
-
- is_allowed(id)
- // go upwards one step or give up
- || match id {
- AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()),
- AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()),
- AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
- AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
- AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
- AttrDefId::ExternCrateId(id) => Some(id.lookup(self.db.upcast()).container.into()),
- AttrDefId::UseId(id) => Some(id.lookup(self.db.upcast()).container.into()),
+ let db = self.db.upcast();
+ let file_id_is_derive = || {
+ match id {
+ AttrDefId::ModuleId(m) => {
+ m.def_map(db)[m.local_id].origin.file_id().map(Into::into)
+ }
+ AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()),
+ AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()),
+ AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()),
+ AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()),
+ AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()),
+ AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()),
+ AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()),
+ AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()),
+ AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()),
// These warnings should not explore macro definitions at all
AttrDefId::MacroId(_) => None,
AttrDefId::AdtId(aid) => match aid {
- AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
- AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()),
+ AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()),
+ AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()),
+ // Unions aren't yet supported
+ AdtId::UnionId(_) => None,
+ },
+ AttrDefId::FieldId(_) => None,
+ AttrDefId::EnumVariantId(_) => None,
+ AttrDefId::TypeAliasId(_) => None,
+ AttrDefId::GenericParamId(_) => None,
+ }
+ .map_or(false, |file_id| {
+ file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())
+ })
+ };
+
+ let parent = || {
+ match id {
+ AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()),
+ AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()),
+ AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()),
+ AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()),
+ AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()),
+ AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()),
+ AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()),
+ AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()),
+ AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()),
+ AttrDefId::UseId(id) => Some(id.lookup(db).container.into()),
+ // These warnings should not explore macro definitions at all
+ AttrDefId::MacroId(_) => None,
+ AttrDefId::AdtId(aid) => match aid {
+ AdtId::StructId(sid) => Some(sid.lookup(db).container.into()),
+ AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()),
// Unions aren't yet supported
AdtId::UnionId(_) => None,
},
@@ -191,6 +222,12 @@
AttrDefId::GenericParamId(_) => None,
}
.is_some_and(|mid| self.allowed(mid, allow_name, true))
+ };
+ is_allowed(id)
+ // FIXME: this is a hack to avoid false positives in derive macros currently
+ || file_id_is_derive()
+ // go upwards one step or give up
+ || parent()
}
fn validate_func(&mut self, func: FunctionId) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 0fb4934..78d3c66 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -194,7 +194,8 @@
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
NoSuchField {
- expr: ExprId,
+ field: ExprOrPatId,
+ private: bool,
},
PrivateField {
expr: ExprId,
@@ -228,6 +229,11 @@
expected: usize,
found: usize,
},
+ MismatchedTupleStructPatArgCount {
+ pat: ExprOrPatId,
+ expected: usize,
+ found: usize,
+ },
ExpectedFunction {
call_expr: ExprId,
found: Ty,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index 9e1c74b..a116d44 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -39,8 +39,14 @@
}
fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool {
- let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { return false; };
- let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { return false; };
- let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { return false; };
+ let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else {
+ return false;
+ };
+ let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else {
+ return false;
+ };
+ let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else {
+ return false;
+ };
table.coerce(expr_elt_ty, cast_inner_ty).is_ok()
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 23efe61..13d6b56 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -452,6 +452,8 @@
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
+ Expr::OffsetOf(_) => (),
+ Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);
self.consume_expr(*then_branch);
@@ -467,13 +469,13 @@
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
if let Some(else_branch) = else_branch {
self.consume_expr(*else_branch);
- if let Some(initializer) = initializer {
- self.consume_expr(*initializer);
- }
- return;
}
if let Some(initializer) = initializer {
- self.walk_expr(*initializer);
+ if else_branch.is_some() {
+ self.consume_expr(*initializer);
+ } else {
+ self.walk_expr(*initializer);
+ }
if let Some(place) = self.place_of_expr(*initializer) {
self.consume_with_pat(place, *pat);
}
@@ -620,6 +622,7 @@
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
self.consume_exprs(exprs.iter().copied())
}
+
Expr::Missing
| Expr::Continue { .. }
| Expr::Path(_)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 8b35214..0c3c725 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -514,9 +514,6 @@
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
- if let Some(variant) = def_id {
- self.write_variant_resolution(tgt_expr.into(), variant);
- }
if let Some(t) = expected.only_has_type(&mut self.table) {
self.unify(&ty, &t);
@@ -526,26 +523,56 @@
.as_adt()
.map(|(_, s)| s.clone())
.unwrap_or_else(|| Substitution::empty(Interner));
- let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
- let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
- for field in fields.iter() {
- let field_def =
- variant_data.as_ref().and_then(|it| match it.field(&field.name) {
- Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }),
- None => {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- expr: field.expr,
- });
- None
- }
- });
- let field_ty = field_def.map_or(self.err_ty(), |it| {
- field_types[it.local_id].clone().substitute(Interner, &substs)
- });
- // Field type might have some unknown types
- // FIXME: we may want to emit a single type variable for all instance of type fields?
- let field_ty = self.insert_type_vars(field_ty);
- self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
+ if let Some(variant) = def_id {
+ self.write_variant_resolution(tgt_expr.into(), variant);
+ }
+ match def_id {
+ _ if fields.is_empty() => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
+ for field in fields.iter() {
+ let field_def = {
+ match variant_data.field(&field.name) {
+ Some(local_id) => {
+ if !visibilities[local_id].is_visible_from(
+ self.db.upcast(),
+ self.resolver.module(),
+ ) {
+ self.push_diagnostic(
+ InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: true,
+ },
+ );
+ }
+ Some(local_id)
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: false,
+ });
+ None
+ }
+ }
+ };
+ let field_ty = field_def.map_or(self.err_ty(), |it| {
+ field_types[it].clone().substitute(Interner, &substs)
+ });
+
+ // Field type might have some unknown types
+ // FIXME: we may want to emit a single type variable for all instance of type fields?
+ let field_ty = self.insert_type_vars(field_ty);
+ self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
+ }
+ }
+ None => {
+ for field in fields.iter() {
+ self.infer_expr_coerce(field.expr, &Expectation::None);
+ }
+ }
}
if let Some(expr) = spread {
self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
@@ -843,6 +870,11 @@
});
expected
}
+ Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
+ Expr::InlineAsm(it) => {
+ self.infer_expr_no_expect(it.e);
+ self.result.standard_types.unit.clone()
+ }
};
// use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);
@@ -1122,7 +1154,7 @@
Expr::Underscore => rhs_ty.clone(),
_ => {
// `lhs` is a place expression, a unit struct, or an enum variant.
- let lhs_ty = self.infer_expr(lhs, &Expectation::none());
+ let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none());
// This is the only branch where this function may coerce any type.
// We are returning early to avoid the unifiability check below.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 396ca00..b8a1af9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -35,6 +35,8 @@
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
match &self.body[tgt_expr] {
Expr::Missing => (),
+ Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
+ Expr::OffsetOf(_) => (),
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
self.infer_mut_expr(then_branch, Mutability::Not);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 5da0ab7..4e28ec0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -15,7 +15,8 @@
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability,
primitive::UintTy,
- static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
+ static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt,
+ TyKind,
};
/// Used to generalize patterns and assignee expressions.
@@ -74,29 +75,68 @@
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
}
+ if let Some(var) = &var_data {
+ let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne };
+
+ if cmp(&subs.len(), &var.fields().len()) {
+ self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
+ pat: id.into(),
+ expected: var.fields().len(),
+ found: subs.len(),
+ });
+ }
+ }
+
self.unify(&ty, expected);
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
- let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
- let (pre, post) = match ellipsis {
- Some(idx) => subs.split_at(idx),
- None => (subs, &[][..]),
- };
- let post_idx_offset = field_tys.iter().count().saturating_sub(post.len());
+ match def {
+ _ if subs.len() == 0 => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
- let pre_iter = pre.iter().enumerate();
- let post_iter = (post_idx_offset..).zip(post.iter());
- for (i, &subpat) in pre_iter.chain(post_iter) {
- let expected_ty = var_data
- .as_ref()
- .and_then(|d| d.field(&Name::new_tuple_field(i)))
- .map_or(self.err_ty(), |field| {
- field_tys[field].clone().substitute(Interner, &substs)
- });
- let expected_ty = self.normalize_associated_types_in(expected_ty);
- T::infer(self, subpat, &expected_ty, default_bm);
+ let (pre, post) = match ellipsis {
+ Some(idx) => subs.split_at(idx),
+ None => (subs, &[][..]),
+ };
+ let post_idx_offset = field_types.iter().count().saturating_sub(post.len());
+
+ let pre_iter = pre.iter().enumerate();
+ let post_iter = (post_idx_offset..).zip(post.iter());
+
+ for (i, &subpat) in pre_iter.chain(post_iter) {
+ let field_def = {
+ match variant_data.field(&Name::new_tuple_field(i)) {
+ Some(local_id) => {
+ if !visibilities[local_id]
+ .is_visible_from(self.db.upcast(), self.resolver.module())
+ {
+ // FIXME(DIAGNOSE): private tuple field
+ }
+ Some(local_id)
+ }
+ None => None,
+ }
+ };
+
+ let expected_ty = field_def.map_or(self.err_ty(), |f| {
+ field_types[f].clone().substitute(Interner, &substs)
+ });
+ let expected_ty = self.normalize_associated_types_in(expected_ty);
+
+ T::infer(self, subpat, &expected_ty, default_bm);
+ }
+ }
+ None => {
+ let err_ty = self.err_ty();
+ for &inner in subs {
+ T::infer(self, inner, &err_ty, default_bm);
+ }
+ }
}
ty
@@ -109,7 +149,7 @@
expected: &Ty,
default_bm: T::BindingMode,
id: T,
- subs: impl Iterator<Item = (Name, T)>,
+ subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator,
) -> Ty {
let (ty, def) = self.resolve_variant(path, false);
if let Some(variant) = def {
@@ -121,17 +161,51 @@
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
- let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
- let var_data = def.map(|it| it.variant_data(self.db.upcast()));
+ match def {
+ _ if subs.len() == 0 => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
- for (name, inner) in subs {
- let expected_ty = var_data
- .as_ref()
- .and_then(|it| it.field(&name))
- .map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs));
- let expected_ty = self.normalize_associated_types_in(expected_ty);
+ for (name, inner) in subs {
+ let field_def = {
+ match variant_data.field(&name) {
+ Some(local_id) => {
+ if !visibilities[local_id]
+ .is_visible_from(self.db.upcast(), self.resolver.module())
+ {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: inner.into(),
+ private: true,
+ });
+ }
+ Some(local_id)
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: inner.into(),
+ private: false,
+ });
+ None
+ }
+ }
+ };
- T::infer(self, inner, &expected_ty, default_bm);
+ let expected_ty = field_def.map_or(self.err_ty(), |f| {
+ field_types[f].clone().substitute(Interner, &substs)
+ });
+ let expected_ty = self.normalize_associated_types_in(expected_ty);
+
+ T::infer(self, inner, &expected_ty, default_bm);
+ }
+ }
+ None => {
+ let err_ty = self.err_ty();
+ for (_, inner) in subs {
+ T::infer(self, inner, &err_ty, default_bm);
+ }
+ }
}
ty
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 2a51c84..c6bbf2f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -178,13 +178,30 @@
remaining_index: usize,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- assert!(remaining_index < path.segments().len());
// there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref.
- let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
- let remaining_segments = path.segments().skip(remaining_index);
+ let _d;
+ let (resolved_segment, remaining_segments) = match path {
+ Path::Normal { .. } => {
+ assert!(remaining_index < path.segments().len());
+ (
+ path.segments().get(remaining_index - 1).unwrap(),
+ path.segments().skip(remaining_index),
+ )
+ }
+ Path::LangItem(..) => (
+ PathSegment {
+ name: {
+ _d = hir_expand::name::known::Unknown;
+ &_d
+ },
+ args_and_bindings: None,
+ },
+ path.segments(),
+ ),
+ };
let is_before_last = remaining_segments.len() == 1;
match (def, is_before_last) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index b15339d..1a6106c0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -24,7 +24,7 @@
macro_rules! user_error {
($it: expr) => {
- return Err(LayoutError::UserError(format!($it)))
+ return Err(LayoutError::UserError(format!($it).into()))
};
}
@@ -50,7 +50,7 @@
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutError {
- UserError(String),
+ UserError(Box<str>),
SizeOverflow,
TargetLayoutNotAvailable,
HasPlaceholder,
@@ -109,7 +109,8 @@
// * the homogeneous field type and the number of fields.
let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) {
// Extract the number of elements from the layout of the array field:
- let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else {
+ let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields
+ else {
user_error!("Array with non array layout");
};
@@ -233,9 +234,9 @@
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
}
TyKind::Array(element, count) => {
- let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(
- "unevaluated or mistyped const generic parameter".to_string(),
- ))? as u64;
+ let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from(
+ "unevaluated or mistyped const generic parameter",
+ )))? as u64;
let element = db.layout_of_ty(element.clone(), trait_env.clone())?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index 1c92e80..85ef649 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -163,7 +163,7 @@
return Err(LayoutError::UserError(
"Integer::repr_discr: `#[repr]` hint too small for \
discriminant range of enum "
- .to_string(),
+ .into(),
));
}
return Ok((discr, ity.is_signed()));
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 333ad47..ffdbb9d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -212,14 +212,14 @@
}
check_fail(
r#"struct Goal(Goal);"#,
- LayoutError::UserError("infinite sized recursive type".to_string()),
+ LayoutError::UserError("infinite sized recursive type".into()),
);
check_fail(
r#"
struct Foo<T>(Foo<T>);
struct Goal(Foo<i32>);
"#,
- LayoutError::UserError("infinite sized recursive type".to_string()),
+ LayoutError::UserError("infinite sized recursive type".into()),
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
index 576e7f3..bbe855a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
@@ -255,3 +255,17 @@
}
}
}
+
+#[test]
+fn regression_15623() {
+ size_and_align_expr! {
+ let a = 2;
+ let b = 3;
+ let c = 5;
+ move || {
+ let 0 = a else { return b; };
+ let y = c;
+ y
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 9be083d..e953058 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -1,6 +1,6 @@
//! MIR definitions and implementation
-use std::{fmt::Display, iter};
+use std::{collections::hash_map::Entry, fmt::Display, iter};
use crate::{
consteval::usize_const,
@@ -37,6 +37,7 @@
monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
monomorphized_mir_body_query, monomorphized_mir_body_recover,
};
+use rustc_hash::FxHashMap;
use smallvec::{smallvec, SmallVec};
use stdx::{impl_from, never};
use triomphe::Arc;
@@ -165,8 +166,8 @@
TyKind::Adt(_, subst) => {
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
}
- _ => {
- never!("Only adt has field");
+ ty => {
+ never!("Only adt has field, found {:?}", ty);
return TyKind::Error.intern(Interner);
}
},
@@ -223,35 +224,93 @@
type PlaceElem = ProjectionElem<LocalId, Ty>;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ProjectionId(u32);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ProjectionStore {
+ id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
+ proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
+}
+
+impl Default for ProjectionStore {
+ fn default() -> Self {
+ let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
+ // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
+ this.intern(Box::new([]));
+ this
+ }
+}
+
+impl ProjectionStore {
+ fn shrink_to_fit(&mut self) {
+ self.id_to_proj.shrink_to_fit();
+ self.proj_to_id.shrink_to_fit();
+ }
+
+ fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
+ self.proj_to_id.get(projection).copied()
+ }
+
+ fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
+ let new_id = ProjectionId(self.proj_to_id.len() as u32);
+ match self.proj_to_id.entry(projection) {
+ Entry::Occupied(id) => *id.get(),
+ Entry::Vacant(e) => {
+ let key_clone = e.key().clone();
+ e.insert(new_id);
+ self.id_to_proj.insert(new_id, key_clone);
+ new_id
+ }
+ }
+ }
+}
+
+impl ProjectionId {
+ const EMPTY: ProjectionId = ProjectionId(0);
+
+ fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
+ store.id_to_proj.get(&self).unwrap()
+ }
+
+ fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
+ let mut current = self.lookup(store).to_vec();
+ current.push(projection);
+ store.intern(current.into())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Place {
pub local: LocalId,
- pub projection: Box<[PlaceElem]>,
+ pub projection: ProjectionId,
}
impl Place {
- fn is_parent(&self, child: &Place) -> bool {
- self.local == child.local && child.projection.starts_with(&self.projection)
+ fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
+ self.local == child.local
+ && child.projection.lookup(store).starts_with(&self.projection.lookup(store))
}
/// The place itself is not included
- fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
- (0..self.projection.len())
- .map(|x| &self.projection[0..x])
- .map(|x| Place { local: self.local, projection: x.to_vec().into() })
+ fn iterate_over_parents<'a>(
+ &'a self,
+ store: &'a ProjectionStore,
+ ) -> impl Iterator<Item = Place> + 'a {
+ let projection = self.projection.lookup(store);
+ (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
+ Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
+ })
}
- fn project(&self, projection: PlaceElem) -> Place {
- Place {
- local: self.local,
- projection: self.projection.iter().cloned().chain([projection]).collect(),
- }
+ fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
+ Place { local: self.local, projection: self.projection.project(projection, store) }
}
}
impl From<LocalId> for Place {
fn from(local: LocalId) -> Self {
- Self { local, projection: vec![].into() }
+ Self { local, projection: ProjectionId::EMPTY }
}
}
@@ -997,6 +1056,7 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MirBody {
+ pub projection_store: ProjectionStore,
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
@@ -1009,11 +1069,15 @@
}
impl MirBody {
- fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
- fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+ fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
+ fn for_operand(
+ op: &mut Operand,
+ f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
+ store: &mut ProjectionStore,
+ ) {
match op {
Operand::Copy(p) | Operand::Move(p) => {
- f(p);
+ f(p, store);
}
Operand::Constant(_) | Operand::Static(_) => (),
}
@@ -1022,30 +1086,30 @@
for statement in &mut block.statements {
match &mut statement.kind {
StatementKind::Assign(p, r) => {
- f(p);
+ f(p, &mut self.projection_store);
match r {
Rvalue::ShallowInitBoxWithAlloc(_) => (),
Rvalue::ShallowInitBox(o, _)
| Rvalue::UnaryOp(_, o)
| Rvalue::Cast(_, o, _)
| Rvalue::Repeat(o, _)
- | Rvalue::Use(o) => for_operand(o, &mut f),
+ | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store),
Rvalue::CopyForDeref(p)
| Rvalue::Discriminant(p)
| Rvalue::Len(p)
- | Rvalue::Ref(_, p) => f(p),
+ | Rvalue::Ref(_, p) => f(p, &mut self.projection_store),
Rvalue::CheckedBinaryOp(_, o1, o2) => {
- for_operand(o1, &mut f);
- for_operand(o2, &mut f);
+ for_operand(o1, &mut f, &mut self.projection_store);
+ for_operand(o2, &mut f, &mut self.projection_store);
}
Rvalue::Aggregate(_, ops) => {
for op in ops.iter_mut() {
- for_operand(op, &mut f);
+ for_operand(op, &mut f, &mut self.projection_store);
}
}
}
}
- StatementKind::Deinit(p) => f(p),
+ StatementKind::Deinit(p) => f(p, &mut self.projection_store),
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Nop => (),
@@ -1053,7 +1117,9 @@
}
match &mut block.terminator {
Some(x) => match &mut x.kind {
- TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+ TerminatorKind::SwitchInt { discr, .. } => {
+ for_operand(discr, &mut f, &mut self.projection_store)
+ }
TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
@@ -1063,23 +1129,24 @@
| TerminatorKind::Return
| TerminatorKind::Unreachable => (),
TerminatorKind::Drop { place, .. } => {
- f(place);
+ f(place, &mut self.projection_store);
}
TerminatorKind::DropAndReplace { place, value, .. } => {
- f(place);
- for_operand(value, &mut f);
+ f(place, &mut self.projection_store);
+ for_operand(value, &mut f, &mut self.projection_store);
}
TerminatorKind::Call { func, args, destination, .. } => {
- for_operand(func, &mut f);
- args.iter_mut().for_each(|x| for_operand(x, &mut f));
- f(destination);
+ for_operand(func, &mut f, &mut self.projection_store);
+ args.iter_mut()
+ .for_each(|x| for_operand(x, &mut f, &mut self.projection_store));
+ f(destination, &mut self.projection_store);
}
TerminatorKind::Assert { cond, .. } => {
- for_operand(cond, &mut f);
+ for_operand(cond, &mut f, &mut self.projection_store);
}
TerminatorKind::Yield { value, resume_arg, .. } => {
- for_operand(value, &mut f);
- f(resume_arg);
+ for_operand(value, &mut f, &mut self.projection_store);
+ f(resume_arg, &mut self.projection_store);
}
},
None => (),
@@ -1096,7 +1163,9 @@
binding_locals,
param_locals,
closures,
+ projection_store,
} = self;
+ projection_store.shrink_to_fit();
basic_blocks.shrink_to_fit();
locals.shrink_to_fit();
binding_locals.shrink_to_fit();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index c70d7f6..41fb129 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -42,30 +42,27 @@
fn all_mir_bodies(
db: &dyn HirDatabase,
def: DefWithBodyId,
-) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ mut cb: impl FnMut(Arc<MirBody>),
+) -> Result<(), MirLowerError> {
fn for_closure(
db: &dyn HirDatabase,
c: ClosureId,
- ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ cb: &mut impl FnMut(Arc<MirBody>),
+ ) -> Result<(), MirLowerError> {
match db.mir_body_for_closure(c) {
Ok(body) => {
- let closures = body.closures.clone();
- Box::new(
- iter::once(Ok(body))
- .chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
- )
+ cb(body.clone());
+ body.closures.iter().map(|&it| for_closure(db, it, cb)).collect()
}
- Err(e) => Box::new(iter::once(Err(e))),
+ Err(e) => Err(e),
}
}
match db.mir_body(def) {
Ok(body) => {
- let closures = body.closures.clone();
- Box::new(
- iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
- )
+ cb(body.clone());
+ body.closures.iter().map(|&it| for_closure(db, it, &mut cb)).collect()
}
- Err(e) => Box::new(iter::once(Err(e))),
+ Err(e) => Err(e),
}
}
@@ -74,17 +71,15 @@
def: DefWithBodyId,
) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query");
- let r = all_mir_bodies(db, def)
- .map(|body| {
- let body = body?;
- Ok(BorrowckResult {
- mutability_of_locals: mutability_of_locals(db, &body),
- moved_out_of_ref: moved_out_of_ref(db, &body),
- mir_body: body,
- })
- })
- .collect::<Result<Vec<_>, MirLowerError>>()?;
- Ok(r.into())
+ let mut res = vec![];
+ all_mir_bodies(db, def, |body| {
+ res.push(BorrowckResult {
+ mutability_of_locals: mutability_of_locals(db, &body),
+ moved_out_of_ref: moved_out_of_ref(db, &body),
+ mir_body: body,
+ });
+ })?;
+ Ok(res.into())
}
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
@@ -93,7 +88,7 @@
Operand::Copy(p) | Operand::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
let mut is_dereference_of_ref = false;
- for proj in &*p.projection {
+ for proj in p.projection.lookup(&body.projection_store) {
if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
is_dereference_of_ref = true;
}
@@ -125,6 +120,7 @@
Operand::Constant(_) | Operand::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
+ db.unwind_if_cancelled();
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(_, r) => match r {
@@ -183,6 +179,7 @@
None => (),
}
}
+ result.shrink_to_fit();
result
}
@@ -199,7 +196,7 @@
fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
let mut is_part_of = false;
let mut ty = body.locals[lvalue.local].ty.clone();
- for proj in lvalue.projection.iter() {
+ for proj in lvalue.projection.lookup(&body.projection_store).iter() {
match proj {
ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
ProjectionElem::Deref // It's direct in case of `Box<T>`
@@ -258,7 +255,7 @@
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(p, _) => {
- if p.projection.len() == 0 && p.local == l {
+ if p.projection.lookup(&body.projection_store).len() == 0 && p.local == l {
is_ever_initialized = true;
}
}
@@ -277,21 +274,37 @@
);
return;
};
- let targets = match &terminator.kind {
- TerminatorKind::Goto { target } => vec![*target],
- TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
+ let mut process = |target, is_ever_initialized| {
+ if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
+ result[target].insert(l, is_ever_initialized);
+ dfs(db, body, target, l, result);
+ }
+ };
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
+ TerminatorKind::SwitchInt { targets, .. } => {
+ targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
+ }
TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
- | TerminatorKind::Unreachable => vec![],
+ | TerminatorKind::Unreachable => (),
TerminatorKind::Call { target, cleanup, destination, .. } => {
- if destination.projection.len() == 0 && destination.local == l {
+ if destination.projection.lookup(&body.projection_store).len() == 0
+ && destination.local == l
+ {
is_ever_initialized = true;
}
- target.into_iter().chain(cleanup.into_iter()).copied().collect()
+ target
+ .into_iter()
+ .chain(cleanup.into_iter())
+ .for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::Drop { target, unwind, place: _ } => {
- Some(target).into_iter().chain(unwind.into_iter()).copied().collect()
+ iter::once(target)
+ .into_iter()
+ .chain(unwind.into_iter())
+ .for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Assert { .. }
@@ -300,13 +313,7 @@
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet");
- vec![]
- }
- };
- for target in targets {
- if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
- result[target].insert(l, is_ever_initialized);
- dfs(db, body, target, l, result);
+ ()
}
}
}
@@ -315,6 +322,7 @@
dfs(db, body, body.start_block, l, &mut result);
}
for l in body.locals.iter().map(|it| it.0) {
+ db.unwind_if_cancelled();
if !result[body.start_block].contains_idx(l) {
result[body.start_block].insert(l, false);
dfs(db, body, body.start_block, l, &mut result);
@@ -384,7 +392,7 @@
| TerminatorKind::Assert { .. }
| TerminatorKind::Yield { .. } => (),
TerminatorKind::Call { destination, .. } => {
- if destination.projection.len() == 0 {
+ if destination.projection.lookup(&body.projection_store).len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
push_mut_span(destination.local, MirSpan::Unknown);
} else {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 3944feb..4364e0d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -46,8 +46,8 @@
use super::{
return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError,
- MirSpan, Operand, Place, PlaceElem, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
- UnOp,
+ MirSpan, Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind,
+ TerminatorKind, UnOp,
};
mod shim;
@@ -215,9 +215,7 @@
}
fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> {
- // FIXME: this could be more efficient
- let bytes = &interval.get(memory)?.to_vec();
- memory.write_memory(self.addr, bytes)
+ memory.copy_from_interval(self.addr, interval)
}
fn slice(self, range: Range<usize>) -> Interval {
@@ -341,7 +339,7 @@
InvalidVTableId(usize),
CoerceUnsizedError(Ty),
LangItemNotFound(LangItem),
- BrokenLayout(Layout),
+ BrokenLayout(Box<Layout>),
}
impl MirEvalError {
@@ -410,7 +408,7 @@
err.pretty_print(f, db, span_formatter)?;
}
MirEvalError::ConstEvalError(name, err) => {
- MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print(
+ MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
f,
db,
span_formatter,
@@ -485,17 +483,18 @@
}
impl DropFlags {
- fn add_place(&mut self, p: Place) {
- if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) {
+ fn add_place(&mut self, p: Place, store: &ProjectionStore) {
+ if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) {
return;
}
- self.need_drop.retain(|it| !p.is_parent(it));
+ self.need_drop.retain(|it| !p.is_parent(it, store));
self.need_drop.insert(p);
}
- fn remove_place(&mut self, p: &Place) -> bool {
+ fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool {
// FIXME: replace parents with parts
- if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) {
+ if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(&it))
+ {
self.need_drop.remove(&parent);
return true;
}
@@ -656,7 +655,7 @@
let mut addr = locals.ptr[p.local].addr;
let mut ty: Ty = locals.body.locals[p.local].ty.clone();
let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
- for proj in &*p.projection {
+ for proj in p.projection.lookup(&locals.body.projection_store) {
let prev_ty = ty.clone();
ty = self.projected_ty(ty, proj.clone());
match proj {
@@ -837,7 +836,9 @@
let addr = self.place_addr(l, &locals)?;
let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
self.write_memory(addr, &result)?;
- locals.drop_flags.add_place(l.clone());
+ locals
+ .drop_flags
+ .add_place(l.clone(), &locals.body.projection_store);
}
StatementKind::Deinit(_) => not_supported!("de-init statement"),
StatementKind::StorageLive(_)
@@ -889,7 +890,9 @@
)?,
it => not_supported!("unknown function type {it:?}"),
};
- locals.drop_flags.add_place(destination.clone());
+ locals
+ .drop_flags
+ .add_place(destination.clone(), &locals.body.projection_store);
if let Some(stack_frame) = stack_frame {
self.code_stack.push(my_stack_frame);
current_block_idx = stack_frame.locals.body.start_block;
@@ -970,7 +973,7 @@
) -> Result<()> {
let mut remain_args = body.param_locals.len();
for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) {
- locals.drop_flags.add_place(l.into());
+ locals.drop_flags.add_place(l.into(), &locals.body.projection_store);
match value {
IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?,
IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?,
@@ -1629,7 +1632,7 @@
if let Some((offset, size, value)) = tag {
match result.get_mut(offset..offset + size) {
Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]),
- None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
+ None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))),
}
}
for (i, op) in values.enumerate() {
@@ -1637,7 +1640,7 @@
let op = op.get(&self)?;
match result.get_mut(offset..offset + op.len()) {
Some(it) => it.copy_from_slice(op),
- None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
+ None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))),
}
}
Ok(result)
@@ -1646,7 +1649,7 @@
fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> {
Ok(match it {
Operand::Copy(p) | Operand::Move(p) => {
- locals.drop_flags.remove_place(p);
+ locals.drop_flags.remove_place(p, &locals.body.projection_store);
self.eval_place(p, locals)?
}
Operand::Static(st) => {
@@ -1760,6 +1763,48 @@
Ok(())
}
+ fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> {
+ if r.size == 0 {
+ return Ok(());
+ }
+
+ let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_string());
+
+ match (addr, r.addr) {
+ (Stack(dst), Stack(src)) => {
+ if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
+ return Err(oob());
+ }
+ self.stack.copy_within(src..src + r.size, dst)
+ }
+ (Heap(dst), Heap(src)) => {
+ if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
+ return Err(oob());
+ }
+ self.heap.copy_within(src..src + r.size, dst)
+ }
+ (Stack(dst), Heap(src)) => {
+ self.stack
+ .get_mut(dst..dst + r.size)
+ .ok_or_else(oob)?
+ .copy_from_slice(self.heap.get(src..src + r.size).ok_or_else(oob)?);
+ }
+ (Heap(dst), Stack(src)) => {
+ self.heap
+ .get_mut(dst..dst + r.size)
+ .ok_or_else(oob)?
+ .copy_from_slice(self.stack.get(src..src + r.size).ok_or_else(oob)?);
+ }
+ _ => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "invalid memory write at address {addr:?}"
+ )))
+ }
+ }
+
+ Ok(())
+ }
+
fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<Option<(usize, usize)>> {
if let Some(layout) = self.layout_cache.borrow().get(ty) {
return Ok(layout
@@ -2468,7 +2513,7 @@
fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> {
let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
- if !locals.drop_flags.remove_place(place) {
+ if !locals.drop_flags.remove_place(place, &locals.body.projection_store) {
return Ok(());
}
let metadata = match metadata {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index 52943e9..803ef63 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -4,7 +4,10 @@
use std::cmp;
use chalk_ir::TyKind;
-use hir_def::resolver::HasResolver;
+use hir_def::{
+ builtin_type::{BuiltinInt, BuiltinUint},
+ resolver::HasResolver,
+};
use hir_expand::mod_path::ModPath;
use super::*;
@@ -300,21 +303,36 @@
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
PanicFmt => {
let message = (|| {
- let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
+ let resolver = self
+ .db
+ .crate_def_map(self.crate_id)
+ .crate_root()
+ .resolver(self.db.upcast());
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
self.db.upcast(),
- &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments(
- hir_expand::mod_path::PathKind::Abs,
- [name![std], name![fmt], name![format]].into_iter(),
- )),
+ &hir_def::path::Path::from_known_path_with_no_generic(
+ ModPath::from_segments(
+ hir_expand::mod_path::PathKind::Abs,
+ [name![std], name![fmt], name![format]].into_iter(),
+ ),
+ ),
) else {
not_supported!("std::fmt::format not found");
};
- let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") };
- let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?;
- let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
+ let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
+ not_supported!("std::fmt::format is not a function")
+ };
+ let message_string = self.interpret_mir(
+ self.db
+ .mir_body(format_fn.into())
+ .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
+ args.map(|x| IntervalOrOwned::Owned(x.clone())),
+ )?;
+ let addr =
+ Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
- Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned())
+ Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
+ .into_owned())
})()
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
Err(MirEvalError::Panic(message))
@@ -483,9 +501,7 @@
}
"syscall" => {
let Some((id, rest)) = args.split_first() else {
- return Err(MirEvalError::TypeError(
- "syscall arg1 is not provided",
- ));
+ return Err(MirEvalError::TypeError("syscall arg1 is not provided"));
};
let id = from_bytes!(i64, id.get(self)?);
self.exec_syscall(id, rest, destination, locals, span)
@@ -710,7 +726,8 @@
}
match name {
"size_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
@@ -718,14 +735,17 @@
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
}
"min_align_of" | "pref_align_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
};
let align = self.layout(ty)?.align.abi.bytes();
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
}
"size_of_val" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of_val generic arg is not provided"));
};
@@ -741,8 +761,12 @@
}
}
"min_align_of_val" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
- return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided"));
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "min_align_of_val generic arg is not provided",
+ ));
};
let [arg] = args else {
return Err(MirEvalError::TypeError("min_align_of_val args are not provided"));
@@ -756,7 +780,8 @@
}
}
"type_name" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
};
@@ -779,7 +804,8 @@
.write_from_bytes(self, &len.to_le_bytes())
}
"needs_drop" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
@@ -831,9 +857,12 @@
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
- return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided"));
+ return Err(MirEvalError::TypeError(
+ "ptr_offset_from generic arg is not provided",
+ ));
};
let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
let ans = ans / size;
@@ -940,7 +969,8 @@
"copy_nonoverlapping args are not provided",
));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError(
"copy_nonoverlapping generic arg is not provided",
@@ -959,9 +989,45 @@
let [ptr, offset] = args else {
return Err(MirEvalError::TypeError("offset args are not provided"));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
- else {
- return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ let ty = if name == "offset" {
+ let Some(ty0) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ let Some(ty1) =
+ generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ if !matches!(
+ ty1.as_builtin(),
+ Some(
+ BuiltinType::Int(BuiltinInt::Isize)
+ | BuiltinType::Uint(BuiltinUint::Usize)
+ )
+ ) {
+ return Err(MirEvalError::TypeError(
+ "offset generic arg is not usize or isize",
+ ));
+ }
+ match ty0.as_raw_ptr() {
+ Some((ty, _)) => ty,
+ None => {
+ return Err(MirEvalError::TypeError(
+ "offset generic arg is not a raw pointer",
+ ));
+ }
+ }
+ } else {
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "arith_offset generic arg is not provided",
+ ));
+ };
+ ty
};
let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
@@ -1079,7 +1145,8 @@
let [arg] = args else {
return Err(MirEvalError::TypeError("discriminant_value arg is not provided"));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError(
"discriminant_value generic arg is not provided",
@@ -1133,17 +1200,32 @@
let addr = Address::from_bytes(arg.interval.get(self)?)?;
destination.write_from_interval(self, Interval { addr, size: destination.size })
}
+ "write_via_move" => {
+ let [ptr, val] = args else {
+ return Err(MirEvalError::TypeError("write_via_move args are not provided"));
+ };
+ let dst = Address::from_bytes(ptr.get(self)?)?;
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "write_via_copy generic arg is not provided",
+ ));
+ };
+ let size = self.size_of_sized(ty, locals, "write_via_move ptr type")?;
+ Interval { addr: dst, size }.write_from_interval(self, val.interval)?;
+ Ok(())
+ }
"write_bytes" => {
let [dst, val, count] = args else {
return Err(MirEvalError::TypeError("write_bytes args are not provided"));
};
let count = from_bytes!(usize, count.get(self)?);
let val = from_bytes!(u8, val.get(self)?);
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
- return Err(MirEvalError::TypeError(
- "write_bytes generic arg is not provided",
- ));
+ return Err(MirEvalError::TypeError("write_bytes generic arg is not provided"));
};
let dst = Address::from_bytes(dst.get(self)?)?;
let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
index ec74631..5190066 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -45,7 +45,9 @@
};
match try_const_usize(self.db, len) {
Some(len) => {
- let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
+ let Some(ty) =
+ subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
return Err(MirEvalError::TypeError("simd type with no ty param"));
};
Ok((len as usize, ty.clone()))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 51cf882..dd2dba7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -71,7 +71,7 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError {
- ConstEvalError(String, Box<ConstEvalError>),
+ ConstEvalError(Box<str>, Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
IncompletePattern,
@@ -84,7 +84,7 @@
UnsizedTemporary(Ty),
MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch),
- /// This should be never happen. Type mismatch should catch everything.
+ /// This should never happen. Type mismatch should catch everything.
TypeError(&'static str),
NotSupported(String),
ContinueWithoutLoop,
@@ -244,6 +244,7 @@
let locals = Arena::new();
let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
let mir = MirBody {
+ projection_store: ProjectionStore::default(),
basic_blocks,
locals,
start_block,
@@ -370,6 +371,12 @@
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] {
+ Expr::OffsetOf(_) => {
+ not_supported!("builtin#offset_of")
+ }
+ Expr::InlineAsm(_) => {
+ not_supported!("builtin#asm")
+ }
Expr::Missing => {
if let DefWithBodyId::FunctionId(f) = self.owner {
let assoc = f.lookup(self.db.upcast());
@@ -803,36 +810,34 @@
current = c;
operands[u32::from(field_id.into_raw()) as usize] = Some(op);
}
- self.push_assignment(
- current,
- place,
- Rvalue::Aggregate(
- AggregateKind::Adt(variant_id, subst),
- match spread_place {
- Some(sp) => operands
- .into_iter()
- .enumerate()
- .map(|(i, it)| match it {
- Some(it) => it,
- None => {
- let p =
- sp.project(ProjectionElem::Field(FieldId {
- parent: variant_id,
- local_id: LocalFieldId::from_raw(
- RawIdx::from(i as u32),
- ),
- }));
- Operand::Copy(p)
- }
- })
- .collect(),
- None => operands.into_iter().collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
- },
- ),
- expr_id.into(),
+ let rvalue = Rvalue::Aggregate(
+ AggregateKind::Adt(variant_id, subst),
+ match spread_place {
+ Some(sp) => operands
+ .into_iter()
+ .enumerate()
+ .map(|(i, it)| match it {
+ Some(it) => it,
+ None => {
+ let p = sp.project(
+ ProjectionElem::Field(FieldId {
+ parent: variant_id,
+ local_id: LocalFieldId::from_raw(RawIdx::from(
+ i as u32,
+ )),
+ }),
+ &mut self.result.projection_store,
+ );
+ Operand::Copy(p)
+ }
+ })
+ .collect(),
+ None => operands.into_iter().collect::<Option<_>>().ok_or(
+ MirLowerError::TypeError("missing field in record literal"),
+ )?,
+ },
);
+ self.push_assignment(current, place, rvalue, expr_id.into());
Ok(Some(current))
}
VariantId::UnionId(union_id) => {
@@ -841,10 +846,10 @@
};
let local_id =
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
- let place = place.project(PlaceElem::Field(FieldId {
- parent: union_id.into(),
- local_id,
- }));
+ let place = place.project(
+ PlaceElem::Field(FieldId { parent: union_id.into(), local_id }),
+ &mut self.result.projection_store,
+ );
self.lower_expr_to_place(*expr, place, current)
}
}
@@ -898,7 +903,7 @@
else {
return Ok(None);
};
- let p = place.project(ProjectionElem::Deref);
+ let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store);
self.push_assignment(current, p, operand.into(), expr_id.into());
Ok(Some(current))
}
@@ -1120,27 +1125,31 @@
for capture in captures.iter() {
let p = Place {
local: self.binding_local(capture.place.local)?,
- projection: capture
- .place
- .projections
- .clone()
- .into_iter()
- .map(|it| match it {
- ProjectionElem::Deref => ProjectionElem::Deref,
- ProjectionElem::Field(it) => ProjectionElem::Field(it),
- ProjectionElem::TupleOrClosureField(it) => {
- ProjectionElem::TupleOrClosureField(it)
- }
- ProjectionElem::ConstantIndex { offset, from_end } => {
- ProjectionElem::ConstantIndex { offset, from_end }
- }
- ProjectionElem::Subslice { from, to } => {
- ProjectionElem::Subslice { from, to }
- }
- ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it),
- ProjectionElem::Index(it) => match it {},
- })
- .collect(),
+ projection: self.result.projection_store.intern(
+ capture
+ .place
+ .projections
+ .clone()
+ .into_iter()
+ .map(|it| match it {
+ ProjectionElem::Deref => ProjectionElem::Deref,
+ ProjectionElem::Field(it) => ProjectionElem::Field(it),
+ ProjectionElem::TupleOrClosureField(it) => {
+ ProjectionElem::TupleOrClosureField(it)
+ }
+ ProjectionElem::ConstantIndex { offset, from_end } => {
+ ProjectionElem::ConstantIndex { offset, from_end }
+ }
+ ProjectionElem::Subslice { from, to } => {
+ ProjectionElem::Subslice { from, to }
+ }
+ ProjectionElem::OpaqueCast(it) => {
+ ProjectionElem::OpaqueCast(it)
+ }
+ ProjectionElem::Index(it) => match it {},
+ })
+ .collect(),
+ ),
};
match &capture.kind {
CaptureKind::ByRef(bk) => {
@@ -1201,7 +1210,8 @@
let Some(values) = elements
.iter()
.map(|it| {
- let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else {
+ let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)?
+ else {
return Ok(None);
};
current = c;
@@ -1254,12 +1264,12 @@
match &self.body.exprs[lhs] {
Expr::Tuple { exprs, is_assignee_expr: _ } => {
for (i, expr) in exprs.iter().enumerate() {
- let Some(c) = self.lower_destructing_assignment(
- current,
- *expr,
- rhs.project(ProjectionElem::TupleOrClosureField(i)),
- span,
- )? else {
+ let rhs = rhs.project(
+ ProjectionElem::TupleOrClosureField(i),
+ &mut self.result.projection_store,
+ );
+ let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
+ else {
return Ok(None);
};
current = c;
@@ -1268,8 +1278,7 @@
}
Expr::Underscore => Ok(Some(current)),
_ => {
- let Some((lhs_place, current)) =
- self.lower_expr_as_place(current, lhs, false)?
+ let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)?
else {
return Ok(None);
};
@@ -1286,9 +1295,7 @@
rhs: ExprId,
span: MirSpan,
) -> Result<Option<BasicBlockId>> {
- let Some((rhs_op, current)) =
- self.lower_expr_to_some_operand(rhs, current)?
- else {
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else {
return Ok(None);
};
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
@@ -1303,9 +1310,7 @@
self.push_assignment(current, temp.clone(), rhs_op.into(), span);
return self.lower_destructing_assignment(current, lhs, temp, span);
}
- let Some((lhs_place, current)) =
- self.lower_expr_as_place(current, lhs, false)?
- else {
+ let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else {
return Ok(None);
};
self.push_assignment(current, lhs_place, rhs_op.into(), span);
@@ -1320,17 +1325,21 @@
placeholder_subst
}
- fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
+ fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> {
if let Expr::Field { expr, name } = &self.body[expr_id] {
if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
- *place = place.project(ProjectionElem::TupleOrClosureField(index))
+ *place = place.project(
+ ProjectionElem::TupleOrClosureField(index),
+ &mut self.result.projection_store,
+ )
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
- *place = place.project(ProjectionElem::Field(field));
+ *place =
+ place.project(ProjectionElem::Field(field), &mut self.result.projection_store);
}
} else {
not_supported!("")
@@ -1447,7 +1456,7 @@
let name = const_id.name(self.db.upcast());
self.db
.const_eval(const_id.into(), subst, None)
- .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
+ .map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))?
};
Ok(Operand::Constant(c))
}
@@ -1844,7 +1853,7 @@
data.name.display(self.db.upcast()),
data.variants[variant.local_id].name.display(self.db.upcast())
);
- Err(MirLowerError::ConstEvalError(name, Box::new(e)))
+ Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
}
}
}
@@ -1992,13 +2001,14 @@
FnTrait::FnOnce => vec![],
FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
};
- ctx.result.walk_places(|p| {
+ ctx.result.walk_places(|p, store| {
if let Some(it) = upvar_map.get(&p.local) {
let r = it.iter().find(|it| {
- if p.projection.len() < it.0.place.projections.len() {
+ if p.projection.lookup(&store).len() < it.0.place.projections.len() {
return false;
}
- for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) {
+ for (it, y) in p.projection.lookup(&store).iter().zip(it.0.place.projections.iter())
+ {
match (it, y) {
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
(ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (),
@@ -2016,13 +2026,18 @@
p.local = closure_local;
let mut next_projs = closure_projection.clone();
next_projs.push(PlaceElem::TupleOrClosureField(it.1));
- let prev_projs = mem::take(&mut p.projection);
+ let prev_projs = p.projection;
if it.0.kind != CaptureKind::ByValue {
next_projs.push(ProjectionElem::Deref);
}
- next_projs
- .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len()));
- p.projection = next_projs.into();
+ next_projs.extend(
+ prev_projs
+ .lookup(&store)
+ .iter()
+ .cloned()
+ .skip(it.0.place.projections.len()),
+ );
+ p.projection = store.intern(next_projs.into());
}
None => err = Some(p.clone()),
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index 213f151..8c078eb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -70,7 +70,7 @@
else {
return Ok(None);
};
- it.0 = it.0.project(ProjectionElem::Deref);
+ it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some(it))
}
Adjust::Deref(Some(od)) => {
@@ -152,7 +152,10 @@
Operand::Static(s).into(),
expr_id.into(),
);
- Ok(Some((temp.project(ProjectionElem::Deref), current)))
+ Ok(Some((
+ temp.project(ProjectionElem::Deref, &mut self.result.projection_store),
+ current,
+ )))
}
_ => try_rvalue(self),
}
@@ -203,7 +206,7 @@
else {
return Ok(None);
};
- r = r.project(ProjectionElem::Deref);
+ r = r.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((r, current)))
}
_ => try_rvalue(self),
@@ -267,7 +270,8 @@
else {
return Ok(None);
};
- p_base = p_base.project(ProjectionElem::Index(l_index));
+ p_base = p_base
+ .project(ProjectionElem::Index(l_index), &mut self.result.projection_store);
Ok(Some((p_base, current)))
}
_ => try_rvalue(self),
@@ -308,7 +312,7 @@
else {
return Ok(None);
};
- result = result.project(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((result, current)))
}
@@ -363,7 +367,7 @@
else {
return Ok(None);
};
- result = result.project(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((result, current)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 1cdfd91..270f75ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -81,13 +81,16 @@
mode: MatchingMode,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
- cond_place.projection = cond_place
- .projection
- .iter()
- .cloned()
- .chain((0..cnt).map(|_| ProjectionElem::Deref))
- .collect::<Vec<_>>()
- .into();
+ cond_place.projection = self.result.projection_store.intern(
+ cond_place
+ .projection
+ .lookup(&self.result.projection_store)
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into(),
+ );
Ok(match &self.body.pats[pattern] {
Pat::Missing => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
@@ -262,20 +265,23 @@
}
}
for (i, &pat) in prefix.iter().enumerate() {
- let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
- offset: i as u64,
- from_end: false,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::ConstantIndex { offset: i as u64, from_end: false },
+ &mut self.result.projection_store,
+ );
(current, current_else) =
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
}
if let Some(slice) = slice {
if mode == MatchingMode::Bind {
if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
- let next_place = (&mut cond_place).project(ProjectionElem::Subslice {
- from: prefix.len() as u64,
- to: suffix.len() as u64,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::Subslice {
+ from: prefix.len() as u64,
+ to: suffix.len() as u64,
+ },
+ &mut self.result.projection_store,
+ );
(current, current_else) = self.pattern_match_binding(
id,
next_place,
@@ -287,10 +293,10 @@
}
}
for (i, &pat) in suffix.iter().enumerate() {
- let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
- offset: i as u64,
- from_end: true,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::ConstantIndex { offset: i as u64, from_end: true },
+ &mut self.result.projection_store,
+ );
(current, current_else) =
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
}
@@ -412,13 +418,11 @@
mode,
)?
}
- Pat::Ref { pat, mutability: _ } => self.pattern_match_inner(
- current,
- current_else,
- cond_place.project(ProjectionElem::Deref),
- *pat,
- mode,
- )?,
+ Pat::Ref { pat, mutability: _ } => {
+ let cond_place =
+ cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
+ self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
+ }
Pat::Box { .. } => not_supported!("box pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
@@ -594,7 +598,7 @@
mode: MatchingMode,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
for (proj, arg) in args {
- let cond_place = cond_place.project(proj);
+ let cond_place = cond_place.project(proj, &mut self.result.projection_store);
(current, current_else) =
self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 781ffae..0108859 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -329,7 +329,7 @@
}
}
}
- f(self, p.local, &p.projection);
+ f(self, p.local, &p.projection.lookup(&self.body.projection_store));
}
fn operand(&mut self, r: &Operand) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 2ad7946..8140c41 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3,55 +3,6 @@
use super::{check, check_infer, check_no_mismatches, check_types};
#[test]
-fn infer_box() {
- check_types(
- r#"
-//- /main.rs crate:main deps:std
-fn test() {
- let x = box 1;
- let t = (x, box x, box &1, box [1]);
- t;
-} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>)
-
-//- /std.rs crate:std
-#[prelude_import] use prelude::*;
-mod prelude {}
-
-mod boxed {
- #[lang = "owned_box"]
- pub struct Box<T: ?Sized> {
- inner: *mut T,
- }
-}
-"#,
- );
-}
-
-#[test]
-fn infer_box_with_allocator() {
- check_types(
- r#"
-//- /main.rs crate:main deps:std
-fn test() {
- let x = box 1;
- let t = (x, box x, box &1, box [1]);
- t;
-} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>)
-
-//- /std.rs crate:std
-#[prelude_import] use prelude::*;
-mod boxed {
- #[lang = "owned_box"]
- pub struct Box<T: ?Sized, A: Allocator> {
- inner: *mut T,
- allocator: A,
- }
-}
-"#,
- );
-}
-
-#[test]
fn infer_adt_self() {
check_types(
r#"
@@ -2763,8 +2714,8 @@
}
fn test() {
- let vec = <[_]>::into_vec(box [1i32]);
- let v: Vec<Box<dyn B>> = <[_]> :: into_vec(box [box Astruct]);
+ let vec = <[_]>::into_vec(#[rustc_box] Box::new([1i32]));
+ let v: Vec<Box<dyn B>> = <[_]> :: into_vec(#[rustc_box] Box::new([#[rustc_box] Box::new(Astruct)]));
}
trait B{}
@@ -2774,20 +2725,20 @@
expect![[r#"
604..608 'self': Box<[T], A>
637..669 '{ ... }': Vec<T, A>
- 683..796 '{ ...t]); }': ()
+ 683..853 '{ ...])); }': ()
693..696 'vec': Vec<i32, Global>
699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
- 699..726 '<[_]>:...1i32])': Vec<i32, Global>
- 715..725 'box [1i32]': Box<[i32; 1], Global>
- 719..725 '[1i32]': [i32; 1]
- 720..724 '1i32': i32
- 736..737 'v': Vec<Box<dyn B, Global>, Global>
- 757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
- 757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
- 775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
- 779..792 '[box Astruct]': [Box<dyn B, Global>; 1]
- 780..791 'box Astruct': Box<Astruct, Global>
- 784..791 'Astruct': Astruct
+ 699..745 '<[_]>:...i32]))': Vec<i32, Global>
+ 715..744 '#[rust...1i32])': Box<[i32; 1], Global>
+ 737..743 '[1i32]': [i32; 1]
+ 738..742 '1i32': i32
+ 755..756 'v': Vec<Box<dyn B, Global>, Global>
+ 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
+ 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B, Global>, Global>
+ 794..849 '#[rust...uct)])': Box<[Box<dyn B, Global>; 1], Global>
+ 816..848 '[#[rus...ruct)]': [Box<dyn B, Global>; 1]
+ 817..847 '#[rust...truct)': Box<Astruct, Global>
+ 839..846 'Astruct': Astruct
"#]],
)
}
@@ -3649,3 +3600,30 @@
"#,
);
}
+
+#[test]
+fn offset_of() {
+ check_types(
+ r#"
+fn main() {
+ builtin#offset_of((,), 0);
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize
+}
+"#,
+ );
+}
+
+#[test]
+fn builtin_format_args() {
+ check(
+ r#"
+//- minicore: fmt
+fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_>
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 542df8b..d36b885 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -162,16 +162,16 @@
#[lang = "owned_box"]
#[fundamental]
-pub struct Box<T: ?Sized, A: Allocator = Global>;
+pub struct Box<T: ?Sized, A: Allocator = Global>(T);
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
fn send() -> Box<dyn Future<Output = ()> + Send + 'static>{
- box async move {}
+ Box(async move {})
}
fn not_send() -> Box<dyn Future<Output = ()> + 'static> {
- box async move {}
+ Box(async move {})
}
"#,
);
@@ -3057,7 +3057,7 @@
fn foo() {
let s = None;
- let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
+ let f: Box<dyn FnOnce(&Option<i32>)> = Box { inner: &mut (|ps| {}) };
f(&s);
}"#,
expect![[r#"
@@ -3068,19 +3068,19 @@
186..197 '*self.inner': T
187..191 'self': &Box<T>
187..197 'self.inner': *mut T
- 218..308 '{ ...&s); }': ()
+ 218..324 '{ ...&s); }': ()
228..229 's': Option<i32>
232..236 'None': Option<i32>
246..247 'f': Box<dyn FnOnce(&Option<i32>)>
- 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
- 286..293 '|ps| {}': impl Fn(&Option<i32>)
- 287..289 'ps': &Option<i32>
- 291..293 '{}': ()
- 300..301 'f': Box<dyn FnOnce(&Option<i32>)>
- 300..305 'f(&s)': ()
- 302..304 '&s': &Option<i32>
- 303..304 's': Option<i32>
- 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
+ 281..310 'Box { ... {}) }': Box<dyn FnOnce(&Option<i32>)>
+ 294..308 '&mut (|ps| {})': &mut impl Fn(&Option<i32>)
+ 300..307 '|ps| {}': impl Fn(&Option<i32>)
+ 301..303 'ps': &Option<i32>
+ 305..307 '{}': ()
+ 316..317 'f': Box<dyn FnOnce(&Option<i32>)>
+ 316..321 'f(&s)': ()
+ 318..320 '&s': &Option<i32>
+ 319..320 's': Option<i32>
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 121e5a0..796490a 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -1,39 +1,27 @@
//! Attributes & documentation for hir types.
use hir_def::{
- attr::{AttrsWithOwner, Documentation},
+ attr::AttrsWithOwner,
item_scope::ItemInNs,
path::{ModPath, Path},
per_ns::Namespace,
resolver::{HasResolver, Resolver, TypeNs},
- AssocItemId, AttrDefId, GenericParamId, ModuleDefId,
+ AssocItemId, AttrDefId, ModuleDefId,
};
use hir_expand::{hygiene::Hygiene, name::Name};
use hir_ty::db::HirDatabase;
use syntax::{ast, AstNode};
use crate::{
- Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field,
- Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait,
- TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
+ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
+ Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct,
+ Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
};
pub trait HasAttrs {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<DocLinkDef>;
-}
-
-/// Subset of `ide_db::Definition` that doc links can resolve to.
-pub enum DocLinkDef {
- ModuleDef(ModuleDef),
- Field(Field),
- SelfType(Trait),
+ #[doc(hidden)]
+ fn attr_id(self) -> AttrDefId;
}
macro_rules! impl_has_attrs {
@@ -43,18 +31,8 @@
let def = AttrDefId::$def_id(self.into());
db.attrs_with_owner(def)
}
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- let def = AttrDefId::$def_id(self.into());
- db.attrs(def).docs()
- }
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>
- ) -> Option<DocLinkDef> {
- let def = AttrDefId::$def_id(self.into());
- resolve_doc_path(db, def, link, ns)
+ fn attr_id(self) -> AttrDefId {
+ AttrDefId::$def_id(self.into())
}
}
)*};
@@ -74,6 +52,7 @@
(Module, ModuleId),
(GenericParam, GenericParamId),
(Impl, ImplId),
+ (ExternCrateDecl, ExternCrateId),
];
macro_rules! impl_has_attrs_enum {
@@ -82,16 +61,8 @@
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
$enum::$variant(self).attrs(db)
}
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- $enum::$variant(self).docs(db)
- }
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>
- ) -> Option<DocLinkDef> {
- $enum::$variant(self).resolve_doc_path(db, link, ns)
+ fn attr_id(self) -> AttrDefId {
+ $enum::$variant(self).attr_id()
}
}
)*};
@@ -108,70 +79,35 @@
AssocItem::TypeAlias(it) => it.attrs(db),
}
}
-
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ fn attr_id(self) -> AttrDefId {
match self {
- AssocItem::Function(it) => it.docs(db),
- AssocItem::Const(it) => it.docs(db),
- AssocItem::TypeAlias(it) => it.docs(db),
+ AssocItem::Function(it) => it.attr_id(),
+ AssocItem::Const(it) => it.attr_id(),
+ AssocItem::TypeAlias(it) => it.attr_id(),
}
}
-
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<DocLinkDef> {
- match self {
- AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
- AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
- AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
- }
- }
-}
-
-impl HasAttrs for ExternCrateDecl {
- fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
- let def = AttrDefId::ExternCrateId(self.into());
- db.attrs_with_owner(def)
- }
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- let crate_docs = self.resolved_crate(db)?.root_module().attrs(db).docs().map(String::from);
- let def = AttrDefId::ExternCrateId(self.into());
- let decl_docs = db.attrs(def).docs().map(String::from);
- match (decl_docs, crate_docs) {
- (None, None) => None,
- (Some(decl_docs), None) => Some(decl_docs),
- (None, Some(crate_docs)) => Some(crate_docs),
- (Some(mut decl_docs), Some(crate_docs)) => {
- decl_docs.push('\n');
- decl_docs.push('\n');
- decl_docs += &crate_docs;
- Some(decl_docs)
- }
- }
- .map(Documentation::new)
- }
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<DocLinkDef> {
- let def = AttrDefId::ExternCrateId(self.into());
- resolve_doc_path(db, def, link, ns)
- }
}
/// Resolves the item `link` points to in the scope of `def`.
-fn resolve_doc_path(
+pub fn resolve_doc_path_on(
db: &dyn HirDatabase,
- def: AttrDefId,
+ def: impl HasAttrs,
link: &str,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
- let resolver = match def {
+ // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
+ // AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()),
+
+ resolve_doc_path_on_(db, link, def.attr_id(), ns)
+}
+
+fn resolve_doc_path_on_(
+ db: &dyn HirDatabase,
+ link: &str,
+ attr_id: AttrDefId,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ let resolver = match attr_id {
AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
AttrDefId::AdtId(it) => it.resolver(db.upcast()),
@@ -187,12 +123,7 @@
AttrDefId::UseId(it) => it.resolver(db.upcast()),
AttrDefId::MacroId(it) => it.resolver(db.upcast()),
AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()),
- AttrDefId::GenericParamId(it) => match it {
- GenericParamId::TypeParamId(it) => it.parent(),
- GenericParamId::ConstParamId(it) => it.parent(),
- GenericParamId::LifetimeParamId(it) => it.parent,
- }
- .resolver(db.upcast()),
+ AttrDefId::GenericParamId(_) => return None,
};
let mut modpath = modpath_from_str(db, link)?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 80c3bcd..479138b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -43,6 +43,7 @@
MacroExpansionParseError,
MalformedDerive,
MismatchedArgCount,
+ MismatchedTupleStructPatArgCount,
MissingFields,
MissingMatchArms,
MissingUnsafe,
@@ -172,7 +173,8 @@
#[derive(Debug)]
pub struct NoSuchField {
- pub field: InFile<AstPtr<ast::RecordExprField>>,
+ pub field: InFile<Either<AstPtr<ast::RecordExprField>, AstPtr<ast::RecordPatField>>>,
+ pub private: bool,
}
#[derive(Debug)]
@@ -183,6 +185,13 @@
}
#[derive(Debug)]
+pub struct MismatchedTupleStructPatArgCount {
+ pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>,
+ pub expected: usize,
+ pub found: usize,
+}
+
+#[derive(Debug)]
pub struct ExpectedFunction {
pub call: InFile<AstPtr<ast::Expr>>,
pub found: Type,
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 512fe7e..b215ed3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -88,13 +88,14 @@
use crate::db::{DefDatabase, HirDatabase};
pub use crate::{
- attrs::{DocLinkDef, HasAttrs},
+ attrs::{resolve_doc_path_on, HasAttrs},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
- MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields,
- MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem,
- PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
+ MacroExpansionParseError, MalformedDerive, MismatchedArgCount,
+ MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
+ MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
+ ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
UnresolvedProcMacro, UnusedMut,
@@ -115,7 +116,7 @@
pub use {
cfg::{CfgAtom, CfgExpr, CfgOptions},
hir_def::{
- attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
+ attr::{builtin::AttributeTemplate, AttrSourceMap, Attrs, AttrsWithOwner},
data::adt::StructKind,
find_path::PrefixKind,
import_map,
@@ -130,7 +131,7 @@
{AdtId, ModuleDefId},
},
hir_expand::{
- attrs::Attr,
+ attrs::{Attr, AttrId},
name::{known, Name},
ExpandResult, HirFileId, InFile, MacroFile, Origin,
},
@@ -563,8 +564,8 @@
emit_def_diagnostic(db, acc, diag);
}
- for decl in self.declarations(db) {
- match decl {
+ for def in self.declarations(db) {
+ match def {
ModuleDef::Module(m) => {
// Only add diagnostics from inline modules
if def_map[m.id.local_id].origin.is_inline() {
@@ -575,7 +576,7 @@
for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
emit_def_diagnostic(db, acc, diag);
}
- acc.extend(decl.diagnostics(db))
+ acc.extend(def.diagnostics(db))
}
ModuleDef::Adt(adt) => {
match adt {
@@ -599,10 +600,10 @@
}
}
}
- acc.extend(decl.diagnostics(db))
+ acc.extend(def.diagnostics(db))
}
ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
- _ => acc.extend(decl.diagnostics(db)),
+ _ => acc.extend(def.diagnostics(db)),
}
}
self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
@@ -1446,6 +1447,7 @@
}
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+ db.unwind_if_cancelled();
let krate = self.module(db).id.krate();
let (body, source_map) = db.body_with_source_map(self.into());
@@ -1501,11 +1503,19 @@
let infer = db.infer(self.into());
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic");
+ let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic");
for d in &infer.diagnostics {
match d {
- &hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
- let field = source_map.field_syntax(expr);
- acc.push(NoSuchField { field }.into())
+ &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => {
+ let expr_or_pat = match expr {
+ ExprOrPatId::ExprId(expr) => {
+ source_map.field_syntax(expr).map(Either::Left)
+ }
+ ExprOrPatId::PatId(pat) => {
+ source_map.pat_field_syntax(pat).map(Either::Right)
+ }
+ };
+ acc.push(NoSuchField { field: expr_or_pat, private }.into())
}
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
acc.push(
@@ -1521,10 +1531,7 @@
&hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
let expr_or_pat = match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
- ExprOrPatId::PatId(pat) => source_map
- .pat_syntax(pat)
- .expect("unexpected synthetic")
- .map(Either::Right),
+ ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right),
};
let item = item.into();
acc.push(PrivateAssocItem { expr_or_pat, item }.into())
@@ -1596,6 +1603,23 @@
.into(),
)
}
+ &hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount {
+ pat,
+ expected,
+ found,
+ } => {
+ let expr_or_pat = match pat {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
+ ExprOrPatId::PatId(pat) => source_map
+ .pat_syntax(pat)
+ .expect("unexpected synthetic")
+ .map(|it| it.unwrap_left())
+ .map(Either::Right),
+ };
+ acc.push(
+ MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(),
+ )
+ }
}
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@@ -4838,3 +4862,10 @@
ExternBlock(),
Crate(CrateId),
}
+
+/// Subset of `ide_db::Definition` that doc links can resolve to.
+pub enum DocLinkDef {
+ ModuleDef(ModuleDef),
+ Field(Field),
+ SelfType(Trait),
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index b8d4ecd..a42e097 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -127,165 +127,24 @@
}
}
+impl<'db, DB> ops::Deref for Semantics<'db, DB> {
+ type Target = SemanticsImpl<'db>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.imp
+ }
+}
+
impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn new(db: &DB) -> Semantics<'_, DB> {
let impl_ = SemanticsImpl::new(db);
Semantics { db, imp: impl_ }
}
- pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
- self.imp.parse(file_id)
- }
-
- pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
- self.imp.parse_or_expand(file_id)
- }
-
- pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
- self.imp.expand(macro_call)
- }
-
- /// If `item` has an attribute macro attached to it, expands it.
- pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
- self.imp.expand_attr_macro(item)
- }
-
- pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
- self.imp.expand_derive_as_pseudo_attr_macro(attr)
- }
-
- pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<Option<Macro>>> {
- self.imp.resolve_derive_macro(derive)
- }
-
- pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<SyntaxNode>> {
- self.imp.expand_derive_macro(derive)
- }
-
- pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
- self.imp.is_attr_macro_call(item)
- }
-
- pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool {
- self.imp.is_derive_annotated(item)
- }
-
- /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
- /// expansion. `token_to_map` should be a token from the `speculative args` node.
- pub fn speculative_expand(
- &self,
- actual_macro_call: &ast::MacroCall,
- speculative_args: &ast::TokenTree,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
- }
-
- /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
- /// expansion. `token_to_map` should be a token from the `speculative args` node.
- pub fn speculative_expand_attr_macro(
- &self,
- actual_macro_call: &ast::Item,
- speculative_args: &ast::Item,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map)
- }
-
- pub fn speculative_expand_derive_as_pseudo_attr_macro(
- &self,
- actual_macro_call: &ast::Attr,
- speculative_args: &ast::Attr,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand_derive_as_pseudo_attr_macro(
- actual_macro_call,
- speculative_args,
- token_to_map,
- )
- }
-
- /// Descend the token into its macro call if it is part of one, returning the token in the
- /// expansion that it is associated with. If `offset` points into the token's range, it will
- /// be considered for the mapping in case of inline format args.
- pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
- self.imp.descend_into_macros_single(token, offset)
- }
-
- /// Descend the token into its macro call if it is part of one, returning the tokens in the
- /// expansion that it is associated with. If `offset` points into the token's range, it will
- /// be considered for the mapping in case of inline format args.
- pub fn descend_into_macros(
- &self,
- token: SyntaxToken,
- offset: TextSize,
- ) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros(token, offset)
- }
-
- /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
- ///
- /// Returns the original non descended token if none of the mapped counterparts have the same text.
- pub fn descend_into_macros_with_same_text(
- &self,
- token: SyntaxToken,
- offset: TextSize,
- ) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros_with_same_text(token, offset)
- }
-
- pub fn descend_into_macros_with_kind_preference(
- &self,
- token: SyntaxToken,
- offset: TextSize,
- ) -> SyntaxToken {
- self.imp.descend_into_macros_with_kind_preference(token, offset)
- }
-
- /// Maps a node down by mapping its first and last token down.
- pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
- self.imp.descend_node_into_attributes(node)
- }
-
- /// Search for a definition's source and cache its syntax tree
- pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
- where
- Def::Ast: AstNode,
- {
- self.imp.source(def)
- }
-
pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId {
self.imp.find_file(syntax_node).file_id
}
- /// Attempts to map the node out of macro expanded files returning the original file range.
- /// If upmapping is not possible, this will fall back to the range of the macro call of the
- /// macro file the node resides in.
- pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
- self.imp.original_range(node)
- }
-
- /// Attempts to map the node out of macro expanded files returning the original file range.
- pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
- self.imp.original_range_opt(node)
- }
-
- /// Attempts to map the node out of macro expanded files.
- /// This only work for attribute expansions, as other ones do not have nodes as input.
- pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
- self.imp.original_ast_node(node)
- }
- /// Attempts to map the node out of macro expanded files.
- /// This only work for attribute expansions, as other ones do not have nodes as input.
- pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
- self.imp.original_syntax_node(node)
- }
-
- pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
- self.imp.diagnostics_display_range(diagnostics)
- }
-
pub fn token_ancestors_with_macros(
&self,
token: SyntaxToken,
@@ -293,19 +152,6 @@
token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it))
}
- /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
- pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
- self.imp.ancestors_with_macros(node)
- }
-
- pub fn ancestors_at_offset_with_macros(
- &self,
- node: &SyntaxNode,
- offset: TextSize,
- ) -> impl Iterator<Item = SyntaxNode> + '_ {
- self.imp.ancestors_at_offset_with_macros(node, offset)
- }
-
/// Find an AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
/// search up until it is of the target AstNode type
pub fn find_node_at_offset_with_macros<N: AstNode>(
@@ -336,53 +182,6 @@
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
}
- pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
- self.imp.resolve_lifetime_param(lifetime)
- }
-
- pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
- self.imp.resolve_label(lifetime)
- }
-
- pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
- self.imp.resolve_type(ty)
- }
-
- pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> {
- self.imp.resolve_trait(trait_)
- }
-
- pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
- self.imp.expr_adjustments(expr)
- }
-
- pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
- self.imp.type_of_expr(expr)
- }
-
- pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
- self.imp.type_of_pat(pat)
- }
-
- /// It also includes the changes that binding mode makes in the type. For example in
- /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
- /// of this function is `&mut Option<T>`
- pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
- self.imp.type_of_binding_in_pat(pat)
- }
-
- pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
- self.imp.type_of_self(param)
- }
-
- pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
- self.imp.pattern_adjustments(pat)
- }
-
- pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
- self.imp.binding_mode_of_pat(pat)
- }
-
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
self.imp.resolve_method_call(call).map(Function::from)
}
@@ -417,61 +216,10 @@
self.imp.resolve_try_expr(try_expr).map(Function::from)
}
- pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
- self.imp.resolve_method_call_as_callable(call)
- }
-
- pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
- self.imp.resolve_field(field)
- }
-
- pub fn resolve_record_field(
- &self,
- field: &ast::RecordExprField,
- ) -> Option<(Field, Option<Local>, Type)> {
- self.imp.resolve_record_field(field)
- }
-
- pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
- self.imp.resolve_record_pat_field(field)
- }
-
- pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
- self.imp.resolve_macro_call(macro_call)
- }
-
- pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
- self.imp.is_unsafe_macro_call(macro_call)
- }
-
- pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
- self.imp.resolve_attr_macro_call(item)
- }
-
- pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
- self.imp.resolve_path(path)
- }
-
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
self.imp.resolve_variant(record_lit).map(VariantDef::from)
}
- pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
- self.imp.resolve_bind_pat_to_const(pat)
- }
-
- pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
- self.imp.record_literal_missing_fields(literal)
- }
-
- pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
- self.imp.record_pattern_missing_fields(pattern)
- }
-
- pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
- self.imp.to_def(src)
- }
-
pub fn to_module_def(&self, file: FileId) -> Option<Module> {
self.imp.to_module_def(file).next()
}
@@ -479,39 +227,6 @@
pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
self.imp.to_module_def(file)
}
-
- pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
- self.imp.scope(node)
- }
-
- pub fn scope_at_offset(
- &self,
- node: &SyntaxNode,
- offset: TextSize,
- ) -> Option<SemanticsScope<'db>> {
- self.imp.scope_at_offset(node, offset)
- }
-
- pub fn assert_contains_node(&self, node: &SyntaxNode) {
- self.imp.assert_contains_node(node)
- }
-
- pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
- self.imp.is_unsafe_method_call(method_call_expr)
- }
-
- pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
- self.imp.is_unsafe_ref_expr(ref_expr)
- }
-
- pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
- self.imp.is_unsafe_ident_pat(ident_pat)
- }
-
- /// Returns `true` if the `node` is inside an `unsafe` context.
- pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
- self.imp.is_inside_unsafe(expr)
- }
}
impl<'db> SemanticsImpl<'db> {
@@ -525,32 +240,33 @@
}
}
- fn parse(&self, file_id: FileId) -> ast::SourceFile {
+ pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
let tree = self.db.parse(file_id).tree();
self.cache(tree.syntax().clone(), file_id.into());
tree
}
- fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
+ pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
node
}
- fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
+ pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
let sa = self.analyze_no_infer(macro_call.syntax())?;
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
let node = self.parse_or_expand(file_id);
Some(node)
}
- fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
+ /// If `item` has an attribute macro attached to it, expands it.
+ pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
Some(self.parse_or_expand(macro_call_id.as_file()))
}
- fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
+ pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let src = self.wrap_node_infile(attr.clone());
let call_id = self.with_ctx(|ctx| {
@@ -559,7 +275,7 @@
Some(self.parse_or_expand(call_id.as_file()))
}
- fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
+ pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
let calls = self.derive_macro_calls(attr)?;
self.with_ctx(|ctx| {
Some(
@@ -573,7 +289,7 @@
})
}
- fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> {
+ pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> {
let res: Vec<_> = self
.derive_macro_calls(attr)?
.into_iter()
@@ -598,19 +314,21 @@
})
}
- fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
+ pub fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
let file_id = self.find_file(adt.syntax()).file_id;
let adt = InFile::new(file_id, adt);
self.with_ctx(|ctx| ctx.has_derives(adt))
}
- fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
+ pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
let file_id = self.find_file(item.syntax()).file_id;
let src = InFile::new(file_id, item.clone());
self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
}
- fn speculative_expand(
+ /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
+ pub fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
speculative_args: &ast::TokenTree,
@@ -633,7 +351,9 @@
)
}
- fn speculative_expand_attr(
+ /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
+ pub fn speculative_expand_attr_macro(
&self,
actual_macro_call: &ast::Item,
speculative_args: &ast::Item,
@@ -649,7 +369,7 @@
)
}
- fn speculative_expand_derive_as_pseudo_attr_macro(
+ pub fn speculative_expand_derive_as_pseudo_attr_macro(
&self,
actual_macro_call: &ast::Attr,
speculative_args: &ast::Attr,
@@ -668,8 +388,9 @@
)
}
- // This might not be the correct way to do this, but it works for now
- fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+ /// Maps a node down by mapping its first and last token down.
+ pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+ // This might not be the correct way to do this, but it works for now
let mut res = smallvec![];
let tokens = (|| {
let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?;
@@ -723,7 +444,10 @@
res
}
- fn descend_into_macros(
+ /// Descend the token into its macro call if it is part of one, returning the tokens in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros(
&self,
token: SyntaxToken,
offset: TextSize,
@@ -736,7 +460,10 @@
res
}
- fn descend_into_macros_with_same_text(
+ /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
+ ///
+ /// Returns the original non descended token if none of the mapped counterparts have the same text.
+ pub fn descend_into_macros_with_same_text(
&self,
token: SyntaxToken,
offset: TextSize,
@@ -755,7 +482,7 @@
res
}
- fn descend_into_macros_with_kind_preference(
+ pub fn descend_into_macros_with_kind_preference(
&self,
token: SyntaxToken,
offset: TextSize,
@@ -785,7 +512,10 @@
res.unwrap_or(token)
}
- fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
+ /// Descend the token into its macro call if it is part of one, returning the token in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
let mut res = token.clone();
self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
res = value;
@@ -995,17 +725,23 @@
})
}
- fn original_range(&self, node: &SyntaxNode) -> FileRange {
+ /// Attempts to map the node out of macro expanded files returning the original file range.
+ /// If upmapping is not possible, this will fall back to the range of the macro call of the
+ /// macro file the node resides in.
+ pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
let node = self.find_file(node);
node.original_file_range(self.db.upcast())
}
- fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
+ /// Attempts to map the node out of macro expanded files returning the original file range.
+ pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
let node = self.find_file(node);
node.original_file_range_opt(self.db.upcast())
}
- fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
+ /// Attempts to map the node out of macro expanded files.
+ /// This only work for attribute expansions, as other ones do not have nodes as input.
+ pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(
|InFile { file_id, value }| {
self.cache(find_root(value.syntax()), file_id);
@@ -1014,7 +750,9 @@
)
}
- fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
+ /// Attempts to map the node out of macro expanded files.
+ /// This only work for attribute expansions, as other ones do not have nodes as input.
+ pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
let InFile { file_id, .. } = self.find_file(node);
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
|InFile { file_id, value }| {
@@ -1024,7 +762,7 @@
)
}
- fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
+ pub fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
let root = self.parse_or_expand(src.file_id);
let node = src.map(|it| it.to_node(&root));
node.as_ref().original_file_range(self.db.upcast())
@@ -1037,7 +775,8 @@
token.parent().into_iter().flat_map(move |parent| self.ancestors_with_macros(parent))
}
- fn ancestors_with_macros(
+ /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
+ pub fn ancestors_with_macros(
&self,
node: SyntaxNode,
) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
@@ -1055,7 +794,7 @@
.map(|it| it.value)
}
- fn ancestors_at_offset_with_macros(
+ pub fn ancestors_at_offset_with_macros(
&self,
node: &SyntaxNode,
offset: TextSize,
@@ -1065,7 +804,7 @@
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
}
- fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
+ pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
let text = lifetime.text();
let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| {
let gpl = ast::AnyHasGenericParams::cast(syn)?.generic_param_list()?;
@@ -1076,7 +815,7 @@
ToDef::to_def(self, src)
}
- fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
+ pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
let text = lifetime.text();
let label = lifetime.syntax().ancestors().find_map(|syn| {
let label = match_ast! {
@@ -1098,7 +837,7 @@
ToDef::to_def(self, src)
}
- fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
+ pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
let ty = hir_ty::TyLoweringContext::new(
@@ -1110,7 +849,7 @@
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
}
- fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
+ pub fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
let analyze = self.analyze(path.syntax())?;
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
@@ -1121,7 +860,7 @@
}
}
- fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
+ pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
let mutability = |m| match m {
hir_ty::Mutability::Not => Mutability::Shared,
hir_ty::Mutability::Mut => Mutability::Mut,
@@ -1165,33 +904,36 @@
})
}
- fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
+ pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
self.analyze(expr.syntax())?
.type_of_expr(self.db, expr)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
- fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
+ pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
self.analyze(pat.syntax())?
.type_of_pat(self.db, pat)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
- fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ /// It also includes the changes that binding mode makes in the type. For example in
+ /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
+ /// of this function is `&mut Option<T>`
+ pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
}
- fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
+ pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.analyze(param.syntax())?.type_of_self(self.db, param)
}
- fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
+ pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
self.analyze(pat.syntax())
.and_then(|it| it.pattern_adjustments(self.db, pat))
.unwrap_or_default()
}
- fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
+ pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
}
@@ -1226,32 +968,32 @@
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
- fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
+ pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
}
- fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
+ pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
self.analyze(field.syntax())?.resolve_field(self.db, field)
}
- fn resolve_record_field(
+ pub fn resolve_record_field(
&self,
field: &ast::RecordExprField,
) -> Option<(Field, Option<Local>, Type)> {
self.analyze(field.syntax())?.resolve_record_field(self.db, field)
}
- fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
+ pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
}
- fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
+ pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
let sa = self.analyze(macro_call.syntax())?;
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
sa.resolve_macro_call(self.db, macro_call)
}
- fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
+ pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
let sa = match self.analyze(macro_call.syntax()) {
Some(it) => it,
None => return false,
@@ -1260,7 +1002,7 @@
sa.is_unsafe_macro_call(self.db, macro_call)
}
- fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
+ pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
let item_in_file = self.wrap_node_infile(item.clone());
let id = self.with_ctx(|ctx| {
let macro_call_id = ctx.item_to_macro_call(item_in_file)?;
@@ -1269,7 +1011,7 @@
Some(Macro { id })
}
- fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
+ pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
self.analyze(path.syntax())?.resolve_path(self.db, path)
}
@@ -1277,17 +1019,17 @@
self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit)
}
- fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
+ pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
self.analyze(pat.syntax())?.resolve_bind_pat_to_const(self.db, pat)
}
- fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
+ pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
self.analyze(literal.syntax())
.and_then(|it| it.record_literal_missing_fields(self.db, literal))
.unwrap_or_default()
}
- fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
+ pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
self.analyze(pattern.syntax())
.and_then(|it| it.record_pattern_missing_fields(self.db, pattern))
.unwrap_or_default()
@@ -1299,7 +1041,7 @@
f(&mut ctx)
}
- fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
+ pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
let src = self.find_file(src.syntax()).with_value(src).cloned();
T::to_def(self, src)
}
@@ -1308,7 +1050,7 @@
self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
}
- fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
+ pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
db: self.db,
file_id,
@@ -1316,7 +1058,11 @@
})
}
- fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> Option<SemanticsScope<'db>> {
+ pub fn scope_at_offset(
+ &self,
+ node: &SyntaxNode,
+ offset: TextSize,
+ ) -> Option<SemanticsScope<'db>> {
self.analyze_with_offset_no_infer(node, offset).map(
|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
db: self.db,
@@ -1326,7 +1072,8 @@
)
}
- fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
+ /// Search for a definition's source and cache its syntax tree
+ pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
{
@@ -1391,7 +1138,7 @@
assert!(prev == None || prev == Some(file_id))
}
- fn assert_contains_node(&self, node: &SyntaxNode) {
+ pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.find_file(node);
}
@@ -1427,7 +1174,7 @@
InFile::new(file_id, node)
}
- fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
+ pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
method_call_expr
.receiver()
.and_then(|expr| {
@@ -1450,7 +1197,7 @@
.unwrap_or(false)
}
- fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+ pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
ref_expr
.expr()
.and_then(|expr| {
@@ -1469,7 +1216,7 @@
// more than it should with the current implementation.
}
- fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+ pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
if ident_pat.ref_token().is_none() {
return false;
}
@@ -1512,7 +1259,8 @@
.unwrap_or(false)
}
- fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
+ /// Returns `true` if the `node` is inside an `unsafe` context.
+ pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
let Some(enclosing_item) =
expr.syntax().ancestors().find_map(Either::<ast::Item, ast::Variant>::cast)
else {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
new file mode 100644
index 0000000..45c1f0c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -0,0 +1,159 @@
+use crate::assist_context::{AssistContext, Assists};
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ defs::Definition,
+ LineIndexDatabase,
+};
+use syntax::{
+ ast::{self, edit_in_place::Indent},
+ AstNode,
+};
+
+// Assist: bind_unused_param
+//
+// Binds unused function parameter to an underscore.
+//
+// ```
+// fn some_function(x: i32$0) {}
+// ```
+// ->
+// ```
+// fn some_function(x: i32) {
+// let _ = x;
+// }
+// ```
+pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let param: ast::Param = ctx.find_node_at_offset()?;
+
+ let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None };
+
+ let param_def = {
+ let local = ctx.sema.to_def(&ident_pat)?;
+ Definition::Local(local)
+ };
+ if param_def.usages(&ctx.sema).at_least_one() {
+ cov_mark::hit!(keep_used);
+ return None;
+ }
+
+ let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
+ let stmt_list = func.body()?.stmt_list()?;
+ let l_curly_range = stmt_list.l_curly_token()?.text_range();
+ let r_curly_range = stmt_list.r_curly_token()?.text_range();
+
+ acc.add(
+ AssistId("bind_unused_param", AssistKind::QuickFix),
+ &format!("Bind as `let _ = {};`", &ident_pat),
+ param.syntax().text_range(),
+ |builder| {
+ let line_index = ctx.db().line_index(ctx.file_id());
+
+ let indent = func.indent_level();
+ let text_indent = indent + 1;
+ let mut text = format!("\n{text_indent}let _ = {ident_pat};");
+
+ let left_line = line_index.line_col(l_curly_range.end()).line;
+ let right_line = line_index.line_col(r_curly_range.start()).line;
+
+ if left_line == right_line {
+ cov_mark::hit!(single_line);
+ text.push_str(&format!("\n{indent}"));
+ }
+
+ builder.insert(l_curly_range.end(), text);
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn bind_unused_empty_block() {
+ cov_mark::check!(single_line);
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo($0y: i32) {}
+"#,
+ r#"
+fn foo(y: i32) {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn bind_unused_empty_block_with_newline() {
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo($0y: i32) {
+}
+"#,
+ r#"
+fn foo(y: i32) {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn bind_unused_generic() {
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo<T>($0y: T)
+where T : Default {
+}
+"#,
+ r#"
+fn foo<T>(y: T)
+where T : Default {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn trait_impl() {
+ check_assist(
+ bind_unused_param,
+ r#"
+trait Trait {
+ fn foo(x: i32);
+}
+impl Trait for () {
+ fn foo($0x: i32) {}
+}
+"#,
+ r#"
+trait Trait {
+ fn foo(x: i32);
+}
+impl Trait for () {
+ fn foo(x: i32) {
+ let _ = x;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn keep_used() {
+ cov_mark::check!(keep_used);
+ check_assist_not_applicable(
+ bind_unused_param,
+ r#"
+fn foo(x: i32, $0y: i32) { y; }
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
index 1af52c5..d231708 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -103,7 +103,6 @@
cond,
ast::Expr::BinExpr(_)
| ast::Expr::BlockExpr(_)
- | ast::Expr::BoxExpr(_)
| ast::Expr::BreakExpr(_)
| ast::Expr::CastExpr(_)
| ast::Expr::ClosureExpr(_)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index c3d925c..31a1ff4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -15,26 +15,13 @@
// Move an expression out of a format string.
//
// ```
-// macro_rules! format_args {
-// ($lit:literal $(tt:tt)*) => { 0 },
-// }
-// macro_rules! print {
-// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-// }
-//
+// # //- minicore: fmt
// fn main() {
// print!("{var} {x + 1}$0");
// }
// ```
// ->
// ```
-// macro_rules! format_args {
-// ($lit:literal $(tt:tt)*) => { 0 },
-// }
-// macro_rules! print {
-// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-// }
-//
// fn main() {
// print!("{var} {}"$0, x + 1);
// }
@@ -158,37 +145,21 @@
use super::*;
use crate::tests::check_assist;
- const MACRO_DECL: &'static str = r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-"#;
-
- fn add_macro_decl(s: &'static str) -> String {
- MACRO_DECL.to_string() + s
- }
-
#[test]
fn multiple_middle_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, y + 2, x + 1, 2);
}
"#,
- ),
);
}
@@ -196,20 +167,17 @@
fn single_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{obj.value:b}$0",);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b}"$0, obj.value);
}
"#,
- ),
);
}
@@ -217,20 +185,17 @@
fn multiple_middle_placeholders_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
}
"#,
- ),
);
}
@@ -238,20 +203,17 @@
fn multiple_trailing_args() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -259,20 +221,17 @@
fn improper_commas() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -280,20 +239,17 @@
fn nested_tt() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("My name is {} {x$0 + x}", stringify!(Paperino))
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("My name is {} {}"$0, stringify!(Paperino), x + x)
}
"#,
- ),
);
}
@@ -301,22 +257,19 @@
fn extract_only_expressions() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {x$0 + x}")
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {}"$0, x + x)
}
"#,
- ),
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index ea7a21e..de591cf 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -531,7 +531,7 @@
fn extracted_from_trait_impl(&self) -> bool {
match self.node().ancestors().find_map(ast::Impl::cast) {
- Some(c) => return c.trait_().is_some(),
+ Some(c) => c.trait_().is_some(),
None => false,
}
}
@@ -1048,23 +1048,17 @@
fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> {
let mut list = Vec::new();
if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) {
- match parent_item {
- ast::Item::Fn(ref fn_) => {
- if let Some(parent_parent) = parent_item
- .syntax()
- .parent()
- .and_then(|it| it.parent())
- .and_then(ast::Item::cast)
- {
- match parent_parent {
- ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
- ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
- _ => (),
- }
+ if let ast::Item::Fn(ref fn_) = parent_item {
+ if let Some(parent_parent) =
+ parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast)
+ {
+ match parent_parent {
+ ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
+ ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
+ _ => (),
}
- list.push(GenericParent::Fn(fn_.clone()));
}
- _ => (),
+ list.push(GenericParent::Fn(fn_.clone()));
}
}
list
@@ -1728,7 +1722,7 @@
let block = match &fun.body {
FunctionBody::Expr(expr) => {
let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
- let expr = ast::Expr::cast(expr).unwrap();
+ let expr = ast::Expr::cast(expr).expect("Body segment should be an expr");
match expr {
ast::Expr::BlockExpr(block) => {
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
@@ -1868,9 +1862,8 @@
if let Some(stmt_list) = block.stmt_list() {
stmt_list.syntax().children_with_tokens().for_each(|node_or_token| {
- match &node_or_token {
- syntax::NodeOrToken::Token(_) => elements.push(node_or_token),
- _ => (),
+ if let syntax::NodeOrToken::Token(_) = &node_or_token {
+ elements.push(node_or_token)
};
});
}
@@ -1934,12 +1927,18 @@
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
{
- ted::replace(node.syntax(), node.expr().unwrap().syntax());
+ ted::replace(
+ node.syntax(),
+ node.expr().expect("RefExpr::expr() cannot be None").syntax(),
+ );
}
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
{
- ted::replace(node.syntax(), node.expr().unwrap().syntax());
+ ted::replace(
+ node.syntax(),
+ node.expr().expect("RefExpr::expr() cannot be None").syntax(),
+ );
}
Some(_) | None => {
let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs
new file mode 100644
index 0000000..663df26
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs
@@ -0,0 +1,205 @@
+use hir::{AsAssocItem, HirDisplay};
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ famous_defs::FamousDefs,
+};
+use syntax::{ast, AstNode};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: into_to_qualified_from
+//
+// Convert an `into` method call to a fully qualified `from` call.
+//
+// ```
+// //- minicore: from
+// struct B;
+// impl From<i32> for B {
+// fn from(a: i32) -> Self {
+// B
+// }
+// }
+//
+// fn main() -> () {
+// let a = 3;
+// let b: B = a.in$0to();
+// }
+// ```
+// ->
+// ```
+// struct B;
+// impl From<i32> for B {
+// fn from(a: i32) -> Self {
+// B
+// }
+// }
+//
+// fn main() -> () {
+// let a = 3;
+// let b: B = B::from(a);
+// }
+// ```
+pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+ let nameref = method_call.name_ref()?;
+ let receiver = method_call.receiver()?;
+ let db = ctx.db();
+ let sema = &ctx.sema;
+ let fnc = sema.resolve_method_call(&method_call)?;
+ let scope = sema.scope(method_call.syntax())?;
+ // Check if the method call refers to Into trait.
+ if fnc.as_assoc_item(db)?.containing_trait_impl(db)?
+ == FamousDefs(sema, scope.krate()).core_convert_Into()?
+ {
+ let type_call = sema.type_of_expr(&method_call.clone().into())?;
+ let type_call_disp =
+ type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?;
+
+ acc.add(
+ AssistId("into_to_qualified_from", AssistKind::Generate),
+ "Convert `into` to fully qualified `from`",
+ nameref.syntax().text_range(),
+ |edit| {
+ edit.replace(
+ method_call.syntax().text_range(),
+ format!("{}::from({})", type_call_disp, receiver),
+ );
+ },
+ );
+ }
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_assist;
+
+ use super::into_to_qualified_from;
+
+ #[test]
+ fn two_types_in_same_mod() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = a.in$0to();
+}"#,
+ r#"
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = B::from(a);
+}"#,
+ )
+ }
+
+ #[test]
+ fn fromed_in_child_mod_imported() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+use C::B;
+
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = a.in$0to();
+}"#,
+ r#"
+use C::B;
+
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = B::from(a);
+}"#,
+ )
+ }
+
+ #[test]
+ fn fromed_in_child_mod_not_imported() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: C::B = a.in$0to();
+}"#,
+ r#"
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: C::B = C::B::from(a);
+}"#,
+ )
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index 5cc110c..6ed9bd8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -76,12 +76,19 @@
let name = to_upper_snake_case(&name.to_string());
let usages = Definition::Local(local).usages(&ctx.sema).all();
if let Some(usages) = usages.references.get(&ctx.file_id()) {
- let name = make::name_ref(&name);
+ let name_ref = make::name_ref(&name);
for usage in usages {
let Some(usage) = usage.name.as_name_ref().cloned() else { continue };
- let usage = edit.make_mut(usage);
- ted::replace(usage.syntax(), name.clone_for_update().syntax());
+ if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage) {
+ let record_field = edit.make_mut(record_field);
+ let name_expr =
+ make::expr_path(make::path_from_text(&name)).clone_for_update();
+ record_field.replace_expr(name_expr);
+ } else {
+ let usage = edit.make_mut(usage);
+ ted::replace(usage.syntax(), name_ref.clone_for_update().syntax());
+ }
}
}
@@ -120,8 +127,7 @@
is_const &=
sema.resolve_method_call(&call).map(|it| it.is_const(sema.db)).unwrap_or(true)
}
- ast::Expr::BoxExpr(_)
- | ast::Expr::ForExpr(_)
+ ast::Expr::ForExpr(_)
| ast::Expr::ReturnExpr(_)
| ast::Expr::TryExpr(_)
| ast::Expr::YieldExpr(_)
@@ -180,6 +186,33 @@
}
#[test]
+ fn usage_in_field_shorthand() {
+ check_assist(
+ promote_local_to_const,
+ r"
+struct Foo {
+ bar: usize,
+}
+
+fn main() {
+ let $0bar = 0;
+ let foo = Foo { bar };
+}
+",
+ r"
+struct Foo {
+ bar: usize,
+}
+
+fn main() {
+ const $0BAR: usize = 0;
+ let foo = Foo { bar: BAR };
+}
+",
+ )
+ }
+
+ #[test]
fn not_applicable_non_const_meth_call() {
cov_mark::check!(promote_local_non_const);
check_assist_not_applicable(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index e2b8222..cffa3f5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -113,10 +113,7 @@
Some(parent) => match (expr, parent) {
(ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
(
- ast::Expr::BoxExpr(_)
- | ast::Expr::PrefixExpr(_)
- | ast::Expr::RefExpr(_)
- | ast::Expr::MacroExpr(_),
+ ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_) | ast::Expr::MacroExpr(_),
ast::Expr::AwaitExpr(_)
| ast::Expr::CallExpr(_)
| ast::Expr::CastExpr(_)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
index 24c3387..61e9bcd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
@@ -48,6 +48,11 @@
return None;
}
+ let new_result_ty =
+ make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update();
+ let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?;
+ let last_genarg = generic_args.generic_args().last()?;
+
acc.add(
AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
"Wrap return type in Result",
@@ -75,19 +80,12 @@
ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax());
}
- let new_result_ty =
- make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update();
let old_result_ty = edit.make_mut(type_ref.clone());
ted::replace(old_result_ty.syntax(), new_result_ty.syntax());
if let Some(cap) = ctx.config.snippet_cap {
- let generic_args = new_result_ty
- .syntax()
- .descendants()
- .find_map(ast::GenericArgList::cast)
- .unwrap();
- edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap());
+ edit.add_placeholder_snippet(cap, last_genarg);
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 2ebb5ef..6f973ab 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -114,6 +114,7 @@
mod add_turbo_fish;
mod apply_demorgan;
mod auto_import;
+ mod bind_unused_param;
mod change_visibility;
mod convert_bool_then;
mod convert_comment_block;
@@ -211,6 +212,7 @@
mod unwrap_result_return_type;
mod unqualify_method_call;
mod wrap_return_type_in_result;
+ mod into_to_qualified_from;
pub(crate) fn all() -> &'static [Handler] {
&[
@@ -224,6 +226,7 @@
add_turbo_fish::add_turbo_fish,
apply_demorgan::apply_demorgan,
auto_import::auto_import,
+ bind_unused_param::bind_unused_param,
change_visibility::change_visibility,
convert_bool_then::convert_bool_then_to_if,
convert_bool_then::convert_if_to_bool_then,
@@ -274,6 +277,7 @@
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
+ into_to_qualified_from::into_to_qualified_from,
introduce_named_generic::introduce_named_generic,
introduce_named_lifetime::introduce_named_lifetime,
invert_if::invert_if,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 6eadc3d..dfaa534 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -266,6 +266,21 @@
}
#[test]
+fn doctest_bind_unused_param() {
+ check_doc_test(
+ "bind_unused_param",
+ r#####"
+fn some_function(x: i32$0) {}
+"#####,
+ r#####"
+fn some_function(x: i32) {
+ let _ = x;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_change_visibility() {
check_doc_test(
"change_visibility",
@@ -694,25 +709,12 @@
check_doc_test(
"extract_expressions_from_format_string",
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
+//- minicore: fmt
fn main() {
print!("{var} {x + 1}$0");
}
"#####,
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
fn main() {
print!("{var} {}"$0, x + 1);
}
@@ -1755,6 +1757,40 @@
}
#[test]
+fn doctest_into_to_qualified_from() {
+ check_doc_test(
+ "into_to_qualified_from",
+ r#####"
+//- minicore: from
+struct B;
+impl From<i32> for B {
+ fn from(a: i32) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a = 3;
+ let b: B = a.in$0to();
+}
+"#####,
+ r#####"
+struct B;
+impl From<i32> for B {
+ fn from(a: i32) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a = 3;
+ let b: B = B::from(a);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_introduce_named_generic() {
check_doc_test(
"introduce_named_generic",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index f74ebfa..16704d5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -103,7 +103,6 @@
match expr {
ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
- ast::Expr::BoxExpr(inner) => next_expr = inner.expr(),
ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
// ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 62bdb6e..87a2867 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -1,10 +1,8 @@
//! Completion for cfg
-use std::iter;
-
use ide_db::SymbolKind;
use itertools::Itertools;
-use syntax::SyntaxKind;
+use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind};
use crate::{completions::Completions, context::CompletionContext, CompletionItem};
@@ -15,31 +13,44 @@
acc.add(completion.build(ctx.db));
};
- let previous = iter::successors(ctx.original_token.prev_token(), |t| {
- (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia())
- .then(|| t.prev_token())
- .flatten()
- })
- .find(|t| matches!(t.kind(), SyntaxKind::IDENT));
+ // FIXME: Move this into context/analysis.rs
+ let previous = ctx
+ .original_token
+ .prev_token()
+ .and_then(|it| {
+ if matches!(it.kind(), SyntaxKind::EQ) {
+ Some(it.into())
+ } else {
+ algo::non_trivia_sibling(it.into(), Direction::Prev)
+ }
+ })
+ .filter(|t| matches!(t.kind(), SyntaxKind::EQ))
+ .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev))
+ .map(|it| match it {
+ NodeOrToken::Node(_) => None,
+ NodeOrToken::Token(t) => Ident::cast(t),
+ });
+ match previous {
+ Some(None) => (),
+ Some(Some(p)) => match p.text() {
+ "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion),
+ "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion),
+ "target_os" => KNOWN_OS.iter().copied().for_each(add_completion),
+ "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion),
+ "target_endian" => ["little", "big"].into_iter().for_each(add_completion),
+ name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
+ let insert_text = format!(r#""{s}""#);
+ let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
+ item.insert_text(insert_text);
- match previous.as_ref().map(|p| p.text()) {
- Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion),
- Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion),
- Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion),
- Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion),
- Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion),
- Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
- let insert_text = format!(r#""{s}""#);
- let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
- item.insert_text(insert_text);
-
- acc.add(item.build(ctx.db));
- }),
+ acc.add(item.build(ctx.db));
+ }),
+ },
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
acc.add(item.build(ctx.db));
}),
- };
+ }
}
const KNOWN_ARCH: [&str; 20] = [
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 9447bc7..90dac19 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -1,6 +1,6 @@
//! Completion for derives
-use hir::{HasAttrs, ScopeDef};
-use ide_db::SymbolKind;
+use hir::ScopeDef;
+use ide_db::{documentation::HasDocs, SymbolKind};
use itertools::Itertools;
use syntax::SmolStr;
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index 6bc6f34..f9dec53 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -1,5 +1,5 @@
//! Completion for lints
-use ide_db::{generated::lints::Lint, SymbolKind};
+use ide_db::{documentation::Documentation, generated::lints::Lint, SymbolKind};
use syntax::ast;
use crate::{context::CompletionContext, item::CompletionItem, Completions};
@@ -55,7 +55,7 @@
_ => name.to_owned(),
};
let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
- item.documentation(hir::Documentation::new(description.to_owned()));
+ item.documentation(Documentation::new(description.to_owned()));
item.add_to(acc, ctx.db)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
index 0d0e143..f9cde44 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
@@ -1,7 +1,7 @@
//! Completion for extern crates
-use hir::{HasAttrs, Name};
-use ide_db::SymbolKind;
+use hir::Name;
+use ide_db::{documentation::HasDocs, SymbolKind};
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 8e904fd..cecbe75 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -51,9 +51,7 @@
fn works_when_wrapped() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
@@ -70,9 +68,7 @@
fn no_completion_without_brace() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("f$0");
@@ -87,18 +83,13 @@
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{f$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
@@ -108,18 +99,13 @@
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 269e40e..42dfbfc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -33,8 +33,8 @@
use hir::{self, HasAttrs};
use ide_db::{
- path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node,
- traits::get_missing_assoc_items, SymbolKind,
+ documentation::HasDocs, path_transform::PathTransform,
+ syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind,
};
use syntax::{
ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 2ffe123..fc21bba 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -2,8 +2,12 @@
mod format_like;
-use hir::{Documentation, HasAttrs};
-use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap};
+use ide_db::{
+ documentation::{Documentation, HasDocs},
+ imports::insert_use::ImportScope,
+ ty_filter::TryEnum,
+ SnippetCap,
+};
use syntax::{
ast::{self, make, AstNode, AstToken},
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index e9831a5..3ff68b9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -1,7 +1,6 @@
//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
-use hir::Documentation;
-use ide_db::{imports::insert_use::ImportScope, SnippetCap};
+use ide_db::{documentation::Documentation, imports::insert_use::ImportScope, SnippetCap};
use crate::{
context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index 0309952..c45cc8d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -2,8 +2,11 @@
use std::fmt;
-use hir::{Documentation, Mutability};
-use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind};
+use hir::Mutability;
+use ide_db::{
+ documentation::Documentation, imports::import_assets::LocatedImport, RootDatabase, SnippetCap,
+ SymbolKind,
+};
use itertools::Itertools;
use smallvec::SmallVec;
use stdx::{impl_from, never};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 1953eb4..dfe8fe7 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -12,7 +12,10 @@
use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
use ide_db::{
- helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind,
+ documentation::{Documentation, HasDocs},
+ helpers::item_name,
+ imports::import_assets::LocatedImport,
+ RootDatabase, SnippetCap, SymbolKind,
};
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
@@ -114,7 +117,7 @@
}
// FIXME: remove this
- fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> {
+ fn docs(&self, def: impl HasDocs) -> Option<Documentation> {
def.docs(self.db())
}
}
@@ -409,7 +412,7 @@
}
}
-fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> {
+fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentation> {
use hir::ModuleDef::*;
match resolution {
ScopeDef::ModuleDef(Module(it)) => it.docs(db),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index 728d236..b218502 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -1,7 +1,10 @@
//! Renderer for `enum` variants.
-use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
-use ide_db::SymbolKind;
+use hir::{db::HirDatabase, StructKind};
+use ide_db::{
+ documentation::{Documentation, HasDocs},
+ SymbolKind,
+};
use crate::{
context::{CompletionContext, PathCompletionCtx, PathKind},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index ce7af1d..68d175c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -1,7 +1,7 @@
//! Renderer for macro invocations.
-use hir::{Documentation, HirDisplay};
-use ide_db::SymbolKind;
+use hir::HirDisplay;
+use ide_db::{documentation::Documentation, SymbolKind};
use syntax::SmolStr;
use crate::{
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index d06abc5..6f99811 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -1,7 +1,7 @@
//! Renderer for patterns.
-use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
-use ide_db::SnippetCap;
+use hir::{db::HirDatabase, Name, StructKind};
+use ide_db::{documentation::HasDocs, SnippetCap};
use itertools::Itertools;
use syntax::SmolStr;
@@ -103,7 +103,7 @@
label: SmolStr,
lookup: SmolStr,
pat: String,
- def: impl HasAttrs + Copy,
+ def: impl HasDocs + Copy,
adt_ty: hir::Type,
// Missing in context of match statement completions
is_variant_missing: bool,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
index 1aaf395..d8c134c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
@@ -67,11 +67,6 @@
}
#[test]
-fn inside_nested_attr() {
- check(r#"#[cfg($0)]"#, expect![[]])
-}
-
-#[test]
fn with_existing_attr() {
check(
r#"#[no_mangle] #[$0] mcall!();"#,
@@ -636,6 +631,32 @@
use super::*;
#[test]
+ fn inside_cfg() {
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg($0)]
+"#,
+ expect![[r#"
+ ba dbg
+ ba opt_level
+ ba test
+ "#]],
+ );
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg(b$0)]
+"#,
+ expect![[r#"
+ ba dbg
+ ba opt_level
+ ba test
+ "#]],
+ );
+ }
+
+ #[test]
fn cfg_target_endian() {
check(
r#"#[cfg(target_endian = $0"#,
@@ -644,6 +665,13 @@
ba little
"#]],
);
+ check(
+ r#"#[cfg(target_endian = b$0"#,
+ expect![[r#"
+ ba big
+ ba little
+ "#]],
+ );
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
new file mode 100644
index 0000000..26f3cd2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -0,0 +1,281 @@
+//! Documentation attribute related utilties.
+use either::Either;
+use hir::{
+ db::{DefDatabase, HirDatabase},
+ resolve_doc_path_on, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile,
+};
+use itertools::Itertools;
+use syntax::{
+ ast::{self, IsString},
+ AstToken,
+};
+use text_edit::{TextRange, TextSize};
+
+/// Holds documentation
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Documentation(String);
+
+impl Documentation {
+ pub fn new(s: String) -> Self {
+ Documentation(s)
+ }
+
+ pub fn as_str(&self) -> &str {
+ &self.0
+ }
+}
+
+impl From<Documentation> for String {
+ fn from(Documentation(string): Documentation) -> Self {
+ string
+ }
+}
+
+pub trait HasDocs: HasAttrs {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef>;
+}
+/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
+#[derive(Debug)]
+pub struct DocsRangeMap {
+ source_map: AttrSourceMap,
+ // (docstring-line-range, attr_index, attr-string-range)
+ // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
+ // the original (untrimmed) syntax doc line
+ mapping: Vec<(TextRange, AttrId, TextRange)>,
+}
+
+impl DocsRangeMap {
+ /// Maps a [`TextRange`] relative to the documentation string back to its AST range
+ pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
+ let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
+ let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
+ if !line_docs_range.contains_range(range) {
+ return None;
+ }
+
+ let relative_range = range - line_docs_range.start();
+
+ let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
+ match source {
+ Either::Left(attr) => {
+ let string = get_doc_string_in_attr(attr)?;
+ let text_range = string.open_quote_text_range()?;
+ let range = TextRange::at(
+ text_range.end() + original_line_src_range.start() + relative_range.start(),
+ string.syntax().text_range().len().min(range.len()),
+ );
+ Some(InFile { file_id, value: range })
+ }
+ Either::Right(comment) => {
+ let text_range = comment.syntax().text_range();
+ let range = TextRange::at(
+ text_range.start()
+ + TextSize::try_from(comment.prefix().len()).ok()?
+ + original_line_src_range.start()
+ + relative_range.start(),
+ text_range.len().min(range.len()),
+ );
+ Some(InFile { file_id, value: range })
+ }
+ }
+ }
+}
+
+pub fn docs_with_rangemap(
+ db: &dyn DefDatabase,
+ attrs: &AttrsWithOwner,
+) -> Option<(Documentation, DocsRangeMap)> {
+ let docs =
+ attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
+ let indent = doc_indent(attrs);
+ let mut buf = String::new();
+ let mut mapping = Vec::new();
+ for (doc, idx) in docs {
+ if !doc.is_empty() {
+ let mut base_offset = 0;
+ for raw_line in doc.split('\n') {
+ let line = raw_line.trim_end();
+ let line_len = line.len();
+ let (offset, line) = match line.char_indices().nth(indent) {
+ Some((offset, _)) => (offset, &line[offset..]),
+ None => (0, line),
+ };
+ let buf_offset = buf.len();
+ buf.push_str(line);
+ mapping.push((
+ TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
+ idx,
+ TextRange::at(
+ (base_offset + offset).try_into().ok()?,
+ line_len.try_into().ok()?,
+ ),
+ ));
+ buf.push('\n');
+ base_offset += raw_line.len() + 1;
+ }
+ } else {
+ buf.push('\n');
+ }
+ }
+ buf.pop();
+ if buf.is_empty() {
+ None
+ } else {
+ Some((Documentation(buf), DocsRangeMap { mapping, source_map: attrs.source_map(db) }))
+ }
+}
+
+pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
+ let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value());
+ let indent = doc_indent(attrs);
+ let mut buf = String::new();
+ for doc in docs {
+ // str::lines doesn't yield anything for the empty string
+ if !doc.is_empty() {
+ buf.extend(Itertools::intersperse(
+ doc.lines().map(|line| {
+ line.char_indices()
+ .nth(indent)
+ .map_or(line, |(offset, _)| &line[offset..])
+ .trim_end()
+ }),
+ "\n",
+ ));
+ }
+ buf.push('\n');
+ }
+ buf.pop();
+ if buf.is_empty() {
+ None
+ } else {
+ Some(buf)
+ }
+}
+
+macro_rules! impl_has_docs {
+ ($($def:ident,)*) => {$(
+ impl HasDocs for hir::$def {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ docs_from_attrs(&self.attrs(db)).map(Documentation)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>
+ ) -> Option<hir::DocLinkDef> {
+ resolve_doc_path_on(db, self, link, ns)
+ }
+ }
+ )*};
+}
+
+impl_has_docs![
+ Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module,
+ Impl,
+];
+
+macro_rules! impl_has_docs_enum {
+ ($($variant:ident),* for $enum:ident) => {$(
+ impl HasDocs for hir::$variant {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ hir::$enum::$variant(self).docs(db)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>
+ ) -> Option<hir::DocLinkDef> {
+ hir::$enum::$variant(self).resolve_doc_path(db, link, ns)
+ }
+ }
+ )*};
+}
+
+impl_has_docs_enum![Struct, Union, Enum for Adt];
+
+impl HasDocs for hir::AssocItem {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ match self {
+ hir::AssocItem::Function(it) => it.docs(db),
+ hir::AssocItem::Const(it) => it.docs(db),
+ hir::AssocItem::TypeAlias(it) => it.docs(db),
+ }
+ }
+
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef> {
+ match self {
+ hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
+ hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
+ hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
+ }
+ }
+}
+
+impl HasDocs for hir::ExternCrateDecl {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ let crate_docs =
+ docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from);
+ let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from);
+ match (decl_docs, crate_docs) {
+ (None, None) => None,
+ (Some(decl_docs), None) => Some(decl_docs),
+ (None, Some(crate_docs)) => Some(crate_docs),
+ (Some(mut decl_docs), Some(crate_docs)) => {
+ decl_docs.push('\n');
+ decl_docs.push('\n');
+ decl_docs += &crate_docs;
+ Some(decl_docs)
+ }
+ }
+ .map(Documentation::new)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef> {
+ resolve_doc_path_on(db, self, link, ns)
+ }
+}
+
+fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
+ match it.expr() {
+ // #[doc = lit]
+ Some(ast::Expr::Literal(lit)) => match lit.kind() {
+ ast::LiteralKind::String(it) => Some(it),
+ _ => None,
+ },
+ // #[cfg_attr(..., doc = "", ...)]
+ None => {
+ // FIXME: See highlight injection for what to do here
+ None
+ }
+ _ => None,
+ }
+}
+
+fn doc_indent(attrs: &hir::Attrs) -> usize {
+ attrs
+ .by_key("doc")
+ .attrs()
+ .filter_map(|attr| attr.string_value())
+ .flat_map(|s| s.lines())
+ .filter(|line| !line.chars().all(|c| c.is_whitespace()))
+ .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
+ .min()
+ .unwrap_or(0)
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index ac3511b..226def4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -22,6 +22,7 @@
pub mod traits;
pub mod ty_filter;
pub mod use_trivial_constructor;
+pub mod documentation;
pub mod imports {
pub mod import_assets;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index aa0bb7c..353a974 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -71,12 +71,29 @@
sema: &Semantics<'_, RootDatabase>,
new_name: &str,
) -> Result<SourceChange> {
+ // self.krate() returns None if
+ // self is a built-in attr, built-in type or tool module.
+ // it is not allowed for these defs to be renamed.
+ // cases where self.krate() is None is handled below.
+ if let Some(krate) = self.krate(sema.db) {
+ if !krate.origin(sema.db).is_local() {
+ bail!("Cannot rename a non-local definition.")
+ }
+ }
+
match *self {
Definition::Module(module) => rename_mod(sema, module, new_name),
+ Definition::ToolModule(_) => {
+ bail!("Cannot rename a tool module")
+ }
Definition::BuiltinType(_) => {
bail!("Cannot rename builtin type")
}
+ Definition::BuiltinAttr(_) => {
+ bail!("Cannot rename a builtin attr.")
+ }
Definition::SelfType(_) => bail!("Cannot rename `Self`"),
+ Definition::Macro(mac) => rename_reference(sema, Definition::Macro(mac), new_name),
def => rename_reference(sema, def, new_name),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
index e27e238..ab2a250 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
@@ -1,5 +1,7 @@
//! Rustdoc specific doc comment handling
+use crate::documentation::Documentation;
+
// stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933
pub fn is_rust_fence(s: &str) -> bool {
let mut seen_rust_tags = false;
@@ -32,3 +34,170 @@
!seen_other_tags || seen_rust_tags
}
+
+const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
+
+pub fn format_docs(src: &Documentation) -> String {
+ format_docs_(src.as_str())
+}
+
+fn format_docs_(src: &str) -> String {
+ let mut processed_lines = Vec::new();
+ let mut in_code_block = false;
+ let mut is_rust = false;
+
+ for mut line in src.lines() {
+ if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
+ continue;
+ }
+
+ if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
+ {
+ in_code_block ^= true;
+
+ if in_code_block {
+ is_rust = is_rust_fence(header);
+
+ if is_rust {
+ line = "```rust";
+ }
+ }
+ }
+
+ if in_code_block {
+ let trimmed = line.trim_start();
+ if is_rust && trimmed.starts_with("##") {
+ line = &trimmed[1..];
+ }
+ }
+
+ processed_lines.push(line);
+ }
+ processed_lines.join("\n")
+}
+
+fn code_line_ignored_by_rustdoc(line: &str) -> bool {
+ let trimmed = line.trim();
+ trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_format_docs_adds_rust() {
+ let comment = "```\nfn some_rust() {}\n```";
+ assert_eq!(format_docs_(comment), "```rust\nfn some_rust() {}\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_plain_text() {
+ let comment = "```text\nthis is plain text\n```";
+ assert_eq!(format_docs_(comment), "```text\nthis is plain text\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_non_rust() {
+ let comment = "```sh\nsupposedly shell code\n```";
+ assert_eq!(format_docs_(comment), "```sh\nsupposedly shell code\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_rust_alias() {
+ let comment = "```ignore\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_complex_code_block_attrs() {
+ let comment = "```rust,no_run\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_error_codes() {
+ let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet b = 0 as *const _;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_skips_comments_in_rust_block() {
+ let comment =
+ "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
+ assert_eq!(format_docs_(comment), "```rust\n#stay1\nstay2\n```");
+ }
+
+ #[test]
+ fn test_format_docs_does_not_skip_lines_if_plain_text() {
+ let comment =
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
+ assert_eq!(
+ format_docs_(comment),
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
+ );
+ }
+
+ #[test]
+ fn test_format_docs_keeps_comments_outside_of_rust_block() {
+ let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_format_docs_preserves_newlines() {
+ let comment = "this\nis\nmultiline";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_rust() {
+ let comment = r#"```rust
+fn main(){}
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_text() {
+ let comment = r#"```text
+filler
+text
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_format_docs_handles_escape_double_hashes() {
+ let comment = r#"```rust
+let s = "foo
+## bar # baz";
+```"#;
+
+ assert_eq!(format_docs_(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_double_hashes_non_rust() {
+ let comment = r#"```markdown
+## A second-level heading
+```"#;
+ assert_eq!(format_docs_(comment), "```markdown\n## A second-level heading\n```");
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 7e00d36..9c4f0ac 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -6,7 +6,7 @@
use std::mem;
-use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
+use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
};
@@ -221,7 +221,6 @@
}
// def is crate root
- // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
if module.is_crate_root() {
return SearchScope::reverse_dependencies(db, module.krate());
@@ -393,7 +392,10 @@
let name = match self.def {
// special case crate modules as these do not have a proper name
Definition::Module(module) if module.is_crate_root() => {
- // FIXME: This assumes the crate name is always equal to its display name when it really isn't
+ // FIXME: This assumes the crate name is always equal to its display name when it
+ // really isn't
+ // we should instead look at the dependency edge name and recursively search our way
+ // up the ancestors
module
.krate()
.display_name(self.sema.db)
@@ -468,6 +470,7 @@
};
for (text, file_id, search_range) in scope_files(sema, &search_scope) {
+ self.sema.db.unwind_if_cancelled();
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
@@ -504,6 +507,7 @@
let finder = &Finder::new("super");
for (text, file_id, search_range) in scope_files(sema, &scope) {
+ self.sema.db.unwind_if_cancelled();
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
for offset in match_indices(&text, finder, search_range) {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
index acf0a67..8302b01 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -1,10 +1,10 @@
//! Tools to work with format string literals for the `format_args!` family of macros.
-use crate::syntax_helpers::node_ext::macro_call_for_string_token;
use syntax::{
ast::{self, IsString},
- TextRange, TextSize,
+ AstNode, AstToken, TextRange, TextSize,
};
+// FIXME: This can probably be re-implemented via the HIR?
pub fn is_format_string(string: &ast::String) -> bool {
// Check if `string` is a format string argument of a macro invocation.
// `string` is a string literal, mapped down into the innermost macro expansion.
@@ -15,19 +15,9 @@
// This setup lets us correctly highlight the components of `concat!("{}", "bla")` format
// strings. It still fails for `concat!("{", "}")`, but that is rare.
(|| {
- let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?;
-
- if !matches!(
- name.text().as_str(),
- "format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021"
- ) {
- return None;
- }
-
- // NB: we match against `panic_2015`/`panic_2021` here because they have a special-cased arm for
- // `"{}"`, which otherwise wouldn't get highlighted.
-
- Some(())
+ let lit = string.syntax().parent().and_then(ast::Literal::cast)?;
+ let fa = lit.syntax().parent().and_then(ast::FormatArgsExpr::cast)?;
+ (fa.template()? == ast::Expr::Literal(lit)).then_some(|| ())
})()
.is_some()
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index 22ced69..e4e735c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -312,7 +312,6 @@
ast::Expr::ArrayExpr(_)
| ast::Expr::AwaitExpr(_)
| ast::Expr::BinExpr(_)
- | ast::Expr::BoxExpr(_)
| ast::Expr::BreakExpr(_)
| ast::Expr::CallExpr(_)
| ast::Expr::CastExpr(_)
@@ -335,7 +334,10 @@
| ast::Expr::LetExpr(_)
| ast::Expr::UnderscoreExpr(_)
| ast::Expr::YieldExpr(_)
- | ast::Expr::YeetExpr(_) => cb(expr),
+ | ast::Expr::YeetExpr(_)
+ | ast::Expr::OffsetOfExpr(_)
+ | ast::Expr::FormatArgsExpr(_)
+ | ast::Expr::AsmExpr(_) => cb(expr),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index f54cdd6..7ca0a0e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -157,6 +157,7 @@
fn macro_diag_builtin() {
check_diagnostics(
r#"
+//- minicore: fmt
#[rustc_builtin_macro]
macro_rules! env {}
@@ -166,9 +167,6 @@
#[rustc_builtin_macro]
macro_rules! compile_error {}
-#[rustc_builtin_macro]
-macro_rules! format_args { () => {} }
-
fn main() {
// Test a handful of built-in (eager) macros:
@@ -189,7 +187,7 @@
// Lazy:
format_args!();
- //^^^^^^^^^^^ error: no rule matches input tokens
+ //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 6238c7e..8265e0b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -1,10 +1,37 @@
+use either::Either;
+use hir::InFile;
use syntax::{
ast::{self, HasArgList},
- AstNode, TextRange,
+ AstNode, SyntaxNodePtr, TextRange,
};
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
+// Diagnostic: mismatched-tuple-struct-pat-arg-count
+//
+// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
+pub(crate) fn mismatched_tuple_struct_pat_arg_count(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::MismatchedTupleStructPatArgCount,
+) -> Diagnostic {
+ let s = if d.found == 1 { "" } else { "s" };
+ let s2 = if d.expected == 1 { "" } else { "s" };
+ let message = format!(
+ "this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
+ d.found, d.expected
+ );
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0023"),
+ message,
+ invalid_args_range(
+ ctx,
+ d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
+ d.expected,
+ d.found,
+ ),
+ )
+}
+
// Diagnostic: mismatched-arg-count
//
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
@@ -14,31 +41,63 @@
) -> Diagnostic {
let s = if d.expected == 1 { "" } else { "s" };
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
- Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d))
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0107"),
+ message,
+ invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found),
+ )
}
-fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange {
- adjusted_display_range::<ast::Expr>(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| {
- let arg_list = match expr {
- ast::Expr::CallExpr(call) => call.arg_list()?,
- ast::Expr::MethodCallExpr(call) => call.arg_list()?,
+fn invalid_args_range(
+ ctx: &DiagnosticsContext<'_>,
+ source: InFile<SyntaxNodePtr>,
+ expected: usize,
+ found: usize,
+) -> TextRange {
+ adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| {
+ let (text_range, r_paren_token, expected_arg) = match expr {
+ Either::Left(ast::Expr::CallExpr(call)) => {
+ let arg_list = call.arg_list()?;
+ (
+ arg_list.syntax().text_range(),
+ arg_list.r_paren_token(),
+ arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
+ Either::Left(ast::Expr::MethodCallExpr(call)) => {
+ let arg_list = call.arg_list()?;
+ (
+ arg_list.syntax().text_range(),
+ arg_list.r_paren_token(),
+ arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
+ Either::Right(pat) => {
+ let r_paren = pat.r_paren_token()?;
+ let l_paren = pat.l_paren_token()?;
+ (
+ l_paren.text_range().cover(r_paren.text_range()),
+ Some(r_paren),
+ pat.fields().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
_ => return None,
};
- if d.found < d.expected {
- if d.found == 0 {
- return Some(arg_list.syntax().text_range());
+ if found < expected {
+ if found == 0 {
+ return Some(text_range);
}
- if let Some(r_paren) = arg_list.r_paren_token() {
+ if let Some(r_paren) = r_paren_token {
return Some(r_paren.text_range());
}
}
- if d.expected < d.found {
- if d.expected == 0 {
- return Some(arg_list.syntax().text_range());
+ if expected < found {
+ if expected == 0 {
+ return Some(text_range);
}
- let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token());
+ let zip = expected_arg.zip(r_paren_token);
if let Some((arg, r_paren)) = zip {
- return Some(arg.syntax().text_range().cover(r_paren.text_range()));
+ return Some(arg.cover(r_paren.text_range()));
}
}
@@ -331,4 +390,21 @@
"#,
)
}
+
+ #[test]
+ fn tuple_struct_pat() {
+ check_diagnostics(
+ r#"
+struct S(u32, u32);
+fn f(
+ S(a, b, c): S,
+ // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+ S(): S,
+ // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+ S(e, f, .., g, d): S
+ // ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
+) {}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 82a9a3b..06b03d3 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -319,6 +319,7 @@
match Either::A {
Either::A => (),
Either::B() => (),
+ // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field
}
}
"#,
@@ -334,9 +335,11 @@
fn main() {
match A::B(1, 2) {
A::B(_, _, _) => (),
+ // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
}
match A::B(1, 2) {
A::C(_) => (),
+ // ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields
}
}
"#,
@@ -846,6 +849,7 @@
struct Foo { }
fn main(f: Foo) {
match f { Foo { bar } => () }
+ // ^^^ error: no such field
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index e0c3bed..d056e5c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -76,7 +76,7 @@
"variable does not need to be mutable",
ast,
)
- .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
+ .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive.
.with_fixes(fixes)
}
@@ -1173,4 +1173,27 @@
"#,
);
}
+
+ #[test]
+ fn regression_15623() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+
+struct Foo;
+
+impl Foo {
+ fn needs_mut(&mut self) {}
+}
+
+fn foo(mut foo: Foo) {
+ let mut call_me = || {
+ let 0 = 1 else { return };
+ foo.needs_mut();
+ };
+ call_me();
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index a34a582..290c16c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,3 +1,4 @@
+use either::Either;
use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics};
use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
use syntax::{
@@ -12,22 +13,39 @@
//
// This diagnostic is triggered if created structure does not have field provided in record.
pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
- Diagnostic::new_with_syntax_node_ptr(
- ctx,
- DiagnosticCode::RustcHardError("E0559"),
- "no such field",
- d.field.clone().map(|it| it.into()),
- )
- .with_fixes(fixes(ctx, d))
+ let node = d.field.clone().map(|it| it.either(Into::into, Into::into));
+ if d.private {
+ // FIXME: quickfix to add required visibility
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0451"),
+ "field is private",
+ node,
+ )
+ } else {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0559"),
+ "no such field",
+ node,
+ )
+ .with_fixes(fixes(ctx, d))
+ }
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.field.file_id);
- missing_record_expr_field_fixes(
- &ctx.sema,
- d.field.file_id.original_file(ctx.sema.db),
- &d.field.value.to_node(&root),
- )
+ // FIXME: quickfix for pattern
+ match &d.field.value {
+ Either::Left(ptr) => {
+ let root = ctx.sema.db.parse_or_expand(d.field.file_id);
+ missing_record_expr_field_fixes(
+ &ctx.sema,
+ d.field.file_id.original_file(ctx.sema.db),
+ &ptr.to_node(&root),
+ )
+ }
+ _ => None,
+ }
}
fn missing_record_expr_field_fixes(
@@ -118,13 +136,34 @@
r#"
struct S { foo: i32, bar: () }
impl S {
- fn new() -> S {
+ fn new(
+ s@S {
+ //^ 💡 error: missing structure fields:
+ //| - bar
+ foo,
+ baz: baz2,
+ //^^^^^^^^^ error: no such field
+ qux
+ //^^^ error: no such field
+ }: S
+ ) -> S {
+ S {
+ //^ 💡 error: missing structure fields:
+ //| - bar
+ foo,
+ baz: baz2,
+ //^^^^^^^^^ error: no such field
+ qux
+ //^^^ error: no such field
+ } = s;
S {
//^ 💡 error: missing structure fields:
//| - bar
foo: 92,
baz: 62,
//^^^^^^^ 💡 error: no such field
+ qux
+ //^^^ error: no such field
}
}
}
@@ -295,4 +334,38 @@
"#,
)
}
+
+ #[test]
+ fn test_struct_field_private() {
+ check_diagnostics(
+ r#"
+mod m {
+ pub struct Struct {
+ field: u32,
+ field2: u32,
+ }
+}
+fn f(s@m::Struct {
+ field: f,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+}: m::Struct) {
+ // assignee expression
+ m::Struct {
+ field: 0,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+ } = s;
+ m::Struct {
+ field: 0,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+ };
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 7de9a9a..495ea74 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -35,6 +35,25 @@
}
#[test]
+ fn while_let_loop_with_label_in_condition() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ let mut optional = Some(0);
+
+ 'my_label: while let Some(a) = match optional {
+ None => break 'my_label,
+ Some(val) => Some(val),
+ } {
+ optional = None;
+ continue 'my_label;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn for_loop() {
check_diagnostics(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
index 0aa439f..c4ac59e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
@@ -1,3 +1,4 @@
+use hir::InFile;
use ide_db::{base_db::FileId, source_change::SourceChange};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxNode};
@@ -39,6 +40,7 @@
"Unnecessary braces in use statement".to_string(),
use_range,
)
+ .with_main_node(InFile::new(file_id.into(), node.clone()))
.with_fixes(Some(vec![fix(
"remove_braces",
"Remove unnecessary braces",
@@ -156,4 +158,23 @@
"#,
);
}
+
+ #[test]
+ fn respect_lint_attributes_for_unused_braces() {
+ check_diagnostics(
+ r#"
+mod b {}
+#[allow(unused_braces)]
+use {b};
+"#,
+ );
+ check_diagnostics(
+ r#"
+mod b {}
+#[deny(unused_braces)]
+use {b};
+ //^^^ 💡 error: Unnecessary braces in use statement
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index b1b9b4b..ebe197a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -369,6 +369,7 @@
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
+ AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
};
res.push(d)
}
@@ -432,7 +433,8 @@
diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
) {
let file_id = sema.hir_file_for(root);
- for ev in root.preorder() {
+ let mut preorder = root.preorder();
+ while let Some(ev) = preorder.next() {
match ev {
syntax::WalkEvent::Enter(node) => {
for attr in node.children().filter_map(ast::Attr::cast) {
@@ -515,7 +517,7 @@
let Some((tag, args_tt)) = attr.as_simple_call() else {
return;
};
- let serevity = match tag.as_str() {
+ let severity = match tag.as_str() {
"allow" => Severity::Allow,
"warn" => Severity::Warning,
"forbid" | "deny" => Severity::Error,
@@ -523,12 +525,12 @@
};
for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() {
if let Some(lint) = lint.as_single_name_ref() {
- job(rustc_stack.entry(lint.to_string()).or_default(), serevity);
+ job(rustc_stack.entry(lint.to_string()).or_default(), severity);
}
if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) {
if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) {
if tool.to_string() == "clippy" {
- job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity);
+ job(clippy_stack.entry(name_ref.to_string()).or_default(), severity);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
index f994c28..fb79b5d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
@@ -94,10 +94,9 @@
enum_
.variants(db)
.into_iter()
- .map(|variant| {
+ .filter_map(|variant| {
variant.source(db).and_then(|node| name_range(db, node, file_id))
})
- .flatten()
.for_each(|range| {
let (annotation_range, target_position) = mk_ranges(range);
annotations.push(Annotation {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 901f7a2..37a1776 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -16,6 +16,7 @@
use ide_db::{
base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
defs::{Definition, NameClass, NameRefClass},
+ documentation::{docs_with_rangemap, Documentation, HasDocs},
helpers::pick_best_token,
RootDatabase,
};
@@ -171,7 +172,7 @@
/// Extracts all links from a given markdown text returning the definition text range, link-text
/// and the namespace if known.
pub(crate) fn extract_definitions_from_docs(
- docs: &hir::Documentation,
+ docs: &Documentation,
) -> Vec<(TextRange, String, Option<hir::Namespace>)> {
Parser::new_with_broken_link_callback(
docs.as_str(),
@@ -297,7 +298,7 @@
let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
let (attributes, def) = doc_attributes(sema, &node)?;
- let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
+ let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?;
let (in_expansion_range, link, ns) =
extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
let mapped = doc_mapping.map(range)?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 8036c77..9ae70ae 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,10 +1,11 @@
use std::ffi::OsStr;
use expect_test::{expect, Expect};
-use hir::{HasAttrs, Semantics};
+use hir::Semantics;
use ide_db::{
base_db::{FilePosition, FileRange},
defs::Definition,
+ documentation::{Documentation, HasDocs},
RootDatabase,
};
use itertools::Itertools;
@@ -78,7 +79,7 @@
fn def_under_cursor(
sema: &Semantics<'_, RootDatabase>,
position: &FilePosition,
-) -> (Definition, hir::Documentation) {
+) -> (Definition, Documentation) {
let (docs, def) = sema
.parse(position.file_id)
.syntax()
@@ -96,7 +97,7 @@
fn node_to_def(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
-) -> Option<Option<(Option<hir::Documentation>, Definition)>> {
+) -> Option<Option<(Option<Documentation>, Definition)>> {
Some(match_ast! {
match node {
ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))),
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index a33a6ee..f72ce37 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,12 +3,13 @@
use either::Either;
use hir::{
- Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout,
- LayoutError, Semantics, TypeInfo,
+ Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError,
+ Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
+ documentation::{Documentation, HasDocs},
famous_defs::FamousDefs,
generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
syntax_helpers::insert_whitespace_into_node,
@@ -470,7 +471,7 @@
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
}
- Definition::GenericParam(it) => label_and_docs(db, it),
+ Definition::GenericParam(it) => (it.display(db).to_string(), None),
Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
Definition::ExternCrateDecl(it) => label_and_docs(db, it),
// FIXME: We should be able to show more info about these
@@ -616,9 +617,9 @@
markup(Some(docs.replace('*', "\\*")), desc, None)
}
-fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
+fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
{
let label = def.display(db).to_string();
let docs = def.docs(db);
@@ -631,9 +632,9 @@
config: &HoverConfig,
layout_extractor: E,
layout_offset_extractor: E2,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Result<Layout, LayoutError>,
E2: Fn(&Layout) -> Option<u64>,
{
@@ -657,9 +658,9 @@
value_extractor: E,
layout_extractor: E2,
layout_tag_extractor: E3,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Option<V>,
E2: Fn(&D) -> Result<Layout, LayoutError>,
E3: Fn(&Layout) -> Option<usize>,
@@ -686,9 +687,9 @@
db: &RootDatabase,
def: D,
value_extractor: E,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Option<V>,
V: Display,
{
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index d0f9f7b..81d6db5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -6574,3 +6574,23 @@
"#]],
);
}
+
+#[test]
+fn format_args_arg() {
+ check(
+ r#"
+//- minicore: fmt
+fn test() {
+ let foo = 0;
+ format_args!("{}", foo$0);
+}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ let foo: i32 // size = 4, align = 4
+ ```
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 2925916..a5d070f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -52,6 +52,28 @@
pub closure_style: ClosureStyle,
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
+ pub fields_to_resolve: InlayFieldsToResolve,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct InlayFieldsToResolve {
+ pub resolve_text_edits: bool,
+ pub resolve_hint_tooltip: bool,
+ pub resolve_label_tooltip: bool,
+ pub resolve_label_location: bool,
+ pub resolve_label_command: bool,
+}
+
+impl InlayFieldsToResolve {
+ pub const fn empty() -> Self {
+ Self {
+ resolve_text_edits: false,
+ resolve_hint_tooltip: false,
+ resolve_label_tooltip: false,
+ resolve_label_location: false,
+ resolve_label_command: false,
+ }
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -123,11 +145,13 @@
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<TextEdit>,
+ pub needs_resolve: bool,
}
impl InlayHint {
fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
+ needs_resolve: false,
range,
kind,
label: InlayHintLabel::from(")"),
@@ -139,6 +163,7 @@
}
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
+ needs_resolve: false,
range,
kind,
label: InlayHintLabel::from("("),
@@ -196,6 +221,10 @@
}),
}
}
+
+ pub fn needs_resolve(&self) -> bool {
+ self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
+ }
}
impl From<String> for InlayHintLabel {
@@ -529,6 +558,7 @@
#[cfg(test)]
mod tests {
+
use expect_test::Expect;
use hir::ClosureStyle;
use itertools::Itertools;
@@ -538,7 +568,7 @@
use crate::DiscriminantHints;
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
- use super::ClosureReturnTypeHints;
+ use super::{ClosureReturnTypeHints, InlayFieldsToResolve};
pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never,
@@ -559,6 +589,7 @@
param_names_for_lifetime_elision_hints: false,
max_length: None,
closing_brace_hints_min_lines: None,
+ fields_to_resolve: InlayFieldsToResolve::empty(),
};
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
type_hints: true,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 6d6bd31..631807d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -137,21 +137,23 @@
}
_ => continue,
};
+ let label = InlayHintLabel::simple(
+ if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
+ Some(InlayTooltip::Markdown(format!(
+ "`{}` → `{}` ({coercion} coercion)",
+ source.display(sema.db),
+ target.display(sema.db),
+ ))),
+ None,
+ );
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(),
pad_left: false,
pad_right: false,
position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
kind: InlayKind::Adjustment,
- label: InlayHintLabel::simple(
- if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
- Some(InlayTooltip::Markdown(format!(
- "`{}` → `{}` ({coercion} coercion)",
- source.display(sema.db),
- target.display(sema.db),
- ))),
- None,
- ),
+ label,
text_edit: None,
});
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 07b9f9c..680035c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -99,6 +99,7 @@
None => pat.syntax().text_range(),
};
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve() || text_edit.is_some(),
range: match type_ascriptable {
Some(Some(t)) => text_range.cover(t.text_range()),
_ => text_range,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 343cf17..35504ff 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -50,9 +50,10 @@
_ => return,
};
acc.push(InlayHint {
+ needs_resolve: false,
range,
kind: InlayKind::BindingMode,
- label: r.to_string().into(),
+ label: r.into(),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
@@ -68,9 +69,10 @@
hir::BindingMode::Ref(Mutability::Shared) => "ref",
};
acc.push(InlayHint {
+ needs_resolve: false,
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
- label: bm.to_string().into(),
+ label: bm.into(),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index b621a8d..12e46c0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -57,10 +57,12 @@
}
}
}
+ let label = label_of_ty(famous_defs, config, &ty)?;
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(),
kind: InlayKind::Chaining,
- label: label_of_ty(famous_defs, config, &ty)?,
+ label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: true,
@@ -128,6 +130,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 147..154,
@@ -152,6 +155,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -221,6 +225,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 143..179,
@@ -245,6 +250,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -298,6 +304,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 143..179,
@@ -322,6 +329,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -389,6 +397,7 @@
"<i32, bool>>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 246..265,
@@ -426,6 +435,7 @@
"<i32, bool>>",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -474,7 +484,7 @@
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -487,7 +497,7 @@
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -495,6 +505,7 @@
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..224,
@@ -511,7 +522,7 @@
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -524,7 +535,7 @@
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -532,6 +543,7 @@
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..206,
@@ -548,7 +560,7 @@
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -561,7 +573,7 @@
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -569,6 +581,7 @@
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..189,
@@ -593,6 +606,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -655,6 +669,7 @@
],
},
),
+ needs_resolve: true,
},
InlayHint {
range: 145..185,
@@ -679,6 +694,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 145..168,
@@ -703,6 +719,7 @@
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 222..228,
@@ -725,6 +742,7 @@
},
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 2cefd5a..2b68538 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -109,6 +109,7 @@
let linked_location = name_range.map(|range| FileRange { file_id, range });
acc.push(InlayHint {
+ needs_resolve: linked_location.is_some(),
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
index 9d5defc..d691303 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -31,9 +31,10 @@
let range = closure.syntax().first_token()?.prev_token()?.text_range();
let range = TextRange::new(range.end() - TextSize::from(1), range.end());
acc.push(InlayHint {
+ needs_resolve: false,
range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple("move", None, None),
+ label: InlayHintLabel::from("move"),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -43,6 +44,7 @@
}
};
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("("),
@@ -59,23 +61,25 @@
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
+ let label = InlayHintLabel::simple(
+ format!(
+ "{}{}",
+ match capture.kind() {
+ hir::CaptureKind::SharedRef => "&",
+ hir::CaptureKind::UniqueSharedRef => "&unique ",
+ hir::CaptureKind::MutableRef => "&mut ",
+ hir::CaptureKind::Move => "",
+ },
+ capture.display_place(sema.db)
+ ),
+ None,
+ source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+ );
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: move_kw_range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple(
- format!(
- "{}{}",
- match capture.kind() {
- hir::CaptureKind::SharedRef => "&",
- hir::CaptureKind::UniqueSharedRef => "&unique ",
- hir::CaptureKind::MutableRef => "&mut ",
- hir::CaptureKind::Move => "",
- },
- capture.display_place(sema.db)
- ),
- None,
- source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
- ),
+ label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -84,9 +88,10 @@
if idx != last {
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple(", ", None, None),
+ label: InlayHintLabel::from(", "),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -95,6 +100,7 @@
}
}
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(")"),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index 3b41db0..204967c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -64,6 +64,7 @@
};
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve() || text_edit.is_some(),
range: param_list.syntax().text_range(),
kind: InlayKind::Type,
label,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index c4d2ac7..26dc6fa 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -79,6 +79,7 @@
None,
);
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: match eq_token {
Some(t) => range.cover(t.text_range()),
_ => range,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index 5fce11b..7b05e32 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -22,6 +22,7 @@
}
let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
+ needs_resolve: false,
range: t.text_range(),
kind: InlayKind::Lifetime,
label: label.into(),
@@ -185,6 +186,7 @@
let angle_tok = gpl.l_angle_token()?;
let is_empty = gpl.generic_params().next().is_none();
acc.push(InlayHint {
+ needs_resolve: false,
range: angle_tok.text_range(),
kind: InlayKind::Lifetime,
label: format!(
@@ -200,6 +202,7 @@
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
+ needs_resolve: false,
range: func.name()?.syntax().text_range(),
kind: InlayKind::GenericParamList,
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index fc297a8..f18e642 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -31,9 +31,10 @@
if ty.lifetime().is_none() {
let t = ty.amp_token()?;
acc.push(InlayHint {
+ needs_resolve: false,
range: t.text_range(),
kind: InlayKind::Lifetime,
- label: "'static".to_owned().into(),
+ label: "'static".into(),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index c4f43f4..b4260d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -57,6 +57,7 @@
let label =
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
InlayHint {
+ needs_resolve: label.needs_resolve(),
range,
kind: InlayKind::Parameter,
label,
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index c9cdbff..aee03d2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -91,9 +91,9 @@
MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
},
inlay_hints::{
- AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
- InlayTooltip, LifetimeElisionHints,
+ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
+ InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition,
+ InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
@@ -111,7 +111,7 @@
HighlightConfig, HlRange,
},
};
-pub use hir::{Documentation, Semantics};
+pub use hir::Semantics;
pub use ide_assists::{
Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
};
@@ -124,6 +124,7 @@
Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
SourceRoot, SourceRootId,
},
+ documentation::Documentation,
label::Label,
line_index::{LineCol, LineIndex},
search::{ReferenceCategory, SearchScope},
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 0740bfb..32f211c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -4,14 +4,15 @@
use either::Either;
use hir::{
- symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource,
- HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
+ symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, HirDisplay, HirFileId,
+ InFile, LocalSource, ModuleSource,
};
use ide_db::{
base_db::{FileId, FileRange},
- SymbolKind,
+ defs::Definition,
+ documentation::{Documentation, HasDocs},
+ RootDatabase, SymbolKind,
};
-use ide_db::{defs::Definition, RootDatabase};
use stdx::never;
use syntax::{
ast::{self, HasName},
@@ -327,7 +328,7 @@
impl<D> TryToNav for D
where
- D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay,
+ D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay,
D::Ast: ast::HasName,
{
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index dae8e71..ac9df5e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -2634,4 +2634,33 @@
// ",
// );
}
+
+ #[test]
+ fn disallow_renaming_for_non_local_definition() {
+ check(
+ "Baz",
+ r#"
+//- /lib.rs crate:lib new_source_root:library
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+use lib::S$0;
+"#,
+ "error: Cannot rename a non-local definition.",
+ );
+ }
+
+ #[test]
+ fn disallow_renaming_for_builtin_macros() {
+ check(
+ "Baz",
+ r#"
+//- minicore: derive, hash
+//- /main.rs crate:main
+use core::hash::Hash;
+#[derive(H$0ash)]
+struct A;
+ "#,
+ "error: Cannot rename a non-local definition.",
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 5f87a78..2d528c6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -7,6 +7,7 @@
use ide_db::{
base_db::{FilePosition, FileRange},
defs::Definition,
+ documentation::docs_from_attrs,
helpers::visit_file_defs,
search::SearchScope,
FxHashMap, FxHashSet, RootDatabase, SymbolKind,
@@ -496,7 +497,7 @@
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
- attrs.docs().map_or(false, |doc| {
+ docs_from_attrs(attrs).map_or(false, |doc| {
let mut in_code_block = false;
for line in String::from(doc).lines() {
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 847ab3d..e020b52 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -4,12 +4,11 @@
use std::collections::BTreeSet;
use either::Either;
-use hir::{
- AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait,
-};
+use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
use ide_db::{
active_parameter::{callable_for_node, generic_def_for_node},
base_db::FilePosition,
+ documentation::{Documentation, HasDocs},
FxIndexMap,
};
use stdx::format_to;
@@ -28,7 +27,7 @@
/// edited.
#[derive(Debug)]
pub struct SignatureHelp {
- pub doc: Option<String>,
+ pub doc: Option<Documentation>,
pub signature: String,
pub active_parameter: Option<usize>,
parameters: Vec<TextRange>,
@@ -179,7 +178,7 @@
let mut fn_params = None;
match callable.kind() {
hir::CallableKind::Function(func) => {
- res.doc = func.docs(db).map(|it| it.into());
+ res.doc = func.docs(db);
format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
@@ -187,11 +186,11 @@
});
}
hir::CallableKind::TupleStruct(strukt) => {
- res.doc = strukt.docs(db).map(|it| it.into());
+ res.doc = strukt.docs(db);
format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
- res.doc = variant.docs(db).map(|it| it.into());
+ res.doc = variant.docs(db);
format_to!(
res.signature,
"enum {}::{}",
@@ -265,38 +264,38 @@
let db = sema.db;
match generics_def {
hir::GenericDef::Function(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
// eg. `None::<u8>`
// We'll use the signature of the enum, but include the docs of the variant.
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
let enum_ = it.parent_enum(db);
format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index d869619..aabd26d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -12,6 +12,7 @@
};
use syntax::{AstNode, SyntaxKind::*, TextRange, T};
+use crate::inlay_hints::InlayFieldsToResolve;
use crate::{
hover::hover_for_definition,
inlay_hints::AdjustmentHintsMode,
@@ -125,6 +126,7 @@
max_length: Some(25),
closure_capture_hints: false,
closing_brace_hints_min_lines: Some(25),
+ fields_to_resolve: InlayFieldsToResolve::empty(),
},
file_id,
None,
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index d2c77e2..c9ee460 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -66,6 +66,7 @@
None => format!("{}", krate.into_raw()),
};
format_to!(buf, "Crate: {}\n", display_crate(krate));
+ format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options);
let deps = crate_graph[krate]
.dependencies
.iter()
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
index 2ed57e2..2ef1315 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
@@ -17,6 +17,7 @@
return;
}
+ // FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 8e96bfa..7d00282 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -617,6 +617,7 @@
CONST => SymbolKind::Const,
STATIC => SymbolKind::Static,
IDENT_PAT => SymbolKind::Local,
+ FORMAT_ARGS_ARG => SymbolKind::Local,
_ => return default.into(),
};
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 2657a64..71f4d07 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -5,8 +5,8 @@
use either::Either;
use hir::{InFile, Semantics};
use ide_db::{
- active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence,
- SymbolKind,
+ active_parameter::ActiveParameter, base_db::FileId, defs::Definition,
+ documentation::docs_with_rangemap, rust_doc::is_rust_fence, SymbolKind,
};
use syntax::{
ast::{self, AstNode, IsString, QuoteOffsets},
@@ -118,7 +118,7 @@
let src_file_id = src_file_id.into();
// Extract intra-doc links and emit highlights for them.
- if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) {
+ if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
extract_definitions_from_docs(&docs)
.into_iter()
.filter_map(|(range, link, ns)| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 3ac8aa9..64e614c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -45,17 +45,11 @@
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="brace">{</span>
- <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span><span class="parenthesis">)</span>
<span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">const_format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
@@ -75,7 +69,7 @@
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span>
- <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+ <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -92,7 +86,7 @@
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -114,18 +108,18 @@
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "The number is 1"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
@@ -140,10 +134,10 @@
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
@@ -167,16 +161,24 @@
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
<span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="none macro">toho</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more {}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{} asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
+ <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
+ <span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span>
+ <span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span>
+ <span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span>
+ <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span>
+ <span class="parenthesis macro">)</span><span class="semicolon">;</span>
+
+ <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 8749d35..542d899 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -401,19 +401,14 @@
// thus, we have to copy the macro definition from `std`
check_highlighting(
r#"
+//- minicore: fmt
macro_rules! println {
($($arg:tt)*) => ({
- $crate::io::_print($crate::format_args_nl!($($arg)*));
+ $crate::io::_print(format_args_nl!($($arg)*));
})
}
#[rustc_builtin_macro]
#[macro_export]
-macro_rules! format_args {}
-#[rustc_builtin_macro]
-#[macro_export]
-macro_rules! const_format_args {}
-#[rustc_builtin_macro]
-#[macro_export]
macro_rules! format_args_nl {}
mod panic {
@@ -433,7 +428,7 @@
$crate::panicking::panic_display(&$arg)
),
($fmt:expr, $($arg:tt)+) => (
- $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
+ $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+))
),
}
}
@@ -450,7 +445,7 @@
macro_rules! toho {
() => ($crate::panic!("not yet implemented"));
- ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+)));
+ ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+)));
}
fn main() {
@@ -534,7 +529,15 @@
assert!(true, "{}", 1);
assert!(true, "{} asdasd", 1);
toho!("{}fmt", 0);
- asm!("mov eax, {0}");
+ let i: u64 = 3;
+ let o: u64;
+ asm!(
+ "mov {0}, {1}",
+ "add {0}, 5",
+ out(reg) o,
+ in(reg) i,
+ );
+
format_args!(concat!("{}"), "{}");
format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash);
}"#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index c7e403f..b405097 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -32,7 +32,7 @@
pub(crate) use on_enter::on_enter;
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
-pub(crate) const TRIGGER_CHARS: &str = ".=<>{";
+pub(crate) const TRIGGER_CHARS: &str = ".=<>{(";
struct ExtendedTextEdit {
edit: TextEdit,
@@ -86,45 +86,57 @@
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
return None;
}
- return match char_typed {
+ let conv = |text_edit: Option<TextEdit>| {
+ Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
+ };
+ match char_typed {
'.' => conv(on_dot_typed(&file.tree(), offset)),
'=' => conv(on_eq_typed(&file.tree(), offset)),
'<' => on_left_angle_typed(&file.tree(), offset),
'>' => conv(on_right_angle_typed(&file.tree(), offset)),
- '{' => conv(on_opening_brace_typed(file, offset)),
- _ => return None,
- };
-
- fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> {
- Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
+ '{' => conv(on_opening_bracket_typed(file, offset, '{')),
+ '(' => conv(on_opening_bracket_typed(file, offset, '(')),
+ _ => None,
}
}
-/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
-/// block, or a part of a `use` item.
-fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
- if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
+/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a
+/// block, or a part of a `use` item (for `{`).
+fn on_opening_bracket_typed(
+ file: &Parse<SourceFile>,
+ offset: TextSize,
+ opening_bracket: char,
+) -> Option<TextEdit> {
+ let (closing_bracket, expected_ast_bracket) = match opening_bracket {
+ '{' => ('}', SyntaxKind::L_CURLY),
+ '(' => (')', SyntaxKind::L_PAREN),
+ _ => return None,
+ };
+
+ if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) {
return None;
}
let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
- if brace_token.kind() != SyntaxKind::L_CURLY {
+ if brace_token.kind() != expected_ast_bracket {
return None;
}
- // Remove the `{` to get a better parse tree, and reparse.
+ // Remove the opening bracket to get a better parse tree, and reparse.
let range = brace_token.text_range();
- if !stdx::always!(range.len() == TextSize::of('{')) {
+ if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
return None;
}
let file = file.reparse(&Indel::delete(range));
- if let Some(edit) = brace_expr(&file.tree(), offset) {
+ if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
return Some(edit);
}
- if let Some(edit) = brace_use_path(&file.tree(), offset) {
- return Some(edit);
+ if closing_bracket == '}' {
+ if let Some(edit) = brace_use_path(&file.tree(), offset) {
+ return Some(edit);
+ }
}
return None;
@@ -143,7 +155,12 @@
))
}
- fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
+ fn bracket_expr(
+ file: &SourceFile,
+ offset: TextSize,
+ opening_bracket: char,
+ closing_bracket: char,
+ ) -> Option<TextEdit> {
let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
if expr.syntax().text_range().start() != offset {
return None;
@@ -166,10 +183,10 @@
return None;
}
- // Insert `}` right after the expression.
+ // Insert the closing bracket right after the expression.
Some(TextEdit::insert(
- expr.syntax().text_range().end() + TextSize::of("{"),
- "}".to_string(),
+ expr.syntax().text_range().end() + TextSize::of(opening_bracket),
+ closing_bracket.to_string(),
))
}
}
@@ -938,6 +955,193 @@
}
#[test]
+ fn adds_closing_parenthesis_for_expr() {
+ type_char(
+ '(',
+ r#"
+fn f() { match () { _ => $0() } }
+ "#,
+ r#"
+fn f() { match () { _ => (()) } }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { $0() }
+ "#,
+ r#"
+fn f() { (()) }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { let x = $0(); }
+ "#,
+ r#"
+fn f() { let x = (()); }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { let x = $0a.b(); }
+ "#,
+ r#"
+fn f() { let x = (a.b()); }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+const S: () = $0();
+fn f() {}
+ "#,
+ r#"
+const S: () = (());
+fn f() {}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+const S: () = $0a.b();
+fn f() {}
+ "#,
+ r#"
+const S: () = (a.b());
+fn f() {}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() {
+ match x {
+ 0 => $0(),
+ 1 => (),
+ }
+}
+ "#,
+ r#"
+fn f() {
+ match x {
+ 0 => (()),
+ 1 => (),
+ }
+}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+ fn f() {
+ let z = Some($03);
+ }
+ "#,
+ r#"
+ fn f() {
+ let z = Some((3));
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_string_literal() {
+ // Regression test for #9351
+ type_char_noop(
+ '(',
+ r##"
+fn check_with(ra_fixture: &str, expect: Expect) {
+ let base = r#"
+enum E { T(), R$0, C }
+use self::E::X;
+const Z: E = E::C;
+mod m {}
+asdasdasdasdasdasda
+sdasdasdasdasdasda
+sdasdasdasdasd
+"#;
+ let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
+ expect.assert_eq(&actual)
+}
+ "##,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_item_position_with_macro() {
+ type_char_noop('(', r#"$0println!();"#);
+ type_char_noop(
+ '(',
+ r#"
+fn main() $0println!("hello");
+}"#,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_use_tree() {
+ type_char_noop(
+ '(',
+ r#"
+use some::$0Path;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::{Path, $0Other};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::{$0Path, Other};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::path::$0to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::$0path::to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use $0some::path::to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::path::$0to::{Item};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use $0Thing as _;
+ "#,
+ );
+
+ type_char_noop(
+ '(',
+ r#"
+use some::pa$0th::to::Item;
+ "#,
+ );
+ }
+
+ #[test]
fn adds_closing_angle_bracket_for_generic_args() {
type_char(
'<',
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index 4d56c77..89b302c 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -16,6 +16,5 @@
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.4.0", features = ["raw-api"] }
hashbrown.workspace = true
-once_cell = "1.17.0"
rustc-hash = "1.1.0"
triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index dabbf3a..2934d26 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -6,11 +6,11 @@
fmt::{self, Debug, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
+ sync::OnceLock,
};
use dashmap::{DashMap, SharedValue};
use hashbrown::{hash_map::RawEntryMut, HashMap};
-use once_cell::sync::OnceCell;
use rustc_hash::FxHasher;
use triomphe::Arc;
@@ -177,12 +177,12 @@
}
pub struct InternStorage<T: ?Sized> {
- map: OnceCell<InternMap<T>>,
+ map: OnceLock<InternMap<T>>,
}
impl<T: ?Sized> InternStorage<T> {
pub const fn new() -> Self {
- Self { map: OnceCell::new() }
+ Self { map: OnceLock::new() }
}
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index d8553d3..4197f24 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -1,3 +1,5 @@
+use crate::grammar::types::type_;
+
use super::*;
// test expr_literals
@@ -73,6 +75,9 @@
if let Some(m) = literal(p) {
return Some((m, BlockLike::NotBlock));
}
+ if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) {
+ return Some((builtin_expr(p)?, BlockLike::NotBlock));
+ }
if paths::is_path_start(p) {
return Some(path_expr(p, r));
}
@@ -93,7 +98,6 @@
m.complete(p, UNDERSCORE_EXPR)
}
T![loop] => loop_expr(p, None),
- T![box] => box_expr(p, None),
T![while] => while_expr(p, None),
T![try] => try_block_expr(p, None),
T![match] => match_expr(p),
@@ -212,6 +216,72 @@
m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR })
}
+// test builtin_expr
+// fn foo() {
+// builtin#asm(0);
+// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
+// builtin#offset_of(Foo, bar.baz.0);
+// }
+fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
+ let m = p.start();
+ p.bump_remap(T![builtin]);
+ p.bump(T![#]);
+ if p.at_contextual_kw(T![offset_of]) {
+ p.bump_remap(T![offset_of]);
+ p.expect(T!['(']);
+ type_(p);
+ p.expect(T![,]);
+ while !p.at(EOF) && !p.at(T![')']) {
+ if p.at(IDENT) || p.at(INT_NUMBER) {
+ name_ref_or_index(p);
+ // } else if p.at(FLOAT_NUMBER) {
+ // FIXME: needs float hack
+ } else {
+ p.err_and_bump("expected field name or number");
+ }
+ if !p.at(T![')']) {
+ p.expect(T![.]);
+ }
+ }
+ p.expect(T![')']);
+ Some(m.complete(p, OFFSET_OF_EXPR))
+ } else if p.at_contextual_kw(T![format_args]) {
+ p.bump_remap(T![format_args]);
+ p.expect(T!['(']);
+ expr(p);
+ if p.eat(T![,]) {
+ while !p.at(EOF) && !p.at(T![')']) {
+ let m = p.start();
+ if p.at(IDENT) && p.nth_at(1, T![=]) {
+ name(p);
+ p.bump(T![=]);
+ }
+ if expr(p).is_none() {
+ m.abandon(p);
+ break;
+ }
+ m.complete(p, FORMAT_ARGS_ARG);
+
+ if !p.at(T![')']) {
+ p.expect(T![,]);
+ }
+ }
+ }
+ p.expect(T![')']);
+ Some(m.complete(p, FORMAT_ARGS_EXPR))
+ } else if p.at_contextual_kw(T![asm]) {
+ p.bump_remap(T![asm]);
+ p.expect(T!['(']);
+ // FIXME: We just put expression here so highlighting kind of keeps working
+ expr(p);
+ p.expect(T![')']);
+ Some(m.complete(p, ASM_EXPR))
+ } else {
+ m.abandon(p);
+ None
+ }
+}
+
// test array_expr
// fn foo() {
// [];
@@ -662,19 +732,3 @@
}
m.complete(p, BLOCK_EXPR)
}
-
-// test box_expr
-// fn foo() {
-// let x = box 1i32;
-// let y = (box 1i32, box 2i32);
-// let z = Foo(box 1i32, box 2i32);
-// }
-fn box_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker {
- assert!(p.at(T![box]));
- let m = m.unwrap_or_else(|| p.start());
- p.bump(T![box]);
- if p.at_ts(EXPR_FIRST) {
- expr(p);
- }
- m.complete(p, BOX_EXPR)
-}
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index e4dce21..36c5295 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -221,6 +221,7 @@
rustc_lexer::TokenKind::Caret => T![^],
rustc_lexer::TokenKind::Percent => T![%],
rustc_lexer::TokenKind::Unknown => ERROR,
+ rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT,
rustc_lexer::TokenKind::UnknownPrefix => {
err = "unknown literal prefix";
IDENT
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 48f4076..db5278f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -105,12 +105,16 @@
WHILE_KW,
YIELD_KW,
AUTO_KW,
+ BUILTIN_KW,
DEFAULT_KW,
EXISTENTIAL_KW,
UNION_KW,
RAW_KW,
MACRO_RULES_KW,
YEET_KW,
+ OFFSET_OF_KW,
+ ASM_KW,
+ FORMAT_ARGS_KW,
INT_NUMBER,
FLOAT_NUMBER,
CHAR,
@@ -203,7 +207,10 @@
RECORD_EXPR,
RECORD_EXPR_FIELD_LIST,
RECORD_EXPR_FIELD,
- BOX_EXPR,
+ OFFSET_OF_EXPR,
+ ASM_EXPR,
+ FORMAT_ARGS_EXPR,
+ FORMAT_ARGS_ARG,
CALL_EXPR,
INDEX_EXPR,
METHOD_CALL_EXPR,
@@ -315,12 +322,16 @@
| WHILE_KW
| YIELD_KW
| AUTO_KW
+ | BUILTIN_KW
| DEFAULT_KW
| EXISTENTIAL_KW
| UNION_KW
| RAW_KW
| MACRO_RULES_KW
| YEET_KW
+ | OFFSET_OF_KW
+ | ASM_KW
+ | FORMAT_ARGS_KW
)
}
pub fn is_punct(self) -> bool {
@@ -435,12 +446,16 @@
pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
let kw = match ident {
"auto" => AUTO_KW,
+ "builtin" => BUILTIN_KW,
"default" => DEFAULT_KW,
"existential" => EXISTENTIAL_KW,
"union" => UNION_KW,
"raw" => RAW_KW,
"macro_rules" => MACRO_RULES_KW,
"yeet" => YEET_KW,
+ "offset_of" => OFFSET_OF_KW,
+ "asm" => ASM_KW,
+ "format_args" => FORMAT_ARGS_KW,
_ => return None,
};
Some(kw)
@@ -481,5 +496,5 @@
}
}
#[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
pub use T;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast
deleted file mode 100644
index b21f37cd..0000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast
+++ /dev/null
@@ -1,90 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "x"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "y"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- TUPLE_EXPR
- L_PAREN "("
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- COMMA ","
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "2i32"
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "z"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Foo"
- ARG_LIST
- L_PAREN "("
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- COMMA ","
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "2i32"
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE "\n"
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs
deleted file mode 100644
index fc9923b..0000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn foo() {
- let x = box 1i32;
- let y = (box 1i32, box 2i32);
- let z = Foo(box 1i32, box 2i32);
-}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
new file mode 100644
index 0000000..361900b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
@@ -0,0 +1,105 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ ASM_KW "asm"
+ L_PAREN "("
+ LITERAL
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ FORMAT_ARGS_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ FORMAT_ARGS_KW "format_args"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ LITERAL
+ INT_NUMBER "0"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ LITERAL
+ INT_NUMBER "1"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ NAME
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "3"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ OFFSET_OF_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ OFFSET_OF_KW "offset_of"
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ COMMA ","
+ WHITESPACE " "
+ NAME_REF
+ IDENT "bar"
+ DOT "."
+ NAME_REF
+ IDENT "baz"
+ DOT "."
+ NAME_REF
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
new file mode 100644
index 0000000..14431b0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
@@ -0,0 +1,5 @@
+fn foo() {
+ builtin#asm(0);
+ builtin#format_args("", 0, 1, a = 2 + 3, a + b);
+ builtin#offset_of(Foo, bar.baz.0);
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
index 19a5caa..7827157 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
@@ -71,6 +71,10 @@
.arg("--target-dir")
.arg(&target_dir);
+ if let Ok(target) = std::env::var("TARGET") {
+ cmd.args(["--target", &target]);
+ }
+
println!("Running {cmd:?}");
let output = cmd.output().unwrap();
diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
index 8392718..c5d55f7 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
@@ -2,14 +2,29 @@
use std::process::Command;
+use anyhow::Context;
use rustc_hash::FxHashMap;
-use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath};
+use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
+
+/// Determines how `rustc --print cfg` is discovered and invoked.
+///
+/// There options are supported:
+/// - [`RustcCfgConfig::Cargo`], which relies on `cargo rustc --print cfg`
+/// and `RUSTC_BOOTSTRAP`.
+/// - [`RustcCfgConfig::Explicit`], which uses an explicit path to the `rustc`
+/// binary in the sysroot.
+/// - [`RustcCfgConfig::Discover`], which uses [`toolchain::rustc`].
+pub(crate) enum RustcCfgConfig<'a> {
+ Cargo(&'a ManifestPath),
+ Explicit(&'a Sysroot),
+ Discover,
+}
pub(crate) fn get(
- cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ config: RustcCfgConfig<'_>,
) -> Vec<CfgFlag> {
let _p = profile::span("rustc_cfg::get");
let mut res = Vec::with_capacity(6 * 2 + 1);
@@ -25,49 +40,67 @@
// Add miri cfg, which is useful for mir eval in stdlib
res.push(CfgFlag::Atom("miri".into()));
- match get_rust_cfgs(cargo_toml, target, extra_env) {
- Ok(rustc_cfgs) => {
- tracing::debug!(
- "rustc cfgs found: {:?}",
- rustc_cfgs
- .lines()
- .map(|it| it.parse::<CfgFlag>().map(|it| it.to_string()))
- .collect::<Vec<_>>()
- );
- res.extend(rustc_cfgs.lines().filter_map(|it| it.parse().ok()));
+ let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
+
+ let rustc_cfgs = match rustc_cfgs {
+ Ok(cfgs) => cfgs,
+ Err(e) => {
+ tracing::error!(?e, "failed to get rustc cfgs");
+ return res;
}
- Err(e) => tracing::error!("failed to get rustc cfgs: {e:?}"),
+ };
+
+ let rustc_cfgs =
+ rustc_cfgs.lines().map(|it| it.parse::<CfgFlag>()).collect::<Result<Vec<_>, _>>();
+
+ match rustc_cfgs {
+ Ok(rustc_cfgs) => {
+ tracing::debug!(?rustc_cfgs, "rustc cfgs found");
+ res.extend(rustc_cfgs);
+ }
+ Err(e) => {
+ tracing::error!(?e, "failed to get rustc cfgs")
+ }
}
res
}
fn get_rust_cfgs(
- cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ config: RustcCfgConfig<'_>,
) -> anyhow::Result<String> {
- if let Some(cargo_toml) = cargo_toml {
- let mut cargo_config = Command::new(toolchain::cargo());
- cargo_config.envs(extra_env);
- cargo_config
- .current_dir(cargo_toml.parent())
- .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
- .env("RUSTC_BOOTSTRAP", "1");
- if let Some(target) = target {
- cargo_config.args(["--target", target]);
+ let mut cmd = match config {
+ RustcCfgConfig::Cargo(cargo_toml) => {
+ let mut cmd = Command::new(toolchain::cargo());
+ cmd.envs(extra_env);
+ cmd.current_dir(cargo_toml.parent())
+ .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
+ .env("RUSTC_BOOTSTRAP", "1");
+ if let Some(target) = target {
+ cmd.args(["--target", target]);
+ }
+
+ return utf8_stdout(cmd).context("Unable to run `cargo rustc`");
}
- match utf8_stdout(cargo_config) {
- Ok(it) => return Ok(it),
- Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"),
+ RustcCfgConfig::Explicit(sysroot) => {
+ let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into();
+ tracing::debug!(?rustc, "using explicit rustc from sysroot");
+ Command::new(rustc)
}
- }
- // using unstable cargo features failed, fall back to using plain rustc
- let mut cmd = Command::new(toolchain::rustc());
+ RustcCfgConfig::Discover => {
+ let rustc = toolchain::rustc();
+ tracing::debug!(?rustc, "using rustc from env");
+ Command::new(rustc)
+ }
+ };
+
cmd.envs(extra_env);
cmd.args(["--print", "cfg", "-O"]);
if let Some(target) = target {
cmd.args(["--target", target]);
}
- utf8_stdout(cmd)
+
+ utf8_stdout(cmd).context("Unable to run `rustc`")
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index da862c9..fe046dd 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -115,10 +115,19 @@
Ok(Sysroot::load(sysroot_dir, src))
}
- pub fn discover_rustc(&self) -> Option<ManifestPath> {
+ pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
get_rustc_src(&self.root)
}
+ pub fn discover_rustc(&self) -> Result<AbsPathBuf, std::io::Error> {
+ let rustc = self.root.join("bin/rustc");
+ tracing::debug!(?rustc, "checking for rustc binary at location");
+ match fs::metadata(&rustc) {
+ Ok(_) => Ok(rustc),
+ Err(e) => Err(e),
+ }
+ }
+
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
format_err!("can't load standard library from sysroot path {sysroot_dir}")
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 13463e9..e0209ca 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, process::Command, sync};
+use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync};
use anyhow::{format_err, Context};
use base_db::{
@@ -21,7 +21,7 @@
cargo_workspace::{DepKind, PackageData, RustLibSource},
cfg_flag::CfgFlag,
project_json::Crate,
- rustc_cfg,
+ rustc_cfg::{self, RustcCfgConfig},
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
@@ -240,9 +240,9 @@
Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
.map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
Some(RustLibSource::Discover) => {
- sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| {
- Some(format!("Failed to discover rustc source for sysroot."))
- })
+ sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
+ || Some(format!("Failed to discover rustc source for sysroot.")),
+ )
}
None => Err(None),
};
@@ -279,8 +279,11 @@
}
});
- let rustc_cfg =
- rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
+ let rustc_cfg = rustc_cfg::get(
+ config.target.as_deref(),
+ &config.extra_env,
+ RustcCfgConfig::Cargo(cargo_toml),
+ );
let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
@@ -331,11 +334,18 @@
}
(None, None) => Err(None),
};
- if let Ok(sysroot) = &sysroot {
- tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
- }
+ let config = match &sysroot {
+ Ok(sysroot) => {
+ tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
+ RustcCfgConfig::Explicit(sysroot)
+ }
+ Err(_) => {
+ tracing::debug!("discovering sysroot");
+ RustcCfgConfig::Discover
+ }
+ };
- let rustc_cfg = rustc_cfg::get(None, target, extra_env);
+ let rustc_cfg = rustc_cfg::get(target, extra_env, config);
ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
}
@@ -357,10 +367,18 @@
}
None => Err(None),
};
- if let Ok(sysroot) = &sysroot {
- tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
- }
- let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
+ let rustc_config = match &sysroot {
+ Ok(sysroot) => {
+ tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
+ RustcCfgConfig::Explicit(sysroot)
+ }
+ Err(_) => {
+ tracing::info!("discovering sysroot");
+ RustcCfgConfig::Discover
+ }
+ };
+
+ let rustc_cfg = rustc_cfg::get(None, &FxHashMap::default(), rustc_config);
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
}
@@ -730,6 +748,7 @@
)
});
+ let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned());
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
let crates: FxHashMap<CrateId, CrateId> = project
.crates()
@@ -754,9 +773,14 @@
let env = env.clone().into_iter().collect();
let target_cfgs = match target.as_deref() {
- Some(target) => cfg_cache
- .entry(target)
- .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
+ Some(target) => cfg_cache.entry(target).or_insert_with(|| {
+ let rustc_cfg = match sysroot {
+ Some(sysroot) => RustcCfgConfig::Explicit(sysroot),
+ None => RustcCfgConfig::Discover,
+ };
+
+ rustc_cfg::get(Some(target), extra_env, rustc_cfg)
+ }),
None => &rustc_cfg,
};
@@ -765,7 +789,12 @@
*edition,
display_name.clone(),
version.clone(),
- target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
+ target_cfgs
+ .iter()
+ .chain(cfg.iter())
+ .chain(iter::once(&r_a_cfg_flag))
+ .cloned()
+ .collect(),
None,
env,
*is_proc_macro,
@@ -820,7 +849,7 @@
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
- // Don't compute cfg and use this if present
+ // Don't compute cfg and use this if present, only used for the sysroot experiment hack
forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
@@ -842,12 +871,7 @@
None => (SysrootPublicDeps::default(), None),
};
- let cfg_options = {
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
- cfg_options.insert_atom("debug_assertions".into());
- cfg_options
- };
+ let cfg_options = create_cfg_options(rustc_cfg);
// Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
@@ -866,6 +890,9 @@
if cargo[pkg].is_local {
cfg_options.insert_atom("test".into());
}
+ if cargo[pkg].is_member {
+ cfg_options.insert_atom("rust_analyzer".into());
+ }
if !override_cfg.global.is_empty() {
cfg_options.apply_diff(override_cfg.global.clone());
@@ -1029,8 +1056,8 @@
None => (SysrootPublicDeps::default(), None),
};
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
+ let mut cfg_options = create_cfg_options(rustc_cfg);
+ cfg_options.insert_atom("rust_analyzer".into());
for detached_file in detached_files {
let file_id = match load(detached_file) {
@@ -1228,6 +1255,10 @@
let mut env = Env::default();
inject_cargo_env(pkg, &mut env);
+ if let Ok(cname) = String::from_str(cargo_name) {
+ // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark.
+ env.set("CARGO_CRATE_NAME", cname.replace("-", "_"));
+ }
if let Some(envs) = build_data.map(|it| &it.envs) {
for (k, v) in envs {
@@ -1291,8 +1322,7 @@
channel: Option<ReleaseChannel>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg.clone());
+ let cfg_options = create_cfg_options(rustc_cfg.clone());
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
Some(cargo) => handle_hack_cargo_workspace(
load,
@@ -1471,3 +1501,10 @@
env.set("CARGO_PKG_LICENSE_FILE", String::new());
}
+
+fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions {
+ let mut cfg_options = CfgOptions::default();
+ cfg_options.extend(rustc_cfg);
+ cfg_options.insert_atom("debug_assertions".into());
+ cfg_options
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index e595cd8..727d39a 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -162,7 +165,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -232,7 +236,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index e595cd8..727d39a 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -162,7 +165,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -232,7 +236,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index f10c55d..89728ba 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -80,6 +81,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -149,6 +151,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -159,7 +162,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -218,6 +221,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -228,7 +232,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index fb3f593..b7bf6cb 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -14,7 +14,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -53,7 +55,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -84,7 +88,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -115,7 +121,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -146,7 +154,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -192,7 +202,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -223,7 +235,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -311,7 +325,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -342,7 +358,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -373,7 +391,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -404,7 +424,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "rust_analyzer",
+ ],
),
potential_cfg_options: None,
env: Env {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 1f9d6db..7410f0a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -50,7 +50,6 @@
# These 3 deps are not used by r-a directly, but we list them here to lock in their versions
# in our transitive deps to prevent them from pulling in windows-sys 0.45.0
mio = "=0.8.5"
-filetime = "=0.2.19"
parking_lot_core = "=0.9.6"
cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index ab06b96..8c9261a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -16,10 +16,12 @@
};
use serde_json::json;
-use crate::config::{Config, RustfmtConfig};
-use crate::line_index::PositionEncoding;
-use crate::lsp_ext::negotiated_encoding;
-use crate::semantic_tokens;
+use crate::{
+ config::{Config, RustfmtConfig},
+ line_index::PositionEncoding,
+ lsp::semantic_tokens,
+ lsp_ext::negotiated_encoding,
+};
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
@@ -218,7 +220,7 @@
}
fn more_trigger_character(config: &Config) -> Vec<String> {
- let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()];
+ let mut res = vec![".".to_string(), ">".to_string(), "{".to_string(), "(".to_string()];
if config.snippet_cap() {
res.push("<".to_string());
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 4a03be1..dcb3ca6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -15,7 +15,10 @@
hir::{ExprId, PatId},
};
use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
-use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase};
+use ide::{
+ Analysis, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve, InlayHintsConfig, LineCol,
+ RootDatabase,
+};
use ide_db::{
base_db::{
salsa::{self, debug::DebugQueryTable, ParallelDatabase},
@@ -317,9 +320,13 @@
fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
let mut sw = self.stop_watch();
- let all = bodies.len() as u64;
+ let mut all = 0;
let mut fail = 0;
for &body in bodies {
+ if matches!(body, DefWithBody::Variant(_)) {
+ continue;
+ }
+ all += 1;
let Err(e) = db.mir_body(body.into()) else {
continue;
};
@@ -782,6 +789,7 @@
closure_style: hir::ClosureStyle::ImplFn,
max_length: Some(25),
closing_brace_hints_min_lines: Some(20),
+ fields_to_resolve: InlayFieldsToResolve::empty(),
},
file_id,
None,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 42d1801..d6a45ce 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -21,7 +21,7 @@
use crate::{
cli::flags,
line_index::{LineEndings, LineIndex, PositionEncoding},
- to_proto,
+ lsp::to_proto,
version::version,
};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 44337f9..8c056ff 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -51,7 +51,7 @@
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
tool_info: Some(scip_types::ToolInfo {
name: "rust-analyzer".to_owned(),
- version: "0.1".to_owned(),
+ version: format!("{}", crate::version::version()),
arguments: vec![],
special_fields: Default::default(),
})
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 40c50f6..ea3a212 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -13,8 +13,9 @@
use flycheck::FlycheckConfig;
use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
- HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
- JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
+ HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
+ InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+ Snippet, SnippetScope,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -1335,6 +1336,18 @@
}
pub fn inlay_hints(&self) -> InlayHintsConfig {
+ let client_capability_fields = self
+ .caps
+ .text_document
+ .as_ref()
+ .and_then(|text| text.inlay_hint.as_ref())
+ .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
+ .map(|inlay_resolve| inlay_resolve.properties.iter())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .collect::<FxHashSet<_>>();
+
InlayHintsConfig {
render_colons: self.data.inlayHints_renderColons,
type_hints: self.data.inlayHints_typeHints_enable,
@@ -1395,6 +1408,13 @@
} else {
None
},
+ fields_to_resolve: InlayFieldsToResolve {
+ resolve_text_edits: client_capability_fields.contains("textEdits"),
+ resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
+ resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
+ resolve_label_location: client_capability_fields.contains("label.location"),
+ resolve_label_command: client_capability_fields.contains("label.command"),
+ },
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index b65f38a..71701ef 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -9,7 +9,7 @@
use rustc_hash::FxHashSet;
use triomphe::Arc;
-use crate::lsp_ext;
+use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext};
pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
@@ -122,3 +122,41 @@
&& left.range == right.range
&& left.message == right.message
}
+
+pub(crate) fn fetch_native_diagnostics(
+ snapshot: GlobalStateSnapshot,
+ subscriptions: Vec<FileId>,
+) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
+ let _p = profile::span("fetch_native_diagnostics");
+ let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
+ subscriptions
+ .into_iter()
+ .filter_map(|file_id| {
+ let line_index = snapshot.file_line_index(file_id).ok()?;
+ let diagnostics = snapshot
+ .analysis
+ .diagnostics(
+ &snapshot.config.diagnostics(),
+ ide::AssistResolveStrategy::None,
+ file_id,
+ )
+ .ok()?
+ .into_iter()
+ .map(move |d| lsp_types::Diagnostic {
+ range: lsp::to_proto::range(&line_index, d.range),
+ severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&d.code.url()).unwrap(),
+ }),
+ source: Some("rust-analyzer".to_string()),
+ message: d.message,
+ related_information: None,
+ tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
+ data: None,
+ })
+ .collect::<Vec<_>>();
+ Some((file_id, diagnostics))
+ })
+ .collect()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 0656457..7315805 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -8,8 +8,8 @@
use vfs::{AbsPath, AbsPathBuf};
use crate::{
- global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext,
- to_proto::url_from_abs_path,
+ global_state::GlobalStateSnapshot, line_index::PositionEncoding,
+ lsp::to_proto::url_from_abs_path, lsp_ext,
};
use super::{DiagnosticsMapConfig, Fix};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
index 5e5cd9a..7da4311 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -8,9 +8,9 @@
use crate::{
global_state::{GlobalState, GlobalStateSnapshot},
+ lsp::LspError,
main_loop::Task,
version::version,
- LspError,
};
/// A visitor for routing a raw JSON request to an appropriate handler function.
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index ea8a697..c09f572 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -12,25 +12,27 @@
use load_cargo::SourceRootConfig;
use lsp_types::{SemanticTokens, Url};
use nohash_hasher::IntMap;
-use parking_lot::{Mutex, RwLock};
+use parking_lot::{
+ MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
+ RwLockWriteGuard,
+};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
-use vfs::AnchoredPathBuf;
+use vfs::{AnchoredPathBuf, Vfs};
use crate::{
config::{Config, ConfigError},
diagnostics::{CheckFixes, DiagnosticCollection},
- from_proto,
line_index::{LineEndings, LineIndex},
+ lsp::{from_proto, to_proto::url_from_abs_path},
lsp_ext,
main_loop::Task,
mem_docs::MemDocs,
op_queue::OpQueue,
reload,
task_pool::TaskPool,
- to_proto::url_from_abs_path,
};
// Enforces drop order
@@ -40,7 +42,7 @@
}
pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
-pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
+type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
/// `GlobalState` is the primary mutable state of the language server
///
@@ -49,6 +51,7 @@
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
+#[doc(alias = "GlobalMess")]
pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
@@ -66,6 +69,7 @@
// status
pub(crate) shutdown_requested: bool,
+ pub(crate) send_hint_refresh_query: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
// proc macros
@@ -177,6 +181,7 @@
mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false,
+ send_hint_refresh_query: false,
last_reported_status: None,
source_root_config: SourceRootConfig::default(),
config_errors: Default::default(),
@@ -216,12 +221,15 @@
let mut file_changes = FxHashMap::default();
let (change, changed_files, workspace_structure_change) = {
let mut change = Change::new();
- let (vfs, line_endings_map) = &mut *self.vfs.write();
- let changed_files = vfs.take_changes();
+ let mut guard = self.vfs.write();
+ let changed_files = guard.0.take_changes();
if changed_files.is_empty() {
return false;
}
+ // downgrade to read lock to allow more readers while we are normalizing text
+ let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
+ let vfs: &Vfs = &guard.0;
// We need to fix up the changed events a bit. If we have a create or modify for a file
// id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on.
@@ -272,6 +280,7 @@
let mut workspace_structure_change = None;
// A file was added or deleted
let mut has_structure_changes = false;
+ let mut bytes = vec![];
for file in &changed_files {
let vfs_path = &vfs.file_path(file.file_id);
if let Some(path) = vfs_path.as_path() {
@@ -293,16 +302,28 @@
let text = if file.exists() {
let bytes = vfs.file_contents(file.file_id).to_vec();
+
String::from_utf8(bytes).ok().and_then(|text| {
+ // FIXME: Consider doing normalization in the `vfs` instead? That allows
+ // getting rid of some locking
let (text, line_endings) = LineEndings::normalize(text);
- line_endings_map.insert(file.file_id, line_endings);
- Some(Arc::from(text))
+ Some((Arc::from(text), line_endings))
})
} else {
None
};
- change.change_file(file.file_id, text);
+ // delay `line_endings_map` changes until we are done normalizing the text
+ // this allows delaying the re-acquisition of the write lock
+ bytes.push((file.file_id, text));
}
+ let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
+ bytes.into_iter().for_each(|(file_id, text)| match text {
+ None => change.change_file(file_id, None),
+ Some((text, line_endings)) => {
+ line_endings_map.insert(file_id, line_endings);
+ change.change_file(file_id, Some(text));
+ }
+ });
if has_structure_changes {
let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
@@ -422,12 +443,16 @@
}
impl GlobalStateSnapshot {
+ fn vfs_read(&self) -> MappedRwLockReadGuard<'_, vfs::Vfs> {
+ RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
+ }
+
pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> {
- url_to_file_id(&self.vfs.read().0, url)
+ url_to_file_id(&self.vfs_read(), url)
}
pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
- file_id_to_url(&self.vfs.read().0, id)
+ file_id_to_url(&self.vfs_read(), id)
}
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
@@ -443,7 +468,7 @@
}
pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
- let mut base = self.vfs.read().0.file_path(path.anchor);
+ let mut base = self.vfs_read().file_path(path.anchor);
base.pop();
let path = base.join(&path.path).unwrap();
let path = path.as_path().unwrap();
@@ -451,7 +476,7 @@
}
pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
- self.vfs.read().0.file_path(file_id)
+ self.vfs_read().file_path(file_id)
}
pub(crate) fn cargo_target_for_crate_root(
@@ -459,7 +484,7 @@
crate_id: CrateId,
) -> Option<(&CargoWorkspace, Target)> {
let file_id = self.analysis.crate_root(crate_id).ok()?;
- let path = self.vfs.read().0.file_path(file_id);
+ let path = self.vfs_read().file_path(file_id);
let path = path.as_path()?;
self.workspaces.iter().find_map(|ws| match ws {
ProjectWorkspace::Cargo { cargo, .. } => {
@@ -471,7 +496,11 @@
}
pub(crate) fn vfs_memory_usage(&self) -> usize {
- self.vfs.read().0.memory_usage()
+ self.vfs_read().memory_usage()
+ }
+
+ pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
+ self.vfs.read().0.exists(file_id)
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index e830e5e..f9070d2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -13,8 +13,12 @@
use vfs::{AbsPathBuf, ChangeKind, VfsPath};
use crate::{
- config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams,
- lsp_utils::apply_document_changes, mem_docs::DocumentData, reload,
+ config::Config,
+ global_state::GlobalState,
+ lsp::{from_proto, utils::apply_document_changes},
+ lsp_ext::RunFlycheckParams,
+ mem_docs::DocumentData,
+ reload,
};
pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
@@ -84,15 +88,16 @@
}
};
- let vfs = &mut state.vfs.write().0;
- let file_id = vfs.file_id(&path).unwrap();
let text = apply_document_changes(
state.config.position_encoding(),
- || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
+ || {
+ let vfs = &state.vfs.read().0;
+ let file_id = vfs.file_id(&path).unwrap();
+ std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into()
+ },
params.content_changes,
);
-
- vfs.set_file_contents(path, Some(text.into_bytes()));
+ state.vfs.write().0.set_file_contents(path, Some(text.into_bytes()));
}
Ok(())
}
@@ -108,6 +113,10 @@
tracing::error!("orphan DidCloseTextDocument: {}", path);
}
+ if let Some(file_id) = state.vfs.read().0.file_id(&path) {
+ state.diagnostics.clear_native_for(file_id);
+ }
+
state.semantic_tokens_cache.lock().remove(¶ms.text_document.uri);
if let Some(path) = path.as_path() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 5f1f731..b8a1a39 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -11,8 +11,8 @@
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
- HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
- SingleResolve, SourceChange, TextEdit,
+ HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory,
+ Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
@@ -30,21 +30,23 @@
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
use triomphe::Arc;
-use vfs::{AbsPath, AbsPathBuf, VfsPath};
+use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
use crate::{
cargo_target_spec::CargoTargetSpec,
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
diff::diff,
- from_proto,
global_state::{GlobalState, GlobalStateSnapshot},
line_index::LineEndings,
+ lsp::{
+ from_proto, to_proto,
+ utils::{all_edits_are_disjoint, invalid_params_error},
+ LspError,
+ },
lsp_ext::{
self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
},
- lsp_utils::{all_edits_are_disjoint, invalid_params_error},
- to_proto, LspError,
};
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
@@ -354,7 +356,7 @@
// This should be a single-file edit
let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap();
- stdx::never!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
+ stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
Ok(Some(change))
@@ -972,7 +974,7 @@
PositionOrRange::Range(range) => range,
};
- let file_range = from_proto::file_range(&snap, params.text_document, range)?;
+ let file_range = from_proto::file_range(&snap, ¶ms.text_document, range)?;
let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
None => return Ok(None),
Some(info) => info,
@@ -1128,7 +1130,7 @@
let line_index =
snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?;
- let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?;
+ let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
let mut assists_config = snap.config.assist();
assists_config.allowed = params
@@ -1381,7 +1383,7 @@
let selections = params
.selections
.iter()
- .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
+ .map(|range| from_proto::file_range(&snap, ¶ms.position.text_document, *range))
.collect::<Result<Vec<_>, _>>()?;
let position = from_proto::file_position(&snap, params.position)?;
let source_change = snap.analysis.structural_search_replace(
@@ -1401,7 +1403,7 @@
let document_uri = ¶ms.text_document.uri;
let FileRange { file_id, range } = from_proto::file_range(
&snap,
- TextDocumentIdentifier::new(document_uri.to_owned()),
+ &TextDocumentIdentifier::new(document_uri.to_owned()),
params.range,
)?;
let line_index = snap.file_line_index(file_id)?;
@@ -1410,17 +1412,73 @@
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
.into_iter()
- .map(|it| to_proto::inlay_hint(&snap, &line_index, it))
+ .map(|it| {
+ to_proto::inlay_hint(
+ &snap,
+ &inlay_hints_config.fields_to_resolve,
+ &line_index,
+ file_id,
+ it,
+ )
+ })
.collect::<Cancellable<Vec<_>>>()?,
))
}
pub(crate) fn handle_inlay_hints_resolve(
- _snap: GlobalStateSnapshot,
- hint: InlayHint,
+ snap: GlobalStateSnapshot,
+ mut original_hint: InlayHint,
) -> anyhow::Result<InlayHint> {
let _p = profile::span("handle_inlay_hints_resolve");
- Ok(hint)
+
+ let data = match original_hint.data.take() {
+ Some(it) => it,
+ None => return Ok(original_hint),
+ };
+
+ let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
+ let file_id = FileId(resolve_data.file_id);
+ anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
+
+ let line_index = snap.file_line_index(file_id)?;
+ let range = from_proto::text_range(
+ &line_index,
+ lsp_types::Range { start: original_hint.position, end: original_hint.position },
+ )?;
+ let range_start = range.start();
+ let range_end = range.end();
+ let large_range = TextRange::new(
+ range_start.checked_sub(1.into()).unwrap_or(range_start),
+ range_end.checked_add(1.into()).unwrap_or(range_end),
+ );
+ let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
+ forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
+ let resolve_hints = snap.analysis.inlay_hints(
+ &forced_resolve_inlay_hints_config,
+ file_id,
+ Some(large_range),
+ )?;
+
+ let mut resolved_hints = resolve_hints
+ .into_iter()
+ .filter_map(|it| {
+ to_proto::inlay_hint(
+ &snap,
+ &forced_resolve_inlay_hints_config.fields_to_resolve,
+ &line_index,
+ file_id,
+ it,
+ )
+ .ok()
+ })
+ .filter(|hint| hint.position == original_hint.position)
+ .filter(|hint| hint.kind == original_hint.kind);
+ if let Some(resolved_hint) = resolved_hints.next() {
+ if resolved_hints.next().is_none() {
+ return Ok(resolved_hint);
+ }
+ }
+ Ok(original_hint)
}
pub(crate) fn handle_call_hierarchy_prepare(
@@ -1453,7 +1511,7 @@
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
+ let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let call_items = match snap.analysis.incoming_calls(fpos)? {
@@ -1488,7 +1546,7 @@
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
+ let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let call_items = match snap.analysis.outgoing_calls(fpos)? {
@@ -1569,18 +1627,21 @@
snap.config.highlighting_non_standard_tokens(),
);
- let mut cache = snap.semantic_tokens_cache.lock();
- let cached_tokens = cache.entry(params.text_document.uri).or_default();
+ let cached_tokens = snap.semantic_tokens_cache.lock().remove(¶ms.text_document.uri);
- if let Some(prev_id) = &cached_tokens.result_id {
+ if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) =
+ &cached_tokens
+ {
if *prev_id == params.previous_result_id {
let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
- *cached_tokens = semantic_tokens;
+ snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens);
return Ok(Some(delta.into()));
}
}
- *cached_tokens = semantic_tokens.clone();
+ // Clone first to keep the lock short
+ let semantic_tokens_clone = semantic_tokens.clone();
+ snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens_clone);
Ok(Some(semantic_tokens.into()))
}
@@ -1591,7 +1652,7 @@
) -> anyhow::Result<Option<SemanticTokensRangeResult>> {
let _p = profile::span("handle_semantic_tokens_range");
- let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
+ let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
@@ -1674,7 +1735,7 @@
) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> {
let _p = profile::span("handle_move_item");
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
- let range = from_proto::file_range(&snap, params.text_document, params.range)?;
+ let range = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
let direction = match params.direction {
lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
@@ -1879,12 +1940,15 @@
// Determine the edition of the crate the file belongs to (if there's multiple, we pick the
// highest edition).
- let editions = snap
+ let Ok(editions) = snap
.analysis
.relevant_crates_for(file_id)?
.into_iter()
.map(|crate_id| snap.analysis.crate_edition(crate_id))
- .collect::<Result<Vec<_>, _>>()?;
+ .collect::<Result<Vec<_>, _>>()
+ else {
+ return Ok(None);
+ };
let edition = editions.iter().copied().max();
let line_index = snap.file_line_index(file_id)?;
@@ -1894,23 +1958,7 @@
let mut cmd = process::Command::new(toolchain::rustfmt());
cmd.envs(snap.config.extra_env());
cmd.args(extra_args);
- // try to chdir to the file so we can respect `rustfmt.toml`
- // FIXME: use `rustfmt --config-path` once
- // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
- match text_document.uri.to_file_path() {
- Ok(mut path) => {
- // pop off file name
- if path.pop() && path.is_dir() {
- cmd.current_dir(path);
- }
- }
- Err(_) => {
- tracing::error!(
- "Unable to get file path for {}, rustfmt.toml might be ignored",
- text_document.uri
- );
- }
- }
+
if let Some(edition) = edition {
cmd.arg("--edition");
cmd.arg(edition.to_string());
@@ -1929,7 +1977,7 @@
.into());
}
- let frange = from_proto::file_range(snap, text_document, range)?;
+ let frange = from_proto::file_range(snap, &text_document, range)?;
let start_line = line_index.index.line_col(frange.range.start()).line;
let end_line = line_index.index.line_col(frange.range.end()).line;
@@ -1948,12 +1996,31 @@
}
RustfmtConfig::CustomCommand { command, args } => {
let mut cmd = process::Command::new(command);
+
cmd.envs(snap.config.extra_env());
cmd.args(args);
cmd
}
};
+ // try to chdir to the file so we can respect `rustfmt.toml`
+ // FIXME: use `rustfmt --config-path` once
+ // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
+ match text_document.uri.to_file_path() {
+ Ok(mut path) => {
+ // pop off file name
+ if path.pop() && path.is_dir() {
+ command.current_dir(path);
+ }
+ }
+ Err(_) => {
+ tracing::error!(
+ text_document = ?text_document.uri,
+ "Unable to get path, rustfmt.toml might be ignored"
+ );
+ }
+ }
+
let mut rustfmt = command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 57e26c2..6c62577 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -23,18 +23,13 @@
mod diagnostics;
mod diff;
mod dispatch;
-mod from_proto;
mod global_state;
mod line_index;
-mod lsp_utils;
mod main_loop;
-mod markdown;
mod mem_docs;
mod op_queue;
mod reload;
-mod semantic_tokens;
mod task_pool;
-mod to_proto;
mod version;
mod handlers {
@@ -43,13 +38,12 @@
}
pub mod config;
-pub mod lsp_ext;
+pub mod lsp;
+use self::lsp::ext as lsp_ext;
#[cfg(test)]
mod integrated_benchmarks;
-use std::fmt;
-
use serde::de::DeserializeOwned;
pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version};
@@ -61,23 +55,3 @@
serde_json::from_value(json.clone())
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
}
-
-#[derive(Debug)]
-struct LspError {
- code: i32,
- message: String,
-}
-
-impl LspError {
- fn new(code: i32, message: String) -> LspError {
- LspError { code, message }
- }
-}
-
-impl fmt::Display for LspError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
- }
-}
-
-impl std::error::Error for LspError {}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
new file mode 100644
index 0000000..ac7e1a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
@@ -0,0 +1,29 @@
+//! Custom LSP definitions and protocol conversions.
+
+use core::fmt;
+
+pub(crate) mod utils;
+pub(crate) mod semantic_tokens;
+pub mod ext;
+pub(crate) mod from_proto;
+pub(crate) mod to_proto;
+
+#[derive(Debug)]
+pub(crate) struct LspError {
+ pub(crate) code: i32,
+ pub(crate) message: String,
+}
+
+impl LspError {
+ pub(crate) fn new(code: i32, message: String) -> LspError {
+ LspError { code, message }
+ }
+}
+
+impl fmt::Display for LspError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
+ }
+}
+
+impl std::error::Error for LspError {}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
similarity index 99%
rename from src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index d0989b3..ad56899 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -682,7 +682,9 @@
}
#[derive(Debug, Serialize, Deserialize)]
-pub struct InlayHintResolveData {}
+pub struct InlayHintResolveData {
+ pub file_id: u32,
+}
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionImport {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
similarity index 97%
rename from src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
index c247e1b..69d6aba 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -12,8 +12,8 @@
from_json,
global_state::GlobalStateSnapshot,
line_index::{LineIndex, PositionEncoding},
+ lsp::utils::invalid_params_error,
lsp_ext,
- lsp_utils::invalid_params_error,
};
pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
@@ -72,7 +72,7 @@
pub(crate) fn file_range(
snap: &GlobalStateSnapshot,
- text_document_identifier: lsp_types::TextDocumentIdentifier,
+ text_document_identifier: &lsp_types::TextDocumentIdentifier,
range: lsp_types::Range,
) -> anyhow::Result<FileRange> {
file_range_uri(snap, &text_document_identifier.uri, range)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
similarity index 94%
rename from src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 7b32180..2307449 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -8,11 +8,12 @@
use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
- Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory,
- RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind,
- SymbolKind, TextEdit, TextRange, TextSize,
+ Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
+ InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
+ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
+ SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
};
+use ide_db::rust_doc::format_docs;
use itertools::Itertools;
use serde_json::to_value;
use vfs::AbsPath;
@@ -22,9 +23,12 @@
config::{CallInfoConfig, Config},
global_state::GlobalStateSnapshot,
line_index::{LineEndings, LineIndex, PositionEncoding},
+ lsp::{
+ semantic_tokens::{self, standard_fallback_type},
+ utils::invalid_params_error,
+ LspError,
+ },
lsp_ext::{self, SnippetTextEdit},
- lsp_utils::invalid_params_error,
- semantic_tokens::{self, standard_fallback_type},
};
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
@@ -102,7 +106,7 @@
}
pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
- let value = crate::markdown::format_docs(documentation.as_str());
+ let value = format_docs(&documentation);
let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
lsp_types::Documentation::MarkupContent(markup_content)
}
@@ -413,7 +417,7 @@
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
- value: crate::markdown::format_docs(&doc),
+ value: format_docs(&doc),
})
});
@@ -434,10 +438,25 @@
pub(crate) fn inlay_hint(
snap: &GlobalStateSnapshot,
+ fields_to_resolve: &InlayFieldsToResolve,
line_index: &LineIndex,
+ file_id: FileId,
inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
+ let needs_resolve = inlay_hint.needs_resolve;
+ let (label, tooltip, mut something_to_resolve) =
+ inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?;
+ let text_edits = if needs_resolve && fields_to_resolve.resolve_text_edits {
+ something_to_resolve |= inlay_hint.text_edit.is_some();
+ None
+ } else {
+ inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
+ };
+ let data = if needs_resolve && something_to_resolve {
+ Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap())
+ } else {
+ None
+ };
Ok(lsp_types::InlayHint {
position: match inlay_hint.position {
@@ -451,8 +470,8 @@
InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
_ => None,
},
- text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
- data: None,
+ text_edits,
+ data,
tooltip,
label,
})
@@ -460,13 +479,18 @@
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
+ fields_to_resolve: &InlayFieldsToResolve,
+ needs_resolve: bool,
mut label: InlayHintLabel,
-) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
- let res = match &*label.parts {
+) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>, bool)> {
+ let mut something_to_resolve = false;
+ let (label, tooltip) = match &*label.parts {
[InlayHintLabelPart { linked_location: None, .. }] => {
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
- (
- lsp_types::InlayHintLabel::String(text),
+ let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip {
+ something_to_resolve |= tooltip.is_some();
+ None
+ } else {
match tooltip {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintTooltip::String(s))
@@ -478,41 +502,52 @@
}))
}
None => None,
- },
- )
+ }
+ };
+ (lsp_types::InlayHintLabel::String(text), hint_tooltip)
}
_ => {
let parts = label
.parts
.into_iter()
.map(|part| {
- part.linked_location.map(|range| location(snap, range)).transpose().map(
- |location| lsp_types::InlayHintLabelPart {
- value: part.text,
- tooltip: match part.tooltip {
- Some(ide::InlayTooltip::String(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::String(s))
- }
- Some(ide::InlayTooltip::Markdown(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
- lsp_types::MarkupContent {
- kind: lsp_types::MarkupKind::Markdown,
- value: s,
- },
- ))
- }
- None => None,
- },
- location,
- command: None,
- },
- )
+ let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip {
+ something_to_resolve |= part.tooltip.is_some();
+ None
+ } else {
+ match part.tooltip {
+ Some(ide::InlayTooltip::String(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::String(s))
+ }
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
+ lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ },
+ ))
+ }
+ None => None,
+ }
+ };
+ let location = if needs_resolve && fields_to_resolve.resolve_label_location {
+ something_to_resolve |= part.linked_location.is_some();
+ None
+ } else {
+ part.linked_location.map(|range| location(snap, range)).transpose()?
+ };
+ Ok(lsp_types::InlayHintLabelPart {
+ value: part.text,
+ tooltip,
+ location,
+ command: None,
+ })
})
.collect::<Cancellable<_>>()?;
(lsp_types::InlayHintLabel::LabelParts(parts), None)
}
};
- Ok(res)
+ Ok((label, tooltip, something_to_resolve))
}
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
@@ -1323,17 +1358,18 @@
})
}
}
- AnnotationKind::HasImpls { pos: file_range, data } => {
+ AnnotationKind::HasImpls { pos, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_range.file_id)?;
+ let line_index = snap.file_line_index(pos.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_range.file_id);
+ let url = url(snap, pos.file_id);
+ let pos = position(&line_index, pos.offset);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
- let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
+ let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
let goto_params = lsp_types::request::GotoImplementationParams {
text_document_position_params: doc_pos,
@@ -1356,7 +1392,7 @@
command::show_references(
implementation_title(locations.len()),
&url,
- annotation_range.start,
+ pos,
locations,
)
});
@@ -1376,28 +1412,24 @@
})(),
})
}
- AnnotationKind::HasReferences { pos: file_range, data } => {
+ AnnotationKind::HasReferences { pos, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_range.file_id)?;
+ let line_index = snap.file_line_index(pos.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_range.file_id);
+ let url = url(snap, pos.file_id);
+ let pos = position(&line_index, pos.offset);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
- let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
+ let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
let command = data.map(|ranges| {
let locations: Vec<lsp_types::Location> =
ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
- command::show_references(
- reference_title(locations.len()),
- &url,
- annotation_range.start,
- locations,
- )
+ command::show_references(reference_title(locations.len()), &url, pos, locations)
});
acc.push(lsp_types::CodeLens {
@@ -1425,8 +1457,8 @@
use crate::{
global_state::GlobalStateSnapshot,
+ lsp::to_proto::{location, location_link},
lsp_ext,
- to_proto::{location, location_link},
};
pub(crate) fn show_references(
@@ -1528,11 +1560,11 @@
ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
};
- let value = crate::markdown::format_docs(markup.as_str());
+ let value = format_docs(&Documentation::new(markup.into()));
lsp_types::MarkupContent { kind, value }
}
-pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
+pub(crate) fn rename_error(err: RenameError) -> LspError {
// This is wrong, but we don't have a better alternative I suppose?
// https://github.com/microsoft/language-server-protocol/issues/1341
invalid_params_error(err.to_string())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
similarity index 99%
rename from src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
rename to src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
index 74e79e8..b388b31 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
@@ -6,10 +6,10 @@
use triomphe::Arc;
use crate::{
- from_proto,
global_state::GlobalState,
line_index::{LineEndings, LineIndex, PositionEncoding},
- lsp_ext, LspError,
+ lsp::{from_proto, LspError},
+ lsp_ext,
};
pub(crate) fn invalid_params_error(message: String) -> LspError {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 7403671..cdf41c9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -17,11 +17,14 @@
use crate::{
config::Config,
+ diagnostics::fetch_native_diagnostics,
dispatch::{NotificationDispatcher, RequestDispatcher},
- from_proto,
global_state::{file_id_to_url, url_to_file_id, GlobalState},
+ lsp::{
+ from_proto,
+ utils::{notification_is, Progress},
+ },
lsp_ext,
- lsp_utils::{notification_is, Progress},
reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
};
@@ -317,8 +320,11 @@
}
// Refresh inlay hints if the client supports it.
- if self.config.inlay_hints_refresh() {
+ if (self.send_hint_refresh_query || self.proc_macro_changed)
+ && self.config.inlay_hints_refresh()
+ {
self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
+ self.send_hint_refresh_query = false;
}
}
@@ -420,6 +426,32 @@
});
}
+ fn update_diagnostics(&mut self) {
+ let db = self.analysis_host.raw_database();
+ let subscriptions = self
+ .mem_docs
+ .iter()
+ .map(|path| self.vfs.read().0.file_id(path).unwrap())
+ .filter(|&file_id| {
+ let source_root = db.file_source_root(file_id);
+ // Only publish diagnostics for files in the workspace, not from crates.io deps
+ // or the sysroot.
+ // While theoretically these should never have errors, we have quite a few false
+ // positives particularly in the stdlib, and those diagnostics would stay around
+ // forever if we emitted them here.
+ !db.source_root(source_root).is_library
+ })
+ .collect::<Vec<_>>();
+ tracing::trace!("updating notifications for {:?}", subscriptions);
+
+ // Diagnostics are triggered by the user typing
+ // so we run them on a latency sensitive thread.
+ self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
+ let snapshot = self.snapshot();
+ move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions))
+ });
+ }
+
fn update_status_or_notify(&mut self) {
let status = self.current_status();
if self.last_reported_status.as_ref() != Some(&status) {
@@ -509,6 +541,7 @@
}
self.switch_workspaces("fetched build data".to_string());
+ self.send_hint_refresh_query = true;
(Some(Progress::End), None)
}
@@ -525,7 +558,7 @@
ProcMacroProgress::End(proc_macro_load_result) => {
self.fetch_proc_macros_queue.op_completed(true);
self.set_proc_macros(proc_macro_load_result);
-
+ self.send_hint_refresh_query = true;
(Some(Progress::End), None)
}
};
@@ -670,6 +703,7 @@
}
use crate::handlers::request as handlers;
+ use lsp_types::request as lsp_request;
dispatcher
// Request handlers that must run on the main thread
@@ -682,30 +716,30 @@
// are run on the main thread to reduce latency:
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
- .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
+ .on_sync::<lsp_request::SelectionRangeRequest>(handlers::handle_selection_range)
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
.on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
// Formatting should be done immediately as the editor might wait on it, but we can't
// put it on the main thread as we do not want the main thread to block on rustfmt.
// So we have an extra thread just for formatting requests to make sure it gets handled
// as fast as possible.
- .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
- .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
+ .on_fmt_thread::<lsp_request::Formatting>(handlers::handle_formatting)
+ .on_fmt_thread::<lsp_request::RangeFormatting>(handlers::handle_range_formatting)
// We can’t run latency-sensitive request handlers which do semantic
// analysis on the main thread because that would block other
// requests. Instead, we run these request handlers on higher priority
// threads in the threadpool.
- .on_latency_sensitive::<lsp_types::request::Completion>(handlers::handle_completion)
- .on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>(
+ .on_latency_sensitive::<lsp_request::Completion>(handlers::handle_completion)
+ .on_latency_sensitive::<lsp_request::ResolveCompletionItem>(
handlers::handle_completion_resolve,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensFullRequest>(
handlers::handle_semantic_tokens_full,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensFullDeltaRequest>(
handlers::handle_semantic_tokens_full_delta,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensRangeRequest>(
handlers::handle_semantic_tokens_range,
)
// All other request handlers
@@ -729,29 +763,25 @@
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
.on::<lsp_ext::MoveItem>(handlers::handle_move_item)
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
- .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
- .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
- .on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
- .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
- .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
- .on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints)
- .on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
- .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)
- .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)
- .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)
- .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)
- .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)
- .on::<lsp_types::request::Rename>(handlers::handle_rename)
- .on::<lsp_types::request::References>(handlers::handle_references)
- .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
- .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
- .on::<lsp_types::request::CallHierarchyIncomingCalls>(
- handlers::handle_call_hierarchy_incoming,
- )
- .on::<lsp_types::request::CallHierarchyOutgoingCalls>(
- handlers::handle_call_hierarchy_outgoing,
- )
- .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
+ .on::<lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
+ .on::<lsp_request::GotoDefinition>(handlers::handle_goto_definition)
+ .on::<lsp_request::GotoDeclaration>(handlers::handle_goto_declaration)
+ .on::<lsp_request::GotoImplementation>(handlers::handle_goto_implementation)
+ .on::<lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
+ .on_no_retry::<lsp_request::InlayHintRequest>(handlers::handle_inlay_hints)
+ .on::<lsp_request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
+ .on::<lsp_request::CodeLensRequest>(handlers::handle_code_lens)
+ .on::<lsp_request::CodeLensResolve>(handlers::handle_code_lens_resolve)
+ .on::<lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
+ .on::<lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
+ .on::<lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename)
+ .on::<lsp_request::Rename>(handlers::handle_rename)
+ .on::<lsp_request::References>(handlers::handle_references)
+ .on::<lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight)
+ .on::<lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
+ .on::<lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)
+ .on::<lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)
+ .on::<lsp_request::WillRenameFiles>(handlers::handle_will_rename_files)
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
.on::<lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
.finish();
@@ -788,77 +818,4 @@
.finish();
Ok(())
}
-
- fn update_diagnostics(&mut self) {
- let db = self.analysis_host.raw_database();
- let subscriptions = self
- .mem_docs
- .iter()
- .map(|path| self.vfs.read().0.file_id(path).unwrap())
- .filter(|&file_id| {
- let source_root = db.file_source_root(file_id);
- // Only publish diagnostics for files in the workspace, not from crates.io deps
- // or the sysroot.
- // While theoretically these should never have errors, we have quite a few false
- // positives particularly in the stdlib, and those diagnostics would stay around
- // forever if we emitted them here.
- !db.source_root(source_root).is_library
- })
- .collect::<Vec<_>>();
-
- tracing::trace!("updating notifications for {:?}", subscriptions);
-
- let snapshot = self.snapshot();
-
- // Diagnostics are triggered by the user typing
- // so we run them on a latency sensitive thread.
- self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
- let _p = profile::span("publish_diagnostics");
- let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
- let diagnostics = subscriptions
- .into_iter()
- .filter_map(|file_id| {
- let line_index = snapshot.file_line_index(file_id).ok()?;
- Some((
- file_id,
- line_index,
- snapshot
- .analysis
- .diagnostics(
- &snapshot.config.diagnostics(),
- ide::AssistResolveStrategy::None,
- file_id,
- )
- .ok()?,
- ))
- })
- .map(|(file_id, line_index, it)| {
- (
- file_id,
- it.into_iter()
- .map(move |d| lsp_types::Diagnostic {
- range: crate::to_proto::range(&line_index, d.range),
- severity: Some(crate::to_proto::diagnostic_severity(d.severity)),
- code: Some(lsp_types::NumberOrString::String(
- d.code.as_str().to_string(),
- )),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&d.code.url()).unwrap(),
- }),
- source: Some("rust-analyzer".to_string()),
- message: d.message,
- related_information: None,
- tags: if d.unused {
- Some(vec![lsp_types::DiagnosticTag::UNNECESSARY])
- } else {
- None
- },
- data: None,
- })
- .collect::<Vec<_>>(),
- )
- });
- Task::Diagnostics(diagnostics.collect())
- });
- }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
deleted file mode 100644
index 58426c6..0000000
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-//! Transforms markdown
-use ide_db::rust_doc::is_rust_fence;
-
-const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
-
-pub(crate) fn format_docs(src: &str) -> String {
- let mut processed_lines = Vec::new();
- let mut in_code_block = false;
- let mut is_rust = false;
-
- for mut line in src.lines() {
- if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
- continue;
- }
-
- if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
- {
- in_code_block ^= true;
-
- if in_code_block {
- is_rust = is_rust_fence(header);
-
- if is_rust {
- line = "```rust";
- }
- }
- }
-
- if in_code_block {
- let trimmed = line.trim_start();
- if is_rust && trimmed.starts_with("##") {
- line = &trimmed[1..];
- }
- }
-
- processed_lines.push(line);
- }
- processed_lines.join("\n")
-}
-
-fn code_line_ignored_by_rustdoc(line: &str) -> bool {
- let trimmed = line.trim();
- trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_format_docs_adds_rust() {
- let comment = "```\nfn some_rust() {}\n```";
- assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
- }
-
- #[test]
- fn test_format_docs_handles_plain_text() {
- let comment = "```text\nthis is plain text\n```";
- assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
- }
-
- #[test]
- fn test_format_docs_handles_non_rust() {
- let comment = "```sh\nsupposedly shell code\n```";
- assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
- }
-
- #[test]
- fn test_format_docs_handles_rust_alias() {
- let comment = "```ignore\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_complex_code_block_attrs() {
- let comment = "```rust,no_run\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_error_codes() {
- let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
- }
-
- #[test]
- fn test_format_docs_skips_comments_in_rust_block() {
- let comment =
- "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
- assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
- }
-
- #[test]
- fn test_format_docs_does_not_skip_lines_if_plain_text() {
- let comment =
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
- assert_eq!(
- format_docs(comment),
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
- );
- }
-
- #[test]
- fn test_format_docs_keeps_comments_outside_of_rust_block() {
- let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_format_docs_preserves_newlines() {
- let comment = "this\nis\nmultiline";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_rust() {
- let comment = r#"```rust
-fn main(){}
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_text() {
- let comment = r#"```text
-filler
-text
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_format_docs_handles_escape_double_hashes() {
- let comment = r#"```rust
-let s = "foo
-## bar # baz";
-```"#;
-
- assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
- }
-
- #[test]
- fn test_format_docs_handles_double_hashes_non_rust() {
- let comment = r#"```markdown
-## A second-level heading
-```"#;
- assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
- }
-}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 0a2bb82..3fae08b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -12,6 +12,7 @@
//! correct. Instead, we try to provide a best-effort service. Even if the
//! project is currently loading and we don't have a full project model, we
//! still want to respond to various requests.
+// FIXME: This is a mess that needs some untangling work
use std::{iter, mem};
use flycheck::{FlycheckConfig, FlycheckHandle};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 0bb29e7..d599142 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -29,7 +29,7 @@
PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
TextDocumentPositionParams, WorkDoneProgressParams,
};
-use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
+use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
use serde_json::json;
use test_utils::skip_slow_tests;
@@ -861,6 +861,7 @@
bar = {path = "../bar"}
//- /foo/src/main.rs
+#![allow(internal_features)]
#![feature(rustc_attrs, decl_macro)]
use bar::Bar;
@@ -938,7 +939,7 @@
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("foo/src/main.rs"),
- Position::new(11, 9),
+ Position::new(12, 9),
),
work_done_progress_params: Default::default(),
});
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 3c52ef5..e49b576 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -9,7 +9,7 @@
use crossbeam_channel::{after, select, Receiver};
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
-use rust_analyzer::{config::Config, lsp_ext, main_loop};
+use rust_analyzer::{config::Config, lsp, main_loop};
use serde::Serialize;
use serde_json::{json, to_string_pretty, Value};
use test_utils::FixtureWithProjectMeta;
@@ -260,9 +260,9 @@
Message::Notification(n) if n.method == "experimental/serverStatus" => {
let status = n
.clone()
- .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
+ .extract::<lsp::ext::ServerStatusParams>("experimental/serverStatus")
.unwrap();
- if status.health != lsp_ext::Health::Ok {
+ if status.health != lsp::ext::Health::Ok {
panic!("server errored/warned while loading workspace: {:?}", status.message);
}
status.quiescent
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index f230cba..8b5c92c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -35,7 +35,7 @@
let expected_hash = {
let lsp_ext_rs = sh
- .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp_ext.rs"))
+ .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp/ext.rs"))
.unwrap();
stable_hash(lsp_ext_rs.as_str())
};
@@ -45,7 +45,7 @@
sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap();
let text = lsp_extensions_md
.lines()
- .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
+ .find_map(|line| line.strip_prefix("lsp/ext.rs hash:"))
.unwrap()
.trim();
u64::from_str_radix(text, 16).unwrap()
@@ -54,7 +54,7 @@
if actual_hash != expected_hash {
panic!(
"
-lsp_ext.rs was changed without touching lsp-extensions.md.
+lsp/ext.rs was changed without touching lsp-extensions.md.
Expected hash: {expected_hash:x}
Actual hash: {actual_hash:x}
@@ -158,7 +158,7 @@
Apache-2.0/MIT
BSD-3-Clause
BlueOak-1.0.0 OR MIT OR Apache-2.0
-CC0-1.0 OR Artistic-2.0
+CC0-1.0
ISC
MIT
MIT / Apache-2.0
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index ea7ebd8..3603560d 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -340,10 +340,10 @@
Expr =
ArrayExpr
+| AsmExpr
| AwaitExpr
| BinExpr
| BlockExpr
-| BoxExpr
| BreakExpr
| CallExpr
| CastExpr
@@ -351,6 +351,7 @@
| ContinueExpr
| FieldExpr
| ForExpr
+| FormatArgsExpr
| IfExpr
| IndexExpr
| Literal
@@ -358,6 +359,7 @@
| MacroExpr
| MatchExpr
| MethodCallExpr
+| OffsetOfExpr
| ParenExpr
| PathExpr
| PrefixExpr
@@ -373,6 +375,21 @@
| LetExpr
| UnderscoreExpr
+OffsetOfExpr =
+ Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+
+AsmExpr =
+ Attr* 'builtin' '#' 'asm' '(' Expr ')'
+
+FormatArgsExpr =
+ Attr* 'builtin' '#' 'format_args' '('
+ template:Expr
+ (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
+ ')'
+
+FormatArgsArg =
+ (Name '=')? Expr
+
MacroExpr =
MacroCall
@@ -526,9 +543,6 @@
AwaitExpr =
Attr* Expr '.' 'await'
-BoxExpr =
- Attr* 'box' Expr
-
//*************************//
// Types //
//*************************//
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 16448db..7ba0d4d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -805,6 +805,20 @@
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for AsmExpr {}
+impl AsmExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AwaitExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -823,16 +837,6 @@
impl BinExpr {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct BoxExpr {
- pub(crate) syntax: SyntaxNode,
-}
-impl ast::HasAttrs for BoxExpr {}
-impl BoxExpr {
- pub fn box_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![box]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BreakExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -916,6 +920,24 @@
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FormatArgsExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for FormatArgsExpr {}
+impl FormatArgsExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn format_args_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![format_args])
+ }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn template(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ pub fn args(&self) -> AstChildren<FormatArgsArg> { support::children(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IfExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -985,6 +1007,24 @@
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct OffsetOfExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for OffsetOfExpr {}
+impl OffsetOfExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn offset_of_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![offset_of])
+ }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ pub fn fields(&self) -> AstChildren<NameRef> { support::children(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParenExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -1127,6 +1167,16 @@
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FormatArgsArg {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasName for FormatArgsArg {}
+impl FormatArgsArg {
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
}
@@ -1555,10 +1605,10 @@
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
ArrayExpr(ArrayExpr),
+ AsmExpr(AsmExpr),
AwaitExpr(AwaitExpr),
BinExpr(BinExpr),
BlockExpr(BlockExpr),
- BoxExpr(BoxExpr),
BreakExpr(BreakExpr),
CallExpr(CallExpr),
CastExpr(CastExpr),
@@ -1566,6 +1616,7 @@
ContinueExpr(ContinueExpr),
FieldExpr(FieldExpr),
ForExpr(ForExpr),
+ FormatArgsExpr(FormatArgsExpr),
IfExpr(IfExpr),
IndexExpr(IndexExpr),
Literal(Literal),
@@ -1573,6 +1624,7 @@
MacroExpr(MacroExpr),
MatchExpr(MatchExpr),
MethodCallExpr(MethodCallExpr),
+ OffsetOfExpr(OffsetOfExpr),
ParenExpr(ParenExpr),
PathExpr(PathExpr),
PrefixExpr(PrefixExpr),
@@ -2453,6 +2505,17 @@
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for AsmExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for AwaitExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2475,17 +2538,6 @@
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for BoxExpr {
- fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_EXPR }
- fn cast(syntax: SyntaxNode) -> Option<Self> {
- if Self::can_cast(syntax.kind()) {
- Some(Self { syntax })
- } else {
- None
- }
- }
- fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
impl AstNode for BreakExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == BREAK_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2563,6 +2615,17 @@
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for FormatArgsExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for IfExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2640,6 +2703,17 @@
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for OffsetOfExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for ParenExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2794,6 +2868,17 @@
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for FormatArgsArg {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for StmtList {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3373,6 +3458,9 @@
impl From<ArrayExpr> for Expr {
fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) }
}
+impl From<AsmExpr> for Expr {
+ fn from(node: AsmExpr) -> Expr { Expr::AsmExpr(node) }
+}
impl From<AwaitExpr> for Expr {
fn from(node: AwaitExpr) -> Expr { Expr::AwaitExpr(node) }
}
@@ -3382,9 +3470,6 @@
impl From<BlockExpr> for Expr {
fn from(node: BlockExpr) -> Expr { Expr::BlockExpr(node) }
}
-impl From<BoxExpr> for Expr {
- fn from(node: BoxExpr) -> Expr { Expr::BoxExpr(node) }
-}
impl From<BreakExpr> for Expr {
fn from(node: BreakExpr) -> Expr { Expr::BreakExpr(node) }
}
@@ -3406,6 +3491,9 @@
impl From<ForExpr> for Expr {
fn from(node: ForExpr) -> Expr { Expr::ForExpr(node) }
}
+impl From<FormatArgsExpr> for Expr {
+ fn from(node: FormatArgsExpr) -> Expr { Expr::FormatArgsExpr(node) }
+}
impl From<IfExpr> for Expr {
fn from(node: IfExpr) -> Expr { Expr::IfExpr(node) }
}
@@ -3427,6 +3515,9 @@
impl From<MethodCallExpr> for Expr {
fn from(node: MethodCallExpr) -> Expr { Expr::MethodCallExpr(node) }
}
+impl From<OffsetOfExpr> for Expr {
+ fn from(node: OffsetOfExpr) -> Expr { Expr::OffsetOfExpr(node) }
+}
impl From<ParenExpr> for Expr {
fn from(node: ParenExpr) -> Expr { Expr::ParenExpr(node) }
}
@@ -3474,10 +3565,10 @@
matches!(
kind,
ARRAY_EXPR
+ | ASM_EXPR
| AWAIT_EXPR
| BIN_EXPR
| BLOCK_EXPR
- | BOX_EXPR
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
@@ -3485,6 +3576,7 @@
| CONTINUE_EXPR
| FIELD_EXPR
| FOR_EXPR
+ | FORMAT_ARGS_EXPR
| IF_EXPR
| INDEX_EXPR
| LITERAL
@@ -3492,6 +3584,7 @@
| MACRO_EXPR
| MATCH_EXPR
| METHOD_CALL_EXPR
+ | OFFSET_OF_EXPR
| PAREN_EXPR
| PATH_EXPR
| PREFIX_EXPR
@@ -3511,10 +3604,10 @@
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
ARRAY_EXPR => Expr::ArrayExpr(ArrayExpr { syntax }),
+ ASM_EXPR => Expr::AsmExpr(AsmExpr { syntax }),
AWAIT_EXPR => Expr::AwaitExpr(AwaitExpr { syntax }),
BIN_EXPR => Expr::BinExpr(BinExpr { syntax }),
BLOCK_EXPR => Expr::BlockExpr(BlockExpr { syntax }),
- BOX_EXPR => Expr::BoxExpr(BoxExpr { syntax }),
BREAK_EXPR => Expr::BreakExpr(BreakExpr { syntax }),
CALL_EXPR => Expr::CallExpr(CallExpr { syntax }),
CAST_EXPR => Expr::CastExpr(CastExpr { syntax }),
@@ -3522,6 +3615,7 @@
CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }),
FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }),
FOR_EXPR => Expr::ForExpr(ForExpr { syntax }),
+ FORMAT_ARGS_EXPR => Expr::FormatArgsExpr(FormatArgsExpr { syntax }),
IF_EXPR => Expr::IfExpr(IfExpr { syntax }),
INDEX_EXPR => Expr::IndexExpr(IndexExpr { syntax }),
LITERAL => Expr::Literal(Literal { syntax }),
@@ -3529,6 +3623,7 @@
MACRO_EXPR => Expr::MacroExpr(MacroExpr { syntax }),
MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }),
METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }),
+ OFFSET_OF_EXPR => Expr::OffsetOfExpr(OffsetOfExpr { syntax }),
PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }),
PATH_EXPR => Expr::PathExpr(PathExpr { syntax }),
PREFIX_EXPR => Expr::PrefixExpr(PrefixExpr { syntax }),
@@ -3550,10 +3645,10 @@
fn syntax(&self) -> &SyntaxNode {
match self {
Expr::ArrayExpr(it) => &it.syntax,
+ Expr::AsmExpr(it) => &it.syntax,
Expr::AwaitExpr(it) => &it.syntax,
Expr::BinExpr(it) => &it.syntax,
Expr::BlockExpr(it) => &it.syntax,
- Expr::BoxExpr(it) => &it.syntax,
Expr::BreakExpr(it) => &it.syntax,
Expr::CallExpr(it) => &it.syntax,
Expr::CastExpr(it) => &it.syntax,
@@ -3561,6 +3656,7 @@
Expr::ContinueExpr(it) => &it.syntax,
Expr::FieldExpr(it) => &it.syntax,
Expr::ForExpr(it) => &it.syntax,
+ Expr::FormatArgsExpr(it) => &it.syntax,
Expr::IfExpr(it) => &it.syntax,
Expr::IndexExpr(it) => &it.syntax,
Expr::Literal(it) => &it.syntax,
@@ -3568,6 +3664,7 @@
Expr::MacroExpr(it) => &it.syntax,
Expr::MatchExpr(it) => &it.syntax,
Expr::MethodCallExpr(it) => &it.syntax,
+ Expr::OffsetOfExpr(it) => &it.syntax,
Expr::ParenExpr(it) => &it.syntax,
Expr::PathExpr(it) => &it.syntax,
Expr::PrefixExpr(it) => &it.syntax,
@@ -4028,9 +4125,9 @@
| TYPE_PARAM
| LET_STMT
| ARRAY_EXPR
+ | ASM_EXPR
| AWAIT_EXPR
| BIN_EXPR
- | BOX_EXPR
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
@@ -4038,12 +4135,14 @@
| CONTINUE_EXPR
| FIELD_EXPR
| FOR_EXPR
+ | FORMAT_ARGS_EXPR
| IF_EXPR
| INDEX_EXPR
| LITERAL
| LOOP_EXPR
| MATCH_EXPR
| METHOD_CALL_EXPR
+ | OFFSET_OF_EXPR
| PAREN_EXPR
| PATH_EXPR
| PREFIX_EXPR
@@ -4179,6 +4278,7 @@
| VARIANT
| CONST_PARAM
| TYPE_PARAM
+ | FORMAT_ARGS_ARG
| IDENT_PAT
)
}
@@ -4620,6 +4720,11 @@
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for AsmExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for AwaitExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4630,11 +4735,6 @@
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for BoxExpr {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self.syntax(), f)
- }
-}
impl std::fmt::Display for BreakExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4670,6 +4770,11 @@
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for FormatArgsExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for IfExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4705,6 +4810,11 @@
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for OffsetOfExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ParenExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4775,6 +4885,11 @@
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for FormatArgsArg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for StmtList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 4ec3889..8e04ab8 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -130,7 +130,8 @@
//
ContinueExpr(_) => (0, 0),
- ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_) => (0, 1),
+ ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_)
+ | OffsetOfExpr(_) | FormatArgsExpr(_) | AsmExpr(_) => (0, 1),
RangeExpr(_) => (5, 5),
@@ -160,7 +161,7 @@
CastExpr(_) => (25, 26),
- BoxExpr(_) | RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27),
+ RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27),
AwaitExpr(_) | CallExpr(_) | MethodCallExpr(_) | IndexExpr(_) | TryExpr(_)
| MacroExpr(_) => (29, 0),
@@ -202,7 +203,6 @@
let rhs = match self {
RefExpr(e) => e.expr(),
BinExpr(e) => e.rhs(),
- BoxExpr(e) => e.expr(),
BreakExpr(e) => e.expr(),
LetExpr(e) => e.expr(),
RangeExpr(e) => e.end(),
@@ -279,7 +279,6 @@
CastExpr(e) => e.as_token(),
FieldExpr(e) => e.dot_token(),
AwaitExpr(e) => e.dot_token(),
- BoxExpr(e) => e.box_token(),
BreakExpr(e) => e.break_token(),
CallExpr(e) => e.arg_list().and_then(|args| args.l_paren_token()),
ClosureExpr(e) => e.param_list().and_then(|params| params.l_paren_token()),
@@ -293,7 +292,9 @@
YieldExpr(e) => e.yield_token(),
YeetExpr(e) => e.do_token(),
LetExpr(e) => e.let_token(),
-
+ OffsetOfExpr(e) => e.builtin_token(),
+ FormatArgsExpr(e) => e.builtin_token(),
+ AsmExpr(e) => e.builtin_token(),
ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_)
| IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_)
| BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) => None,
@@ -310,12 +311,12 @@
ArrayExpr(_) | AwaitExpr(_) | BlockExpr(_) | CallExpr(_) | CastExpr(_)
| ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_)
| MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_)
- | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) => false,
+ | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) | OffsetOfExpr(_)
+ | FormatArgsExpr(_) | AsmExpr(_) => false,
// For BinExpr and RangeExpr this is technically wrong -- the child can be on the left...
- BinExpr(_) | RangeExpr(_) | BoxExpr(_) | BreakExpr(_) | ContinueExpr(_)
- | PrefixExpr(_) | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_)
- | LetExpr(_) => self
+ BinExpr(_) | RangeExpr(_) | BreakExpr(_) | ContinueExpr(_) | PrefixExpr(_)
+ | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | LetExpr(_) => self
.syntax()
.parent()
.and_then(Expr::cast)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index e4db33f..341bda8 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -70,7 +70,19 @@
"match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
"super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
],
- contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"],
+ contextual_keywords: &[
+ "auto",
+ "builtin",
+ "default",
+ "existential",
+ "union",
+ "raw",
+ "macro_rules",
+ "yeet",
+ "offset_of",
+ "asm",
+ "format_args",
+ ],
literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"],
tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
nodes: &[
@@ -154,7 +166,10 @@
"RECORD_EXPR",
"RECORD_EXPR_FIELD_LIST",
"RECORD_EXPR_FIELD",
- "BOX_EXPR",
+ "OFFSET_OF_EXPR",
+ "ASM_EXPR",
+ "FORMAT_ARGS_EXPR",
+ "FORMAT_ARGS_ARG",
// postfix
"CALL_EXPR",
"INDEX_EXPR",
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index c49c5fa1..56227fc 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -623,7 +623,7 @@
}
fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) {
- if lower_comma_list(acc, grammar, label, rule) {
+ if lower_seperated_list(acc, grammar, label, rule) {
return;
}
@@ -689,7 +689,7 @@
}
// (T (',' T)* ','?)
-fn lower_comma_list(
+fn lower_seperated_list(
acc: &mut Vec<Field>,
grammar: &Grammar,
label: Option<&String>,
@@ -699,19 +699,23 @@
Rule::Seq(it) => it,
_ => return false,
};
- let (node, repeat, trailing_comma) = match rule.as_slice() {
- [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {
- (node, repeat, trailing_comma)
+ let (node, repeat, trailing_sep) = match rule.as_slice() {
+ [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
+ (node, repeat, Some(trailing_sep))
}
+ [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
_ => return false,
};
let repeat = match &**repeat {
Rule::Seq(it) => it,
_ => return false,
};
- match repeat.as_slice() {
- [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),
- _ => return false,
+ if !matches!(
+ repeat.as_slice(),
+ [comma, Rule::Node(n)]
+ if trailing_sep.map_or(true, |it| comma == &**it) && n == node
+ ) {
+ return false;
}
let ty = grammar[*node].name.clone();
let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index c765f42..573f56b 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -899,32 +899,90 @@
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
- extern "C" {
- type Opaque;
- }
+ mod rt {
- #[lang = "format_argument"]
- pub struct Argument<'a> {
- value: &'a Opaque,
- formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
- }
+ extern "C" {
+ type Opaque;
+ }
- impl<'a> Argument<'a> {
- pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
- use crate::mem::transmute;
- unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ #[lang = "format_argument"]
+ pub struct Argument<'a> {
+ value: &'a Opaque,
+ formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+ }
+
+ impl<'a> Argument<'a> {
+ pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+ use crate::mem::transmute;
+ unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ }
+ }
+
+ #[lang = "format_alignment"]
+ pub enum Alignment {
+ Left,
+ Right,
+ Center,
+ Unknown,
+ }
+
+ #[lang = "format_count"]
+ pub enum Count {
+ Is(usize),
+ Param(usize),
+ Implied,
+ }
+
+ #[lang = "format_placeholder"]
+ pub struct Placeholder {
+ pub position: usize,
+ pub fill: char,
+ pub align: Alignment,
+ pub flags: u32,
+ pub precision: Count,
+ pub width: Count,
+ }
+
+ impl Placeholder {
+ pub const fn new(
+ position: usize,
+ fill: char,
+ align: Alignment,
+ flags: u32,
+ precision: Count,
+ width: Count,
+ ) -> Self;
+ }
+
+ #[lang = "format_unsafe_arg"]
+ pub struct UnsafeArg {
+ _private: (),
+ }
+
+ impl UnsafeArg {
+ pub unsafe fn new() -> Self;
}
}
#[lang = "format_arguments"]
pub struct Arguments<'a> {
pieces: &'a [&'static str],
- args: &'a [Argument<'a>],
+ fmt: Option<&'a [rt::Placeholder]>,
+ args: &'a [rt::Argument<'a>],
}
impl<'a> Arguments<'a> {
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
- Arguments { pieces, args }
+ Arguments { pieces, fmt: None, args }
+ }
+
+ pub fn new_v1_formatted(
+ pieces: &'a [&'static str],
+ args: &'a [rt::Argument<'a>],
+ fmt: &'a [rt::Placeholder],
+ _unsafe_arg: rt::UnsafeArg,
+ ) -> Arguments<'a> {
+ Arguments { pieces, fmt: Some(fmt), args }
}
}
@@ -1294,8 +1352,6 @@
/* compiler built-in */
};
}
-
- pub(crate) use panic;
// endregion:panic
// region:fmt
@@ -1306,7 +1362,20 @@
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
}
- pub(crate) use const_format_args;
+ #[macro_export]
+ #[rustc_builtin_macro]
+ macro_rules! format_args {
+ ($fmt:expr) => {{ /* compiler built-in */ }};
+ ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+ }
+
+ #[macro_export]
+ macro_rules! print {
+ ($($arg:tt)*) => {{
+ $crate::io::_print($crate::format_args!($($arg)*));
+ }};
+ }
+
// endregion:fmt
// region:derive
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index 95c5142..11055f0 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@
walkdir = "2.3.2"
crossbeam-channel = "0.5.5"
# We demand 5.1.0 as any higher version pulls in a new windows-sys dupe
-notify = "=5.1.0"
+notify = "6.1.1"
stdx.workspace = true
vfs.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index fe3dfe6..06004ad 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -184,6 +184,11 @@
mem::take(&mut self.changes)
}
+ /// Provides a panic-less way to verify file_id validity.
+ pub fn exists(&self, file_id: FileId) -> bool {
+ self.get(file_id).is_some()
+ }
+
/// Returns the id associated with `path`
///
/// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a
diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md
index 895de57..b7d585c 100644
--- a/src/tools/rust-analyzer/docs/dev/architecture.md
+++ b/src/tools/rust-analyzer/docs/dev/architecture.md
@@ -268,7 +268,7 @@
And it also handles the actual parsing and expansion of declarative macro (a-la "Macros By Example" or mbe).
For proc macros, the client-server model are used.
-We pass an argument `--proc-macro` to `rust-analyzer` binary to start a separate process (`proc_macro_srv`).
+We start a separate process (`proc_macro_srv`) which loads and runs the proc-macros for us.
And the client (`proc_macro_api`) provides an interface to talk to that server separately.
And then token trees are passed from client, and the server will load the corresponding dynamic library (which built by `cargo`).
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index 024acb8..0801e98 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp_ext.rs hash: 149a5be3c5e469d1
+lsp/ext.rs hash: 121482ee911854da
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
@@ -322,7 +322,7 @@
```rust
fn main() {
- let x: Vec<()>/*cursor here*/ = vec![]
+ let x: Vec<()>/*cursor here*/ = vec![];
}
```
@@ -362,7 +362,7 @@
```typescript
interface Runnable {
label: string;
- /// If this Runnable is associated with a specific function/module, etc, the location of this item
+ /// If this Runnable is associated with a specific function/module, etc., the location of this item
location?: LocationLink;
/// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
// the type of `args` is specific to `kind`. The actual running is handled by the client.
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 44f1b81..233e7bf 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -471,16 +471,13 @@
"default": false,
"type": "boolean"
},
- "rust-analyzer.discoverProjectCommand": {
- "markdownDescription": "Sets the command that rust-analyzer uses to generate `rust-project.json` files. This command should only be used\n if a build system like Buck or Bazel is also in use. The command must accept files as arguments and return \n a rust-project.json over stdout.",
+ "rust-analyzer.discoverProjectRunner": {
+ "markdownDescription": "Sets the extension responsible for determining which extension the rust-analyzer extension uses to generate `rust-project.json` files. This should should only be used\n if a build system like Buck or Bazel is also in use.",
"default": null,
"type": [
"null",
- "array"
- ],
- "items": {
- "type": "string"
- }
+ "string"
+ ]
},
"rust-analyzer.showUnlinkedFileNotification": {
"markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.",
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index aba37ba..245557b 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -3,7 +3,7 @@
import * as ra from "./lsp_ext";
import * as path from "path";
-import { type Ctx, type Cmd, type CtxInit, discoverWorkspace } from "./ctx";
+import type { Ctx, Cmd, CtxInit } from "./ctx";
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
import { spawnSync } from "child_process";
import { type RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
@@ -871,22 +871,16 @@
export function addProject(ctx: CtxInit): Cmd {
return async () => {
- const discoverProjectCommand = ctx.config.discoverProjectCommand;
- if (!discoverProjectCommand) {
+ const extensionName = ctx.config.discoverProjectRunner;
+ // this command shouldn't be enabled in the first place if this isn't set.
+ if (!extensionName) {
return;
}
- const workspaces: JsonProject[] = await Promise.all(
- vscode.workspace.textDocuments
- .filter(isRustDocument)
- .map(async (file): Promise<JsonProject> => {
- return discoverWorkspace([file], discoverProjectCommand, {
- cwd: path.dirname(file.uri.fsPath),
- });
- }),
- );
+ const command = `${extensionName}.discoverWorkspaceCommand`;
+ const project: JsonProject = await vscode.commands.executeCommand(command);
- ctx.addToDiscoveredWorkspaces(workspaces);
+ ctx.addToDiscoveredWorkspaces([project]);
// this is a workaround to avoid needing writing the `rust-project.json` into
// a workspace-level VS Code-specific settings folder. We'd like to keep the
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index 39e2f76..9821aee 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -253,8 +253,8 @@
return this.get<boolean>("trace.extension");
}
- get discoverProjectCommand() {
- return this.get<string[] | undefined>("discoverProjectCommand");
+ get discoverProjectRunner(): string | undefined {
+ return this.get<string | undefined>("discoverProjectRunner");
}
get problemMatcher(): string[] {
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index 363a7a8..904efa4 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -1,12 +1,10 @@
import * as vscode from "vscode";
import type * as lc from "vscode-languageclient/node";
import * as ra from "./lsp_ext";
-import * as path from "path";
import { Config, prepareVSCodeConfig } from "./config";
import { createClient } from "./client";
import {
- executeDiscoverProject,
isDocumentInWorkspace,
isRustDocument,
isRustEditor,
@@ -24,7 +22,6 @@
import { execRevealDependency } from "./commands";
import { PersistentState } from "./persistent_state";
import { bootstrap } from "./bootstrap";
-import type { ExecOptions } from "child_process";
// We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
// only those are in use. We use "Empty" to represent these scenarios
@@ -58,17 +55,6 @@
: { kind: "Workspace Folder" };
}
-export async function discoverWorkspace(
- files: readonly vscode.TextDocument[],
- command: string[],
- options: ExecOptions,
-): Promise<JsonProject> {
- const paths = files.map((f) => `"${f.uri.fsPath}"`).join(" ");
- const joinedCommand = command.join(" ");
- const data = await executeDiscoverProject(`${joinedCommand} ${paths}`, options);
- return JSON.parse(data) as JsonProject;
-}
-
export type CommandFactory = {
enabled: (ctx: CtxInit) => Cmd;
disabled?: (ctx: Ctx) => Cmd;
@@ -200,6 +186,12 @@
};
let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
+ if (this.config.discoverProjectRunner) {
+ const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`;
+ log.info(`running command: ${command}`);
+ const project: JsonProject = await vscode.commands.executeCommand(command);
+ this.addToDiscoveredWorkspaces([project]);
+ }
if (this.workspace.kind === "Detached Files") {
rawInitializationOptions = {
@@ -208,21 +200,6 @@
};
}
- const discoverProjectCommand = this.config.discoverProjectCommand;
- if (discoverProjectCommand) {
- const workspaces: JsonProject[] = await Promise.all(
- vscode.workspace.textDocuments
- .filter(isRustDocument)
- .map(async (file): Promise<JsonProject> => {
- return discoverWorkspace([file], discoverProjectCommand, {
- cwd: path.dirname(file.uri.fsPath),
- });
- }),
- );
-
- this.addToDiscoveredWorkspaces(workspaces);
- }
-
const initializationOptions = prepareVSCodeConfig(
rawInitializationOptions,
(key, obj) => {
diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts
index 0414ea0..51f921a 100644
--- a/src/tools/rust-analyzer/editors/code/src/util.ts
+++ b/src/tools/rust-analyzer/editors/code/src/util.ts
@@ -154,22 +154,6 @@
});
}
-export function executeDiscoverProject(command: string, options: ExecOptions): Promise<string> {
- options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options);
- log.info(`running command: ${command}`);
- return new Promise((resolve, reject) => {
- exec(command, options, (err, stdout, _) => {
- if (err) {
- log.error(err);
- reject(err);
- return;
- }
-
- resolve(stdout.trimEnd());
- });
- });
-}
-
export class LazyOutputChannel implements vscode.OutputChannel {
constructor(name: string) {
this.name = name;
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index e1c49db..7ec3247 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lsp-server"
-version = "0.7.3"
+version = "0.7.4"
description = "Generic LSP server scaffold."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"
@@ -9,8 +9,7 @@
[dependencies]
log = "0.4.17"
serde_json = "1.0.96"
-# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
-serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
+serde = { version = "1.0.156", features = ["derive"] }
crossbeam-channel = "0.5.6"
[dev-dependencies]
diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs
index 7720ad6..e52cbfc 100644
--- a/src/tools/rust-analyzer/xtask/src/flags.rs
+++ b/src/tools/rust-analyzer/xtask/src/flags.rs
@@ -114,6 +114,7 @@
AnalyzeRipgrep,
AnalyzeWebRender,
AnalyzeDiesel,
+ AnalyzeHyper,
}
impl FromStr for MeasurementType {
@@ -122,13 +123,26 @@
match s {
"build" => Ok(Self::Build),
"self" => Ok(Self::AnalyzeSelf),
- "ripgrep" => Ok(Self::AnalyzeRipgrep),
- "webrender" => Ok(Self::AnalyzeWebRender),
- "diesel" => Ok(Self::AnalyzeDiesel),
+ "ripgrep-13.0.0" => Ok(Self::AnalyzeRipgrep),
+ "webrender-2022" => Ok(Self::AnalyzeWebRender),
+ "diesel-1.4.8" => Ok(Self::AnalyzeDiesel),
+ "hyper-0.14.18" => Ok(Self::AnalyzeHyper),
_ => Err("Invalid option".to_string()),
}
}
}
+impl AsRef<str> for MeasurementType {
+ fn as_ref(&self) -> &str {
+ match self {
+ Self::Build => "build",
+ Self::AnalyzeSelf => "self",
+ Self::AnalyzeRipgrep => "ripgrep-13.0.0",
+ Self::AnalyzeWebRender => "webrender-2022",
+ Self::AnalyzeDiesel => "diesel-1.4.8",
+ Self::AnalyzeHyper => "hyper-0.14.18",
+ }
+ }
+}
#[derive(Debug)]
pub struct Metrics {
diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs
index e471026..59d41d8 100644
--- a/src/tools/rust-analyzer/xtask/src/metrics.rs
+++ b/src/tools/rust-analyzer/xtask/src/metrics.rs
@@ -29,46 +29,38 @@
let _env = sh.push_env("RA_METRICS", "1");
- let filename = match self.measurement_type {
- Some(ms) => match ms {
- MeasurementType::Build => {
- metrics.measure_build(sh)?;
- "build.json"
- }
- MeasurementType::AnalyzeSelf => {
- metrics.measure_analysis_stats_self(sh)?;
- "self.json"
- }
- MeasurementType::AnalyzeRipgrep => {
- metrics.measure_analysis_stats(sh, "ripgrep")?;
- "ripgrep.json"
- }
- MeasurementType::AnalyzeWebRender => {
- {
- // https://github.com/rust-lang/rust-analyzer/issues/9997
- let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender");
- cmd!(sh, "cargo update -p url --precise 1.6.1").run()?;
+ let name = match &self.measurement_type {
+ Some(ms) => {
+ let name = ms.as_ref();
+ match ms {
+ MeasurementType::Build => {
+ metrics.measure_build(sh)?;
}
- metrics.measure_analysis_stats(sh, "webrender")?;
- "webrender.json"
- }
- MeasurementType::AnalyzeDiesel => {
- metrics.measure_analysis_stats(sh, "diesel/diesel")?;
- "diesel.json"
- }
- },
+ MeasurementType::AnalyzeSelf => {
+ metrics.measure_analysis_stats_self(sh)?;
+ }
+ MeasurementType::AnalyzeRipgrep
+ | MeasurementType::AnalyzeWebRender
+ | MeasurementType::AnalyzeDiesel
+ | MeasurementType::AnalyzeHyper => {
+ metrics.measure_analysis_stats(sh, name)?;
+ }
+ };
+ name
+ }
None => {
metrics.measure_build(sh)?;
metrics.measure_analysis_stats_self(sh)?;
- metrics.measure_analysis_stats(sh, "ripgrep")?;
- metrics.measure_analysis_stats(sh, "webrender")?;
- metrics.measure_analysis_stats(sh, "diesel/diesel")?;
- "all.json"
+ metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeRipgrep.as_ref())?;
+ metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeWebRender.as_ref())?;
+ metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeDiesel.as_ref())?;
+ metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeHyper.as_ref())?;
+ "all"
}
};
let mut file =
- fs::File::options().write(true).create(true).open(format!("target/{}", filename))?;
+ fs::File::options().write(true).create(true).open(format!("target/{}.json", name))?;
writeln!(file, "{}", metrics.json())?;
eprintln!("{metrics:#?}");
Ok(())
@@ -93,7 +85,7 @@
self.measure_analysis_stats_path(
sh,
bench,
- &format!("./target/rustc-perf/collector/benchmarks/{bench}"),
+ &format!("./target/rustc-perf/collector/compile-benchmarks/{bench}"),
)
}
fn measure_analysis_stats_path(
@@ -102,6 +94,7 @@
name: &str,
path: &str,
) -> anyhow::Result<()> {
+ assert!(Path::new(path).exists(), "unable to find bench in {path}");
eprintln!("\nMeasuring analysis-stats/{name}");
let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?;
for (metric, value, unit) in parse_metrics(&output) {
@@ -145,7 +138,7 @@
let host = Host::new(sh)?;
let timestamp = SystemTime::now();
let revision = cmd!(sh, "git rev-parse HEAD").read()?;
- let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into();
+ let perf_revision = "a584462e145a0c04760fd9391daefb4f6bd13a99".into();
Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() })
}
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 416517d..c7e6dd3 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -23,7 +23,9 @@
}
function shouldIgnoreField(fieldName) {
- return fieldName === "query" || fieldName === "correction";
+ return fieldName === "query" || fieldName === "correction" ||
+ fieldName === "proposeCorrectionFrom" ||
+ fieldName === "proposeCorrectionTo";
}
// This function is only called when no matching result was found and therefore will only display
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index ca16001..fc69c14 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -63,7 +63,6 @@
pub mod fluent_alphabetical;
pub mod mir_opt_tests;
pub mod pal;
-pub mod primitive_docs;
pub mod rustdoc_css_themes;
pub mod rustdoc_gui_tests;
pub mod style;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 5585e12..80e58ba 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -112,7 +112,6 @@
// Checks that only make sense for the std libs.
check!(pal, &library_path);
- check!(primitive_docs, &library_path);
// Checks that need to be done for both the compiler and std libraries.
check!(unit_tests, &src_path);
diff --git a/src/tools/tidy/src/primitive_docs.rs b/src/tools/tidy/src/primitive_docs.rs
deleted file mode 100644
index f3200e0..0000000
--- a/src/tools/tidy/src/primitive_docs.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//! Tidy check to make sure `library/{std,core}/src/primitive_docs.rs` are the same file. These are
-//! different files so that relative links work properly without having to have `CARGO_PKG_NAME`
-//! set, but conceptually they should always be the same.
-
-use std::path::Path;
-
-pub fn check(library_path: &Path, bad: &mut bool) {
- let std_name = "std/src/primitive_docs.rs";
- let core_name = "core/src/primitive_docs.rs";
- let std_contents = std::fs::read_to_string(library_path.join(std_name))
- .unwrap_or_else(|e| panic!("failed to read library/{std_name}: {e}"));
- let core_contents = std::fs::read_to_string(library_path.join(core_name))
- .unwrap_or_else(|e| panic!("failed to read library/{core_name}: {e}"));
- if std_contents != core_contents {
- tidy_error!(bad, "library/{core_name} and library/{std_name} have different contents");
- }
-}
diff --git a/tests/coverage-map/status-quo/closure.cov-map b/tests/coverage-map/status-quo/closure.cov-map
index f3a32f0..7dbf6ec 100644
--- a/tests/coverage-map/status-quo/closure.cov-map
+++ b/tests/coverage-map/status-quo/closure.cov-map
@@ -1,5 +1,5 @@
Function name: closure::main
-Raw bytes (170): 0x[01, 01, 17, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 05, 05, 5a, 01, 05, 18, 01, 08, 01, 0f, 0d, 03, 16, 0e, 06, 0a, 07, 10, 05, 13, 0d, 0b, 1a, 0e, 08, 09, 0f, 10, 05, 0e, 09, 13, 16, 05, 0d, 18, 17, 19, 09, 01, 21, 1b, 04, 09, 00, 29, 1f, 01, 09, 00, 2d, 23, 01, 09, 00, 24, 27, 05, 09, 00, 24, 2b, 02, 09, 00, 21, 2f, 04, 09, 00, 21, 33, 04, 09, 00, 28, 37, 09, 09, 00, 32, 3b, 04, 09, 00, 33, 3f, 07, 09, 00, 4b, 43, 08, 09, 01, 09, 47, 0a, 09, 01, 09, 4b, 08, 09, 01, 09, 4f, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 5a, 04, 06, 00, 07, 57, 01, 05, 03, 02]
+Raw bytes (170): 0x[01, 01, 17, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 05, 05, 5a, 01, 05, 18, 01, 08, 01, 0f, 0d, 03, 16, 0e, 06, 0a, 07, 10, 05, 13, 0d, 0b, 1a, 0e, 06, 0a, 0f, 10, 05, 0c, 16, 13, 16, 05, 0d, 18, 17, 19, 09, 01, 1e, 1b, 04, 09, 00, 29, 1f, 01, 09, 00, 2d, 23, 01, 09, 00, 24, 27, 05, 09, 00, 24, 2b, 02, 09, 00, 21, 2f, 04, 09, 00, 21, 33, 04, 09, 00, 28, 37, 09, 09, 00, 32, 3b, 04, 09, 00, 33, 3f, 07, 09, 00, 4b, 43, 08, 09, 00, 48, 47, 0a, 09, 00, 47, 4b, 08, 09, 00, 44, 4f, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 5a, 04, 06, 00, 07, 57, 01, 05, 03, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 23
@@ -32,13 +32,13 @@
= (c0 + Zero)
- Code(Expression(1, Add)) at (prev + 16, 5) to (start + 19, 13)
= (c0 + Zero)
-- Code(Expression(2, Add)) at (prev + 26, 14) to (start + 8, 9)
+- Code(Expression(2, Add)) at (prev + 26, 14) to (start + 6, 10)
= (c0 + Zero)
-- Code(Expression(3, Add)) at (prev + 16, 5) to (start + 14, 9)
+- Code(Expression(3, Add)) at (prev + 16, 5) to (start + 12, 22)
= (c0 + Zero)
- Code(Expression(4, Add)) at (prev + 22, 5) to (start + 13, 24)
= (c0 + Zero)
-- Code(Expression(5, Add)) at (prev + 25, 9) to (start + 1, 33)
+- Code(Expression(5, Add)) at (prev + 25, 9) to (start + 1, 30)
= (c0 + Zero)
- Code(Expression(6, Add)) at (prev + 4, 9) to (start + 0, 41)
= (c0 + Zero)
@@ -60,11 +60,11 @@
= (c0 + Zero)
- Code(Expression(15, Add)) at (prev + 7, 9) to (start + 0, 75)
= (c0 + Zero)
-- Code(Expression(16, Add)) at (prev + 8, 9) to (start + 1, 9)
+- Code(Expression(16, Add)) at (prev + 8, 9) to (start + 0, 72)
= (c0 + Zero)
-- Code(Expression(17, Add)) at (prev + 10, 9) to (start + 1, 9)
+- Code(Expression(17, Add)) at (prev + 10, 9) to (start + 0, 71)
= (c0 + Zero)
-- Code(Expression(18, Add)) at (prev + 8, 9) to (start + 1, 9)
+- Code(Expression(18, Add)) at (prev + 8, 9) to (start + 0, 68)
= (c0 + Zero)
- Code(Expression(19, Add)) at (prev + 10, 8) to (start + 0, 16)
= (c0 + Zero)
diff --git a/tests/coverage-map/status-quo/closure_bug.cov-map b/tests/coverage-map/status-quo/closure_bug.cov-map
new file mode 100644
index 0000000..4fe2e5a
--- /dev/null
+++ b/tests/coverage-map/status-quo/closure_bug.cov-map
@@ -0,0 +1,148 @@
+Function name: closure_bug::main
+Raw bytes (241): 0x[01, 01, 34, 01, 00, 01, 05, 05, ce, 01, 01, 05, cb, 01, 00, 05, ce, 01, 01, 05, cb, 01, 09, 05, ce, 01, 01, 05, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, c3, 01, 00, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, bb, 01, 00, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, bb, 01, 11, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 11, b6, 01, bb, 01, 11, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 11, 01, 06, 01, 03, 0a, 03, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, ce, 01, 00, 17, 00, 18, cb, 01, 02, 09, 00, 0a, 13, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, c6, 01, 00, 17, 00, 18, c3, 01, 02, 09, 00, 0a, 3b, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, be, 01, 00, 17, 00, 18, bb, 01, 02, 09, 00, 0a, 7b, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, b6, 01, 00, 17, 00, 18, b3, 01, 01, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 52
+- expression 0 operands: lhs = Counter(0), rhs = Zero
+- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 2 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 4 operands: lhs = Expression(50, Add), rhs = Zero
+- expression 5 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 6 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 7 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 8 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 9 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 10 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 11 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 12 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 13 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 14 operands: lhs = Expression(48, Add), rhs = Zero
+- expression 15 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 16 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 17 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 18 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 19 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 20 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 21 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 22 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 23 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 24 operands: lhs = Counter(3), rhs = Expression(47, Sub)
+- expression 25 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 26 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 27 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 28 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 29 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 30 operands: lhs = Expression(46, Add), rhs = Zero
+- expression 31 operands: lhs = Counter(3), rhs = Expression(47, Sub)
+- expression 32 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 33 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 34 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 35 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 36 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 37 operands: lhs = Expression(46, Add), rhs = Counter(4)
+- expression 38 operands: lhs = Counter(3), rhs = Expression(47, Sub)
+- expression 39 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 40 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 41 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 42 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 43 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 44 operands: lhs = Counter(4), rhs = Expression(45, Sub)
+- expression 45 operands: lhs = Expression(46, Add), rhs = Counter(4)
+- expression 46 operands: lhs = Counter(3), rhs = Expression(47, Sub)
+- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 48 operands: lhs = Counter(2), rhs = Expression(49, Sub)
+- expression 49 operands: lhs = Expression(50, Add), rhs = Counter(2)
+- expression 50 operands: lhs = Counter(1), rhs = Expression(51, Sub)
+- expression 51 operands: lhs = Counter(0), rhs = Counter(1)
+Number of file 0 mappings: 17
+- Code(Counter(0)) at (prev + 6, 1) to (start + 3, 10)
+- Code(Expression(0, Add)) at (prev + 9, 5) to (start + 1, 14)
+ = (c0 + Zero)
+- Code(Counter(1)) at (prev + 1, 15) to (start + 0, 23)
+- Code(Expression(51, Sub)) at (prev + 0, 23) to (start + 0, 24)
+ = (c0 - c1)
+- Code(Expression(50, Add)) at (prev + 2, 9) to (start + 0, 10)
+ = (c1 + (c0 - c1))
+- Code(Expression(4, Add)) at (prev + 6, 5) to (start + 1, 14)
+ = ((c1 + (c0 - c1)) + Zero)
+- Code(Counter(2)) at (prev + 1, 15) to (start + 0, 23)
+- Code(Expression(49, Sub)) at (prev + 0, 23) to (start + 0, 24)
+ = ((c1 + (c0 - c1)) - c2)
+- Code(Expression(48, Add)) at (prev + 2, 9) to (start + 0, 10)
+ = (c2 + ((c1 + (c0 - c1)) - c2))
+- Code(Expression(14, Add)) at (prev + 6, 5) to (start + 1, 14)
+ = ((c2 + ((c1 + (c0 - c1)) - c2)) + Zero)
+- Code(Counter(3)) at (prev + 1, 15) to (start + 0, 23)
+- Code(Expression(47, Sub)) at (prev + 0, 23) to (start + 0, 24)
+ = ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)
+- Code(Expression(46, Add)) at (prev + 2, 9) to (start + 0, 10)
+ = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3))
+- Code(Expression(30, Add)) at (prev + 6, 5) to (start + 1, 14)
+ = ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) + Zero)
+- Code(Counter(4)) at (prev + 1, 15) to (start + 0, 23)
+- Code(Expression(45, Sub)) at (prev + 0, 23) to (start + 0, 24)
+ = ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)
+- Code(Expression(44, Add)) at (prev + 1, 1) to (start + 0, 2)
+ = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4))
+
+Function name: closure_bug::main::{closure#0}
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0d, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
+Number of file 0 mappings: 4
+- Code(Counter(0)) at (prev + 13, 9) to (start + 0, 18)
+- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25)
+- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40)
+ = (c0 - c1)
+- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42)
+ = (c1 + (c0 - c1))
+
+Function name: closure_bug::main::{closure#1}
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 16, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
+Number of file 0 mappings: 4
+- Code(Counter(0)) at (prev + 22, 9) to (start + 0, 18)
+- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25)
+- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40)
+ = (c0 - c1)
+- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42)
+ = (c1 + (c0 - c1))
+
+Function name: closure_bug::main::{closure#2}
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 1f, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
+Number of file 0 mappings: 4
+- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 18)
+- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25)
+- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40)
+ = (c0 - c1)
+- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42)
+ = (c1 + (c0 - c1))
+
+Function name: closure_bug::main::{closure#3}
+Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 2
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
+Number of file 0 mappings: 4
+- Code(Counter(0)) at (prev + 40, 9) to (start + 0, 18)
+- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25)
+- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40)
+ = (c0 - c1)
+- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42)
+ = (c1 + (c0 - c1))
+
diff --git a/tests/coverage-map/status-quo/closure_bug.rs b/tests/coverage-map/status-quo/closure_bug.rs
new file mode 100644
index 0000000..739bc5f
--- /dev/null
+++ b/tests/coverage-map/status-quo/closure_bug.rs
@@ -0,0 +1,44 @@
+// Regression test for #115930.
+// All of these closures are identical, and should produce identical output in
+// the coverage report. However, an unstable sort was causing them to be treated
+// inconsistently when preparing coverage spans.
+
+fn main() {
+ let truthy = std::env::args().len() == 1;
+
+ let a
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ a();
+ if truthy { a(); }
+
+ let b
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ b();
+ if truthy { b(); }
+
+ let c
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ c();
+ if truthy { c(); }
+
+ let d
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ d();
+ if truthy { d(); }
+}
diff --git a/tests/coverage-map/status-quo/generator.cov-map b/tests/coverage-map/status-quo/generator.cov-map
index 6e10b58..a66c1af 100644
--- a/tests/coverage-map/status-quo/generator.cov-map
+++ b/tests/coverage-map/status-quo/generator.cov-map
@@ -14,7 +14,7 @@
= (c1 + (c0 - c1))
Function name: generator::main
-Raw bytes (71): 0x[01, 01, 0b, 01, 00, 05, 0b, 09, 0d, 11, 00, 11, 15, 2a, 19, 11, 15, 15, 19, 26, 00, 2a, 19, 11, 15, 09, 01, 0f, 01, 02, 19, 03, 07, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 07, 01, 0e, 00, 35, 0f, 02, 0b, 00, 2e, 2a, 01, 22, 00, 27, 26, 00, 2c, 00, 2e, 1f, 01, 0e, 00, 35, 23, 02, 01, 00, 02]
+Raw bytes (71): 0x[01, 01, 0b, 01, 00, 05, 0b, 09, 0d, 11, 00, 11, 15, 2a, 19, 11, 15, 15, 19, 26, 00, 2a, 19, 11, 15, 09, 01, 0f, 01, 02, 16, 03, 07, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 07, 01, 0e, 00, 35, 0f, 02, 0b, 00, 2e, 2a, 01, 22, 00, 27, 26, 00, 2c, 00, 2e, 1f, 01, 0e, 00, 35, 23, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 11
@@ -30,7 +30,7 @@
- expression 9 operands: lhs = Expression(10, Sub), rhs = Counter(6)
- expression 10 operands: lhs = Counter(4), rhs = Counter(5)
Number of file 0 mappings: 9
-- Code(Counter(0)) at (prev + 15, 1) to (start + 2, 25)
+- Code(Counter(0)) at (prev + 15, 1) to (start + 2, 22)
- Code(Expression(0, Add)) at (prev + 7, 11) to (start + 0, 46)
= (c0 + Zero)
- Code(Counter(4)) at (prev + 1, 43) to (start + 0, 45)
diff --git a/tests/mir-opt/dont_inline_type_id.call.Inline.diff b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
new file mode 100644
index 0000000..80cfe07
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
@@ -0,0 +1,20 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+
+ fn call(_1: &T) -> TypeId {
+ debug s => _1;
+ let mut _0: std::any::TypeId;
+ let mut _2: &T;
+
+ bb0: {
+ StorageLive(_2);
+ _2 = &(*_1);
+ _0 = <T as Any>::type_id(move _2) -> [return: bb1, unwind unreachable];
+ }
+
+ bb1: {
+ StorageDead(_2);
+ return;
+ }
+ }
+
diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs
new file mode 100644
index 0000000..d8a5663
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.rs
@@ -0,0 +1,15 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+use std::any::Any;
+use std::any::TypeId;
+
+struct A<T: ?Sized + 'static> {
+ a: i32,
+ b: T,
+}
+
+// EMIT_MIR dont_inline_type_id.call.Inline.diff
+pub fn call<T: ?Sized + 'static>(s: &T) -> TypeId {
+ s.type_id()
+}
diff --git "a/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-abort.mir" "b/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-abort.mir"
index acbb790..958078b 100644
--- "a/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-abort.mir"
+++ "b/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-abort.mir"
@@ -2,11 +2,7 @@
/* generator_layout = GeneratorLayout {
field_tys: {
_0: GeneratorSavedTy {
- ty: Adt(
- std::string::String,
- [
- ],
- ),
+ ty: std::string::String,
source_info: SourceInfo {
span: $DIR/generator_drop_cleanup.rs:11:13: 11:15 (#0),
scope: scope[0],
diff --git "a/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-unwind.mir" "b/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-unwind.mir"
index c17d442..7e050e5 100644
--- "a/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-unwind.mir"
+++ "b/tests/mir-opt/generator_drop_cleanup.main-\173closure\0430\175.generator_drop.0.panic-unwind.mir"
@@ -2,11 +2,7 @@
/* generator_layout = GeneratorLayout {
field_tys: {
_0: GeneratorSavedTy {
- ty: Adt(
- std::string::String,
- [
- ],
- ),
+ ty: std::string::String,
source_info: SourceInfo {
span: $DIR/generator_drop_cleanup.rs:11:13: 11:15 (#0),
scope: scope[0],
diff --git "a/tests/mir-opt/generator_tiny.main-\173closure\0430\175.generator_resume.0.mir" "b/tests/mir-opt/generator_tiny.main-\173closure\0430\175.generator_resume.0.mir"
index e33f5f5..13d703b 100644
--- "a/tests/mir-opt/generator_tiny.main-\173closure\0430\175.generator_resume.0.mir"
+++ "b/tests/mir-opt/generator_tiny.main-\173closure\0430\175.generator_resume.0.mir"
@@ -2,11 +2,7 @@
/* generator_layout = GeneratorLayout {
field_tys: {
_0: GeneratorSavedTy {
- ty: Adt(
- HasDrop,
- [
- ],
- ),
+ ty: HasDrop,
source_info: SourceInfo {
span: $DIR/generator_tiny.rs:20:13: 20:15 (#0),
scope: scope[0],
diff --git a/tests/mir-opt/inline_generically_if_sized.call.Inline.diff b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
new file mode 100644
index 0000000..0cf4565
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
@@ -0,0 +1,24 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+
+ fn call(_1: &T) -> i32 {
+ debug s => _1;
+ let mut _0: i32;
+ let mut _2: &T;
++ scope 1 (inlined <T as Foo>::bar) {
++ debug self => _2;
++ }
+
+ bb0: {
+ StorageLive(_2);
+ _2 = &(*_1);
+- _0 = <T as Foo>::bar(move _2) -> [return: bb1, unwind unreachable];
+- }
+-
+- bb1: {
++ _0 = const 0_i32;
+ StorageDead(_2);
+ return;
+ }
+ }
+
diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs
new file mode 100644
index 0000000..1acfff7
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.rs
@@ -0,0 +1,17 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+trait Foo {
+ fn bar(&self) -> i32;
+}
+
+impl<T> Foo for T {
+ fn bar(&self) -> i32 {
+ 0
+ }
+}
+
+// EMIT_MIR inline_generically_if_sized.call.Inline.diff
+pub fn call<T>(s: &T) -> i32 {
+ s.bar()
+}
diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
index 1d3405a..18a663d 100644
--- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
@@ -11,7 +11,7 @@
_0 = inner(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/spans.rs:10:5: 10:14
// mir::Constant
// + span: $DIR/spans.rs:10:5: 10:10
- // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(<ZST>) }
+ // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
}
bb1: {
diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
index 285b6e4..1c02fb7 100644
--- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
@@ -11,7 +11,7 @@
_0 = inner(move _2) -> [return: bb1, unwind continue]; // scope 0 at $DIR/spans.rs:10:5: 10:14
// mir::Constant
// + span: $DIR/spans.rs:10:5: 10:10
- // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(<ZST>) }
+ // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
}
bb1: {
diff --git a/tests/run-coverage/closure.coverage b/tests/run-coverage/closure.coverage
index 930348d..67014f7 100644
--- a/tests/run-coverage/closure.coverage
+++ b/tests/run-coverage/closure.coverage
@@ -76,8 +76,8 @@
LL| 1| some_string = None;
LL| 1| let
LL| 1| a
- LL| 1| =
- LL| 1| ||
+ LL| | =
+ LL| | ||
LL| 1| {
LL| 1| let mut countdown = 0;
LL| 1| if is_false {
@@ -98,8 +98,8 @@
LL| 1|
LL| 1| let
LL| 1| quote_closure
- LL| 1| =
- LL| 1| |val|
+ LL| | =
+ LL| | |val|
LL| 5| {
LL| 5| let mut countdown = 0;
LL| 5| if is_false {
@@ -186,7 +186,7 @@
LL| | ;
LL| |
LL| 1| let short_used_not_covered_closure_line_break_block_embedded_branch =
- LL| 1| | _unused_arg: u8 |
+ LL| | | _unused_arg: u8 |
LL| 0| {
LL| 0| println!(
LL| 0| "not called: {}",
@@ -196,7 +196,7 @@
LL| | ;
LL| |
LL| 1| let short_used_covered_closure_line_break_no_block_embedded_branch =
- LL| 1| | _unused_arg: u8 |
+ LL| | | _unused_arg: u8 |
LL| 1| println!(
LL| 1| "not called: {}",
LL| 1| if is_true { "check" } else { "me" }
@@ -205,7 +205,7 @@
LL| | ;
LL| |
LL| 1| let short_used_covered_closure_line_break_block_embedded_branch =
- LL| 1| | _unused_arg: u8 |
+ LL| | | _unused_arg: u8 |
LL| 1| {
LL| 1| println!(
LL| 1| "not called: {}",
diff --git a/tests/run-coverage/closure_bug.coverage b/tests/run-coverage/closure_bug.coverage
new file mode 100644
index 0000000..f329983
--- /dev/null
+++ b/tests/run-coverage/closure_bug.coverage
@@ -0,0 +1,53 @@
+ LL| |// Regression test for #115930.
+ LL| |// All of these closures are identical, and should produce identical output in
+ LL| |// the coverage report. However, an unstable sort was causing them to be treated
+ LL| |// inconsistently when preparing coverage spans.
+ LL| |
+ LL| 1|fn main() {
+ LL| 1| let truthy = std::env::args().len() == 1;
+ LL| 1|
+ LL| 1| let a
+ LL| | =
+ LL| | |
+ LL| | |
+ LL| 2| if truthy { true } else { false };
+ ^0
+ LL| |
+ LL| 1| a();
+ LL| 1| if truthy { a(); }
+ ^0
+ LL| |
+ LL| 1| let b
+ LL| | =
+ LL| | |
+ LL| | |
+ LL| 2| if truthy { true } else { false };
+ ^0
+ LL| |
+ LL| 1| b();
+ LL| 1| if truthy { b(); }
+ ^0
+ LL| |
+ LL| 1| let c
+ LL| | =
+ LL| | |
+ LL| | |
+ LL| 2| if truthy { true } else { false };
+ ^0
+ LL| |
+ LL| 1| c();
+ LL| 1| if truthy { c(); }
+ ^0
+ LL| |
+ LL| 1| let d
+ LL| | =
+ LL| | |
+ LL| | |
+ LL| 2| if truthy { true } else { false };
+ ^0
+ LL| |
+ LL| 1| d();
+ LL| 1| if truthy { d(); }
+ ^0
+ LL| 1|}
+
diff --git a/tests/run-coverage/closure_bug.rs b/tests/run-coverage/closure_bug.rs
new file mode 100644
index 0000000..739bc5f
--- /dev/null
+++ b/tests/run-coverage/closure_bug.rs
@@ -0,0 +1,44 @@
+// Regression test for #115930.
+// All of these closures are identical, and should produce identical output in
+// the coverage report. However, an unstable sort was causing them to be treated
+// inconsistently when preparing coverage spans.
+
+fn main() {
+ let truthy = std::env::args().len() == 1;
+
+ let a
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ a();
+ if truthy { a(); }
+
+ let b
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ b();
+ if truthy { b(); }
+
+ let c
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ c();
+ if truthy { c(); }
+
+ let d
+ =
+ |
+ |
+ if truthy { true } else { false };
+
+ d();
+ if truthy { d(); }
+}
diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml
index 5d1b83b..aeb3c9b 100644
--- a/tests/rustdoc-gui/search-corrections.goml
+++ b/tests/rustdoc-gui/search-corrections.goml
@@ -54,3 +54,53 @@
".search-corrections",
"Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
)
+
+// Now, generic correction
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+ "display": "block"
+})
+assert-text: (
+ ".search-corrections",
+ "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+// Now, generic correction plus error
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "Foo<NotableStructWithLongNamr>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+ "display": "block"
+})
+assert-text: (
+ ".search-corrections",
+ "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "generic:NotableStructWithLongNamr<x>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".error", {
+ "display": "block"
+})
+assert-text: (
+ ".error",
+ "Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"."
+)
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
index 2599785..e154fa7 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -1,3 +1,7 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
const EXPECTED = [
{
'query': 'option, fnonce -> option',
@@ -19,4 +23,62 @@
{ 'path': 'std::option::Option', 'name': 'as_mut_slice' },
],
},
+ {
+ 'query': 'option<t>, option<t> -> option<t>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'or' },
+ { 'path': 'std::option::Option', 'name': 'xor' },
+ ],
+ },
+ {
+ 'query': 'option<t>, option<u> -> option<u>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'and' },
+ { 'path': 'std::option::Option', 'name': 'zip' },
+ ],
+ },
+ {
+ 'query': 'option<t>, option<u> -> option<t>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'and' },
+ { 'path': 'std::option::Option', 'name': 'zip' },
+ ],
+ },
+ {
+ 'query': 'option<t>, option<u> -> option<t, u>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'zip' },
+ ],
+ },
+ {
+ 'query': 'option<t>, e -> result<t, e>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'ok_or' },
+ { 'path': 'std::result::Result', 'name': 'transpose' },
+ ],
+ },
+ {
+ 'query': 'result<option<t>, e> -> option<result<t, e>>',
+ 'others': [
+ { 'path': 'std::result::Result', 'name': 'transpose' },
+ ],
+ },
+ {
+ 'query': 'option<t>, option<t> -> bool',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'eq' },
+ ],
+ },
+ {
+ 'query': 'option<option<t>> -> option<t>',
+ 'others': [
+ { 'path': 'std::option::Option', 'name': 'flatten' },
+ ],
+ },
+ {
+ 'query': 'option<t>',
+ 'returned': [
+ { 'path': 'std::result::Result', 'name': 'ok' },
+ ],
+ },
];
diff --git a/tests/rustdoc-js-std/vec-type-signatures.js b/tests/rustdoc-js-std/vec-type-signatures.js
new file mode 100644
index 0000000..18cf9d6
--- /dev/null
+++ b/tests/rustdoc-js-std/vec-type-signatures.js
@@ -0,0 +1,22 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
+const EXPECTED = [
+ {
+ 'query': 'vec::intoiter<T> -> [T]',
+ 'others': [
+ { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+ { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+ { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+ ],
+ },
+ {
+ 'query': 'vec::intoiter<T> -> []',
+ 'others': [
+ { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+ { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+ { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+ ],
+ },
+];
diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js
index a9932a1..edce426 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.js
+++ b/tests/rustdoc-js/generics-match-ambiguity.js
@@ -79,6 +79,7 @@
{ 'path': 'generics_match_ambiguity', 'name': 'baac' },
{ 'path': 'generics_match_ambiguity', 'name': 'baaf' },
{ 'path': 'generics_match_ambiguity', 'name': 'baag' },
+ { 'path': 'generics_match_ambiguity', 'name': 'baah' },
],
},
{
diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js
index 4ccfb8f..a71393b 100644
--- a/tests/rustdoc-js/generics-trait.js
+++ b/tests/rustdoc-js/generics-trait.js
@@ -12,12 +12,16 @@
],
},
{
- 'query': 'Result<SomeTraiz>',
- 'correction': null,
+ 'query': 'Resulx<SomeTrait>',
'in_args': [],
'returned': [],
},
{
+ 'query': 'Result<SomeTraiz>',
+ 'proposeCorrectionFrom': 'SomeTraiz',
+ 'proposeCorrectionTo': 'SomeTrait',
+ },
+ {
'query': 'OtherThingxxxxxxxx',
'correction': null,
'in_args': [
diff --git a/tests/rustdoc-js/generics-unbox.js b/tests/rustdoc-js/generics-unbox.js
new file mode 100644
index 0000000..9cdfc7a
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.js
@@ -0,0 +1,38 @@
+// exact-check
+
+const EXPECTED = [
+ {
+ 'query': 'Inside<T> -> Out1<T>',
+ 'others': [
+ { 'path': 'generics_unbox', 'name': 'alpha' },
+ ],
+ },
+ {
+ 'query': 'Inside<T> -> Out3<T>',
+ 'others': [
+ { 'path': 'generics_unbox', 'name': 'beta' },
+ { 'path': 'generics_unbox', 'name': 'gamma' },
+ ],
+ },
+ {
+ 'query': 'Inside<T> -> Out4<T>',
+ 'others': [
+ { 'path': 'generics_unbox', 'name': 'beta' },
+ { 'path': 'generics_unbox', 'name': 'gamma' },
+ ],
+ },
+ {
+ 'query': 'Inside<T> -> Out3<U, T>',
+ 'others': [
+ { 'path': 'generics_unbox', 'name': 'beta' },
+ { 'path': 'generics_unbox', 'name': 'gamma' },
+ ],
+ },
+ {
+ 'query': 'Inside<T> -> Out4<U, T>',
+ 'others': [
+ { 'path': 'generics_unbox', 'name': 'beta' },
+ { 'path': 'generics_unbox', 'name': 'gamma' },
+ ],
+ },
+];
diff --git a/tests/rustdoc-js/generics-unbox.rs b/tests/rustdoc-js/generics-unbox.rs
new file mode 100644
index 0000000..bef34f8
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.rs
@@ -0,0 +1,36 @@
+pub struct Out<A, B = ()> {
+ a: A,
+ b: B,
+}
+
+pub struct Out1<A, const N: usize> {
+ a: [A; N],
+}
+
+pub struct Out2<A, const N: usize> {
+ a: [A; N],
+}
+
+pub struct Out3<A, B> {
+ a: A,
+ b: B,
+}
+
+pub struct Out4<A, B> {
+ a: A,
+ b: B,
+}
+
+pub struct Inside<T>(T);
+
+pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {
+ loop {}
+}
+
+pub fn beta<T, U>(_: Inside<T>) -> Out<Out3<T, U>, Out4<U, T>> {
+ loop {}
+}
+
+pub fn gamma<T, U>(_: Inside<T>) -> Out<Out3<U, T>, Out4<T, U>> {
+ loop {}
+}
diff --git a/tests/rustdoc-js/type-parameters.js b/tests/rustdoc-js/type-parameters.js
new file mode 100644
index 0000000..e695f18
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.js
@@ -0,0 +1,87 @@
+// exact-check
+// ignore-order
+
+const EXPECTED = [
+ {
+ query: '-> trait:Some',
+ others: [
+ { path: 'foo', name: 'alef' },
+ { path: 'foo', name: 'alpha' },
+ ],
+ },
+ {
+ query: '-> generic:T',
+ others: [
+ { path: 'foo', name: 'bet' },
+ { path: 'foo', name: 'alef' },
+ { path: 'foo', name: 'beta' },
+ ],
+ },
+ {
+ query: 'A -> B',
+ others: [
+ { path: 'foo', name: 'bet' },
+ ],
+ },
+ {
+ query: 'A -> A',
+ others: [
+ { path: 'foo', name: 'beta' },
+ ],
+ },
+ {
+ query: 'A, A',
+ others: [
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'A, B',
+ others: [
+ { path: 'foo', name: 'other' },
+ ],
+ },
+ {
+ query: 'Other, Other',
+ others: [
+ { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'generic:T',
+ in_args: [
+ { path: 'foo', name: 'bet' },
+ { path: 'foo', name: 'beta' },
+ { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'generic:Other',
+ in_args: [
+ { path: 'foo', name: 'bet' },
+ { path: 'foo', name: 'beta' },
+ { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'trait:Other',
+ in_args: [
+ { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'Other',
+ in_args: [
+ { path: 'foo', name: 'other' },
+ { path: 'foo', name: 'alternate' },
+ ],
+ },
+ {
+ query: 'trait:T',
+ in_args: [],
+ },
+];
diff --git a/tests/rustdoc-js/type-parameters.rs b/tests/rustdoc-js/type-parameters.rs
new file mode 100644
index 0000000..cda5e26
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.rs
@@ -0,0 +1,15 @@
+#![crate_name="foo"]
+
+pub trait Some {}
+impl Some for () {}
+pub trait Other {}
+impl Other for () {}
+
+pub fn alef<T: Some>() -> T { loop {} }
+pub fn alpha() -> impl Some { }
+
+pub fn bet<T, U>(t: T) -> U { loop {} }
+pub fn beta<T>(t: T) -> T {}
+
+pub fn other<T: Other, U: Other>(t: T, u: U) { loop {} }
+pub fn alternate<T: Other>(t: T, u: T) { loop {} }
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index b3e75bb..d4f42cd 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -10,7 +10,6 @@
// Hence there are `cfg` throughout this test to disable parts of it on those targets.
// sparc64: https://github.com/rust-lang/rust/issues/115336
// mips64: https://github.com/rust-lang/rust/issues/115404
-// riscv64: https://github.com/rust-lang/rust/issues/115481
// loongarch64: https://github.com/rust-lang/rust/issues/115509
macro_rules! assert_abi_compatible {
@@ -110,7 +109,7 @@
test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
- #[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
+ #[cfg(not(target_arch = "loongarch64"))]
test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
}
};
diff --git a/tests/ui/associated-consts/defaults-cyclic-fail.rs b/tests/ui/associated-consts/defaults-cyclic-fail.rs
index a1c6840..9ef0003 100644
--- a/tests/ui/associated-consts/defaults-cyclic-fail.rs
+++ b/tests/ui/associated-consts/defaults-cyclic-fail.rs
@@ -3,7 +3,7 @@
// Cyclic assoc. const defaults don't error unless *used*
trait Tr {
const A: u8 = Self::B;
- //~^ cycle detected when const-evaluating + checking `Tr::A`
+ //~^ cycle detected
const B: u8 = Self::A;
}
diff --git a/tests/ui/associated-consts/defaults-cyclic-fail.stderr b/tests/ui/associated-consts/defaults-cyclic-fail.stderr
index ebdb76e..e29c32f 100644
--- a/tests/ui/associated-consts/defaults-cyclic-fail.stderr
+++ b/tests/ui/associated-consts/defaults-cyclic-fail.stderr
@@ -1,15 +1,25 @@
-error[E0391]: cycle detected when const-evaluating + checking `Tr::A`
+error[E0391]: cycle detected when simplifying constant for the type system `Tr::A`
+ --> $DIR/defaults-cyclic-fail.rs:5:5
+ |
+LL | const A: u8 = Self::B;
+ | ^^^^^^^^^^^
+ |
+note: ...which requires const-evaluating + checking `Tr::A`...
--> $DIR/defaults-cyclic-fail.rs:5:19
|
LL | const A: u8 = Self::B;
| ^^^^^^^
+note: ...which requires simplifying constant for the type system `Tr::B`...
+ --> $DIR/defaults-cyclic-fail.rs:8:5
|
+LL | const B: u8 = Self::A;
+ | ^^^^^^^^^^^
note: ...which requires const-evaluating + checking `Tr::B`...
--> $DIR/defaults-cyclic-fail.rs:8:19
|
LL | const B: u8 = Self::A;
| ^^^^^^^
- = note: ...which again requires const-evaluating + checking `Tr::A`, completing the cycle
+ = note: ...which again requires simplifying constant for the type system `Tr::A`, completing the cycle
note: cycle used when const-evaluating + checking `main::promoted[1]`
--> $DIR/defaults-cyclic-fail.rs:16:16
|
diff --git a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
index 9b761b0..d659912 100644
--- a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
+++ b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
@@ -4,13 +4,13 @@
LL | const B: u8 = Self::A + 1;
| ^^^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/defaults-not-assumed-fail.rs:33:16
|
LL | assert_eq!(<() as Tr>::B, 0); // causes the error above
| ^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/defaults-not-assumed-fail.rs:33:5
|
LL | assert_eq!(<() as Tr>::B, 0); // causes the error above
@@ -18,7 +18,7 @@
|
= note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/defaults-not-assumed-fail.rs:33:5
|
LL | assert_eq!(<() as Tr>::B, 0); // causes the error above
diff --git a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
index 51bf0cb..4418fb7 100644
--- a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
+++ b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
@@ -4,7 +4,12 @@
LL | const BAR: u32 = IMPL_REF_BAR;
| ^^^^^^^^^^^^
|
-note: ...which requires const-evaluating + checking `IMPL_REF_BAR`...
+note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1
+ |
+LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1
|
LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
@@ -14,6 +19,11 @@
|
LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
| ^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 11:19>::BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5
+ |
+LL | const BAR: u32 = IMPL_REF_BAR;
+ | ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 11:19>::BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5
|
diff --git a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
index 8277d41..392cd5e 100644
--- a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
+++ b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
@@ -4,7 +4,12 @@
LL | const BAR: u32 = DEFAULT_REF_BAR;
| ^^^^^^^^^^^^^^^
|
-note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`...
+note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1
+ |
+LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1
|
LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
@@ -14,6 +19,11 @@
|
LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
| ^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `FooDefault::BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5
+ |
+LL | const BAR: u32 = DEFAULT_REF_BAR;
+ | ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `FooDefault::BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5
|
diff --git a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
index 9983ba7..6cbddca9 100644
--- a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
+++ b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
@@ -4,7 +4,12 @@
LL | const BAR: u32 = TRAIT_REF_BAR;
| ^^^^^^^^^^^^^
|
-note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`...
+note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1
+ |
+LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1
|
LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
@@ -14,6 +19,11 @@
|
LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
| ^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires simplifying constant for the type system `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 11:28>::BAR`...
+ --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5
+ |
+LL | const BAR: u32 = TRAIT_REF_BAR;
+ | ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 11:28>::BAR`...
--> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5
|
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 0000000..85d17dd
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,38 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+#![feature(async_fn_in_trait)]
+
+fn main() {
+ let _ = async {
+ A.first().await.second().await;
+ };
+}
+
+pub trait First {
+ type Second: Second;
+ async fn first(self) -> Self::Second;
+}
+
+struct A;
+
+impl First for A {
+ type Second = A;
+ async fn first(self) -> Self::Second {
+ A
+ }
+}
+
+pub trait Second {
+ async fn second(self);
+}
+
+impl<C> Second for C
+where
+ C: First,
+{
+ async fn second(self) {
+ self.first().await.second().await;
+ }
+}
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 0000000..3f487a6
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
index 3b63ec4..8f45902 100644
--- a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
@@ -16,6 +16,14 @@
| required by a bound introduced by this call
|
= help: the trait `for<'a> Send` is not implemented for `impl Future<Output = ()> { <_ as Foo>::bar() }`
+note: this is a known limitation of the trait solver that will be lifted in the future
+ --> $DIR/normalizing-self-auto-trait-issue-109924.rs:23:11
+ |
+LL | build(Bar);
+ | ------^^^-
+ | | |
+ | | the trait solver is unable to infer the generic types that should be inferred from this argument
+ | add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required by a bound in `build`
--> $DIR/normalizing-self-auto-trait-issue-109924.rs:20:39
|
diff --git a/tests/ui/borrowck/issue-81899.stderr b/tests/ui/borrowck/issue-81899.stderr
index 1b03bc3..63e8172 100644
--- a/tests/ui/borrowck/issue-81899.stderr
+++ b/tests/ui/borrowck/issue-81899.stderr
@@ -16,7 +16,7 @@
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-81899.rs:4:23
|
LL | const _CONST: &[u8] = &f(&[], |_| {});
diff --git a/tests/ui/borrowck/issue-88434-minimal-example.stderr b/tests/ui/borrowck/issue-88434-minimal-example.stderr
index a5a571c..4c887b2 100644
--- a/tests/ui/borrowck/issue-88434-minimal-example.stderr
+++ b/tests/ui/borrowck/issue-88434-minimal-example.stderr
@@ -16,7 +16,7 @@
| ^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-88434-minimal-example.rs:3:21
|
LL | const _CONST: &() = &f(&|_| {});
diff --git a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
index 00023c4..f725781 100644
--- a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
+++ b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
@@ -16,7 +16,7 @@
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-88434-removal-index-should-be-less.rs:3:23
|
LL | const _CONST: &[u8] = &f(&[], |_| {});
diff --git a/tests/ui/cast/cast-as-bool.rs b/tests/ui/cast/cast-as-bool.rs
index 750a88f..511a027 100644
--- a/tests/ui/cast/cast-as-bool.rs
+++ b/tests/ui/cast/cast-as-bool.rs
@@ -1,11 +1,11 @@
fn main() {
let u = 5 as bool; //~ ERROR cannot cast `i32` as `bool`
//~| HELP compare with zero instead
- //~| SUGGESTION 5 != 0
+ //~| SUGGESTION != 0
let t = (1 + 2) as bool; //~ ERROR cannot cast `i32` as `bool`
//~| HELP compare with zero instead
- //~| SUGGESTION (1 + 2) != 0
+ //~| SUGGESTION != 0
let _ = 5_u32 as bool; //~ ERROR cannot cast `u32` as `bool`
//~| HELP compare with zero instead
diff --git a/tests/ui/cast/cast-as-bool.stderr b/tests/ui/cast/cast-as-bool.stderr
index 852fb30..4ff56a9 100644
--- a/tests/ui/cast/cast-as-bool.stderr
+++ b/tests/ui/cast/cast-as-bool.stderr
@@ -2,25 +2,45 @@
--> $DIR/cast-as-bool.rs:2:13
|
LL | let u = 5 as bool;
- | ^^^^^^^^^ help: compare with zero instead: `5 != 0`
+ | ^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let u = 5 != 0;
+ | ~~~~
error[E0054]: cannot cast `i32` as `bool`
--> $DIR/cast-as-bool.rs:6:13
|
LL | let t = (1 + 2) as bool;
- | ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
+ | ^^^^^^^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let t = (1 + 2) != 0;
+ | ~~~~
error[E0054]: cannot cast `u32` as `bool`
--> $DIR/cast-as-bool.rs:10:13
|
LL | let _ = 5_u32 as bool;
- | ^^^^^^^^^^^^^ help: compare with zero instead: `5_u32 != 0`
+ | ^^^^^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let _ = 5_u32 != 0;
+ | ~~~~
error[E0054]: cannot cast `f64` as `bool`
--> $DIR/cast-as-bool.rs:13:13
|
LL | let _ = 64.0_f64 as bool;
- | ^^^^^^^^^^^^^^^^ help: compare with zero instead: `64.0_f64 != 0`
+ | ^^^^^^^^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let _ = 64.0_f64 != 0;
+ | ~~~~
error[E0054]: cannot cast `IntEnum` as `bool`
--> $DIR/cast-as-bool.rs:24:13
diff --git a/tests/ui/cast/cast-rfc0401-2.stderr b/tests/ui/cast/cast-rfc0401-2.stderr
index 5dc21ca..dd90c3a 100644
--- a/tests/ui/cast/cast-rfc0401-2.stderr
+++ b/tests/ui/cast/cast-rfc0401-2.stderr
@@ -2,7 +2,12 @@
--> $DIR/cast-rfc0401-2.rs:6:13
|
LL | let _ = 3 as bool;
- | ^^^^^^^^^ help: compare with zero instead: `3 != 0`
+ | ^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let _ = 3 != 0;
+ | ~~~~
error: aborting due to previous error
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance-2.rs b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
new file mode 100644
index 0000000..e5525af
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
@@ -0,0 +1,26 @@
+// run-pass
+
+#![feature(inline_const)]
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+ fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+ fn foo(&self) -> &'static str {
+ std::any::type_name::<T>()
+ }
+}
+
+fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+ const { Trait::foo as fn(&T) -> &'static str }
+ // If const prop were to propagate the instance
+}
+
+fn main() {
+ assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance.rs b/tests/ui/const_prop/dont-propagate-generic-instance.rs
new file mode 100644
index 0000000..5994961
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance.rs
@@ -0,0 +1,24 @@
+// run-pass
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+ fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+ fn foo(&self) -> &'static str {
+ std::any::type_name::<T>()
+ }
+}
+
+const fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+ Trait::foo
+ // If const prop were to propagate the instance
+}
+
+fn main() {
+ assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/consts/const-err-late.stderr b/tests/ui/consts/const-err-late.stderr
index 149d3b5..85bc56b 100644
--- a/tests/ui/consts/const-err-late.stderr
+++ b/tests/ui/consts/const-err-late.stderr
@@ -4,7 +4,7 @@
LL | const FOO: u8 = [5u8][1];
| ^^^^^^^^ index out of bounds: the length is 1 but the index is 1
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-late.rs:19:16
|
LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
@@ -16,13 +16,13 @@
LL | const FOO: u8 = [5u8][1];
| ^^^^^^^^ index out of bounds: the length is 1 but the index is 1
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-late.rs:19:31
|
LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
| ^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-late.rs:19:16
|
LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
diff --git a/tests/ui/consts/const-err-multi.stderr b/tests/ui/consts/const-err-multi.stderr
index 28af8e5..1ad504b 100644
--- a/tests/ui/consts/const-err-multi.stderr
+++ b/tests/ui/consts/const-err-multi.stderr
@@ -4,19 +4,19 @@
LL | pub const A: i8 = -i8::MIN;
| ^^^^^^^^ attempt to negate `i8::MIN`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-multi.rs:3:19
|
LL | pub const B: i8 = A;
| ^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-multi.rs:5:19
|
LL | pub const C: u8 = A as u8;
| ^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-err-multi.rs:7:24
|
LL | pub const D: i8 = 50 - A;
diff --git a/tests/ui/consts/const-eval/erroneous-const.stderr b/tests/ui/consts/const-eval/erroneous-const.stderr
index 770f950..0e31520 100644
--- a/tests/ui/consts/const-eval/erroneous-const.stderr
+++ b/tests/ui/consts/const-eval/erroneous-const.stderr
@@ -4,7 +4,7 @@
LL | const VOID: () = [()][2];
| ^^^^^^^ index out of bounds: the length is 1 but the index is 2
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/erroneous-const.rs:13:13
|
LL | PrintName::<T>::VOID;
diff --git a/tests/ui/consts/const-eval/erroneous-const2.stderr b/tests/ui/consts/const-eval/erroneous-const2.stderr
index 082c287..4ca44694 100644
--- a/tests/ui/consts/const-eval/erroneous-const2.stderr
+++ b/tests/ui/consts/const-eval/erroneous-const2.stderr
@@ -4,7 +4,7 @@
LL | const VOID: () = [()][2];
| ^^^^^^^ index out of bounds: the length is 1 but the index is 2
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/erroneous-const2.rs:13:9
|
LL | PrintName::<i32>::VOID;
diff --git a/tests/ui/consts/const-eval/issue-44578.stderr b/tests/ui/consts/const-eval/issue-44578.stderr
index f395280..c7aaee9 100644
--- a/tests/ui/consts/const-eval/issue-44578.stderr
+++ b/tests/ui/consts/const-eval/issue-44578.stderr
@@ -4,13 +4,13 @@
LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 1 but the index is 1
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-44578.rs:25:20
|
LL | println!("{}", <Bar<u16, u8> as Foo>::AMT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-44578.rs:25:20
|
LL | println!("{}", <Bar<u16, u8> as Foo>::AMT);
@@ -18,7 +18,7 @@
|
= note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-44578.rs:25:20
|
LL | println!("{}", <Bar<u16, u8> as Foo>::AMT);
diff --git a/tests/ui/consts/const-eval/issue-50814-2.stderr b/tests/ui/consts/const-eval/issue-50814-2.stderr
index 956f7ae..450fb00 100644
--- a/tests/ui/consts/const-eval/issue-50814-2.stderr
+++ b/tests/ui/consts/const-eval/issue-50814-2.stderr
@@ -4,7 +4,7 @@
LL | const BAR: usize = [5, 6, 7][T::BOO];
| ^^^^^^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 42
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-50814-2.rs:18:6
|
LL | &<A<T> as Foo<T>>::BAR
diff --git a/tests/ui/consts/const-eval/issue-50814.stderr b/tests/ui/consts/const-eval/issue-50814.stderr
index 05b6271..48a20d0 100644
--- a/tests/ui/consts/const-eval/issue-50814.stderr
+++ b/tests/ui/consts/const-eval/issue-50814.stderr
@@ -4,7 +4,7 @@
LL | const MAX: u8 = A::MAX + B::MAX;
| ^^^^^^^^^^^^^^^ attempt to compute `u8::MAX + u8::MAX`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-50814.rs:20:6
|
LL | &Sum::<U8, U8>::MAX
diff --git a/tests/ui/consts/const-eval/panic-assoc-never-type.rs b/tests/ui/consts/const-eval/panic-assoc-never-type.rs
index 1abe708..88ce5b0 100644
--- a/tests/ui/consts/const-eval/panic-assoc-never-type.rs
+++ b/tests/ui/consts/const-eval/panic-assoc-never-type.rs
@@ -11,5 +11,5 @@
}
fn main() {
- let _ = PrintName::VOID; //~ erroneous constant used
+ let _ = PrintName::VOID; //~ erroneous constant encountered
}
diff --git a/tests/ui/consts/const-eval/panic-assoc-never-type.stderr b/tests/ui/consts/const-eval/panic-assoc-never-type.stderr
index 7c36a3a..4706497 100644
--- a/tests/ui/consts/const-eval/panic-assoc-never-type.stderr
+++ b/tests/ui/consts/const-eval/panic-assoc-never-type.stderr
@@ -6,13 +6,13 @@
|
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/panic-assoc-never-type.rs:14:13
|
LL | let _ = PrintName::VOID;
| ^^^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/panic-assoc-never-type.rs:14:13
|
LL | let _ = PrintName::VOID;
diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
index e087a0e..042e7ee 100644
--- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
+++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
@@ -341,7 +341,7 @@
╾ALLOC_ID╼ │ ╾──╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:160:40
|
LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
@@ -358,7 +358,7 @@
╾ALLOC_ID╼ │ ╾──╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:166:42
|
LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
@@ -375,7 +375,7 @@
╾ALLOC_ID╼ │ ╾──╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:170:42
|
LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
index 4c65516..8426a95 100644
--- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
+++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
@@ -341,7 +341,7 @@
╾ALLOC_ID╼ │ ╾──────╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:160:40
|
LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
@@ -358,7 +358,7 @@
╾ALLOC_ID╼ │ ╾──────╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:166:42
|
LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
@@ -375,7 +375,7 @@
╾ALLOC_ID╼ │ ╾──────╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/raw-bytes.rs:170:42
|
LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr
index 0ee1e60..6d5c36c 100644
--- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr
+++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr
@@ -60,7 +60,7 @@
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/ub-ref-ptr.rs:36:38
|
LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
@@ -75,7 +75,7 @@
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/ub-ref-ptr.rs:39:85
|
LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.stderr
index 02bbbf5..d8add67 100644
--- a/tests/ui/consts/const-eval/ub-wide-ptr.stderr
+++ b/tests/ui/consts/const-eval/ub-wide-ptr.stderr
@@ -139,7 +139,7 @@
HEX_DUMP
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/ub-wide-ptr.rs:85:40
|
LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
@@ -156,7 +156,7 @@
HEX_DUMP
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/ub-wide-ptr.rs:92:42
|
LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
@@ -173,7 +173,7 @@
HEX_DUMP
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/ub-wide-ptr.rs:96:42
|
LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr
index 9899c56..ce26030 100644
--- a/tests/ui/consts/const-eval/union-const-eval-field.stderr
+++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr
@@ -4,13 +4,13 @@
LL | const FIELD3: Field3 = unsafe { UNION.field3 };
| ^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/union-const-eval-field.rs:31:5
|
LL | FIELD3
| ^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/union-const-eval-field.rs:31:5
|
LL | FIELD3
diff --git a/tests/ui/consts/const-float-bits-reject-conv.stderr b/tests/ui/consts/const-float-bits-reject-conv.stderr
index 7ad0225..1511dab 100644
--- a/tests/ui/consts/const-float-bits-reject-conv.stderr
+++ b/tests/ui/consts/const-float-bits-reject-conv.stderr
@@ -30,25 +30,25 @@
| ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:35:34
|
LL | const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:36:34
|
LL | const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:42:34
|
LL | const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:43:34
|
LL | const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
@@ -86,25 +86,25 @@
| ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:57:34
|
LL | const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:58:34
|
LL | const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:61:34
|
LL | const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-float-bits-reject-conv.rs:62:34
|
LL | const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
diff --git a/tests/ui/consts/const-len-underflow-separate-spans.next.stderr b/tests/ui/consts/const-len-underflow-separate-spans.next.stderr
index d9208d0..b7b5b64 100644
--- a/tests/ui/consts/const-len-underflow-separate-spans.next.stderr
+++ b/tests/ui/consts/const-len-underflow-separate-spans.next.stderr
@@ -4,7 +4,7 @@
LL | const LEN: usize = ONE - TWO;
| ^^^^^^^^^ attempt to compute `1_usize - 2_usize`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-len-underflow-separate-spans.rs:14:17
|
LL | let a: [i8; LEN] = unimplemented!();
diff --git a/tests/ui/consts/const-len-underflow-separate-spans.old.stderr b/tests/ui/consts/const-len-underflow-separate-spans.old.stderr
index d9208d0..b7b5b64 100644
--- a/tests/ui/consts/const-len-underflow-separate-spans.old.stderr
+++ b/tests/ui/consts/const-len-underflow-separate-spans.old.stderr
@@ -4,7 +4,7 @@
LL | const LEN: usize = ONE - TWO;
| ^^^^^^^^^ attempt to compute `1_usize - 2_usize`, which would overflow
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/const-len-underflow-separate-spans.rs:14:17
|
LL | let a: [i8; LEN] = unimplemented!();
diff --git a/tests/ui/consts/invalid-union.32bit.stderr b/tests/ui/consts/invalid-union.32bit.stderr
index 0dd18a5..b6cf060 100644
--- a/tests/ui/consts/invalid-union.32bit.stderr
+++ b/tests/ui/consts/invalid-union.32bit.stderr
@@ -9,13 +9,13 @@
╾─alloc7──╼ │ ╾──╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/invalid-union.rs:43:25
|
LL | let _: &'static _ = &C;
| ^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/invalid-union.rs:43:25
|
LL | let _: &'static _ = &C;
diff --git a/tests/ui/consts/invalid-union.64bit.stderr b/tests/ui/consts/invalid-union.64bit.stderr
index 07f36ee..e3a3ef6 100644
--- a/tests/ui/consts/invalid-union.64bit.stderr
+++ b/tests/ui/consts/invalid-union.64bit.stderr
@@ -9,13 +9,13 @@
╾───────alloc7────────╼ │ ╾──────╼
}
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/invalid-union.rs:43:25
|
LL | let _: &'static _ = &C;
| ^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/invalid-union.rs:43:25
|
LL | let _: &'static _ = &C;
diff --git a/tests/ui/consts/issue-36163.stderr b/tests/ui/consts/issue-36163.stderr
index 190b410..6fcfe3e 100644
--- a/tests/ui/consts/issue-36163.stderr
+++ b/tests/ui/consts/issue-36163.stderr
@@ -1,15 +1,25 @@
-error[E0391]: cycle detected when const-evaluating + checking `Foo::B::{constant#0}`
+error[E0391]: cycle detected when simplifying constant for the type system `Foo::B::{constant#0}`
--> $DIR/issue-36163.rs:4:9
|
LL | B = A,
| ^
|
+note: ...which requires const-evaluating + checking `Foo::B::{constant#0}`...
+ --> $DIR/issue-36163.rs:4:9
+ |
+LL | B = A,
+ | ^
+note: ...which requires simplifying constant for the type system `A`...
+ --> $DIR/issue-36163.rs:1:1
+ |
+LL | const A: isize = Foo::B as isize;
+ | ^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `A`...
--> $DIR/issue-36163.rs:1:18
|
LL | const A: isize = Foo::B as isize;
| ^^^^^^^^^^^^^^^
- = note: ...which again requires const-evaluating + checking `Foo::B::{constant#0}`, completing the cycle
+ = note: ...which again requires simplifying constant for the type system `Foo::B::{constant#0}`, completing the cycle
note: cycle used when simplifying constant for the type system `Foo::B::{constant#0}`
--> $DIR/issue-36163.rs:4:9
|
diff --git a/tests/ui/consts/miri_unleashed/assoc_const.stderr b/tests/ui/consts/miri_unleashed/assoc_const.stderr
index d97097d..274b1de 100644
--- a/tests/ui/consts/miri_unleashed/assoc_const.stderr
+++ b/tests/ui/consts/miri_unleashed/assoc_const.stderr
@@ -13,13 +13,13 @@
LL | const F: u32 = (U::X, 42).1;
| ^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/assoc_const.rs:29:13
|
LL | let y = <String as Bar<Vec<u32>, String>>::F;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/assoc_const.rs:29:13
|
LL | let y = <String as Bar<Vec<u32>, String>>::F;
diff --git a/tests/ui/consts/miri_unleashed/assoc_const_2.stderr b/tests/ui/consts/miri_unleashed/assoc_const_2.stderr
index ae7b03f..c8e4cab 100644
--- a/tests/ui/consts/miri_unleashed/assoc_const_2.stderr
+++ b/tests/ui/consts/miri_unleashed/assoc_const_2.stderr
@@ -4,13 +4,13 @@
LL | const F: u32 = 100 / U::X;
| ^^^^^^^^^^ attempt to divide `100_u32` by zero
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/assoc_const_2.rs:27:13
|
LL | let y = <String as Bar<String>>::F;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/assoc_const_2.rs:27:13
|
LL | let y = <String as Bar<String>>::F;
diff --git a/tests/ui/consts/uninhabited-const-issue-61744.stderr b/tests/ui/consts/uninhabited-const-issue-61744.stderr
index adefbf3..f48e6c4 100644
--- a/tests/ui/consts/uninhabited-const-issue-61744.stderr
+++ b/tests/ui/consts/uninhabited-const-issue-61744.stderr
@@ -645,13 +645,13 @@
LL | const CONSTANT: i32 = unsafe { fake_type() };
| ^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/uninhabited-const-issue-61744.rs:18:10
|
LL | dbg!(i32::CONSTANT);
| ^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/uninhabited-const-issue-61744.rs:18:10
|
LL | dbg!(i32::CONSTANT);
diff --git a/tests/ui/error-codes/E0054.stderr b/tests/ui/error-codes/E0054.stderr
index ea81f44..0a4adab 100644
--- a/tests/ui/error-codes/E0054.stderr
+++ b/tests/ui/error-codes/E0054.stderr
@@ -2,7 +2,12 @@
--> $DIR/E0054.rs:3:24
|
LL | let x_is_nonzero = x as bool;
- | ^^^^^^^^^ help: compare with zero instead: `x != 0`
+ | ^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let x_is_nonzero = x != 0;
+ | ~~~~
error: aborting due to previous error
diff --git a/tests/ui/error-festival.stderr b/tests/ui/error-festival.stderr
index 74a2bc8..9d75671 100644
--- a/tests/ui/error-festival.stderr
+++ b/tests/ui/error-festival.stderr
@@ -63,7 +63,12 @@
--> $DIR/error-festival.rs:33:24
|
LL | let x_is_nonzero = x as bool;
- | ^^^^^^^^^ help: compare with zero instead: `x != 0`
+ | ^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let x_is_nonzero = x != 0;
+ | ~~~~
error[E0606]: casting `&u8` as `u32` is invalid
--> $DIR/error-festival.rs:37:18
diff --git a/tests/ui/generic-associated-types/bugs/issue-88460.stderr b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
index a2047f1..b9e2c41 100644
--- a/tests/ui/generic-associated-types/bugs/issue-88460.stderr
+++ b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
@@ -7,6 +7,14 @@
| required by a bound introduced by this call
|
= help: the trait `Marker` is implemented for `()`
+note: this is a known limitation of the trait solver that will be lifted in the future
+ --> $DIR/issue-88460.rs:28:10
+ |
+LL | test(Foo);
+ | -----^^^-
+ | | |
+ | | the trait solver is unable to infer the generic types that should be inferred from this argument
+ | add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required by a bound in `test`
--> $DIR/issue-88460.rs:15:27
|
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
index b30dd36..2cc2bb2 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
@@ -8,6 +8,14 @@
|
= note: expected a closure with arguments `((),)`
found a closure with arguments `(<_ as ATC<'a>>::Type,)`
+note: this is a known limitation of the trait solver that will be lifted in the future
+ --> $DIR/issue-62529-3.rs:25:14
+ |
+LL | call(f, ());
+ | -----^-----
+ | | |
+ | | the trait solver is unable to infer the generic types that should be inferred from this argument
+ | add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required by a bound in `call`
--> $DIR/issue-62529-3.rs:9:36
|
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
index 5be33bc..55eaef7 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
@@ -7,6 +7,14 @@
| required by a bound introduced by this call
|
= help: the trait `IsCovariant<'a>` is implemented for `std::borrow::Cow<'a, T>`
+note: this is a known limitation of the trait solver that will be lifted in the future
+ --> $DIR/issue-90950.rs:50:12
+ |
+LL | upcast(y)
+ | -------^-
+ | | |
+ | | the trait solver is unable to infer the generic types that should be inferred from this argument
+ | add turbofish arguments to this call to specify the types manually, even if it's redundant
note: required by a bound in `upcast`
--> $DIR/issue-90950.rs:27:42
|
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
index 73388a7..081dbb6 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
@@ -4,6 +4,11 @@
LL | let _: () = weird_bound();
| ^^^^^^^^^^^ the trait `for<'a> Copy` is not implemented for `<_ as Trait<'a>>::Out`
|
+note: this is a known limitation of the trait solver that will be lifted in the future
+ --> $DIR/norm-before-method-resolution.rs:22:17
+ |
+LL | let _: () = weird_bound();
+ | ^^^^^^^^^^^ try adding turbofish arguments to this expression to specify the types manually, even if it's redundant
note: required by a bound in `weird_bound`
--> $DIR/norm-before-method-resolution.rs:18:40
|
diff --git a/tests/ui/issues/issue-17252.stderr b/tests/ui/issues/issue-17252.stderr
index daaf82e..32e20d7 100644
--- a/tests/ui/issues/issue-17252.stderr
+++ b/tests/ui/issues/issue-17252.stderr
@@ -1,10 +1,15 @@
-error[E0391]: cycle detected when const-evaluating + checking `FOO`
+error[E0391]: cycle detected when simplifying constant for the type system `FOO`
+ --> $DIR/issue-17252.rs:1:1
+ |
+LL | const FOO: usize = FOO;
+ | ^^^^^^^^^^^^^^^^
+ |
+note: ...which requires const-evaluating + checking `FOO`...
--> $DIR/issue-17252.rs:1:20
|
LL | const FOO: usize = FOO;
| ^^^
- |
- = note: ...which immediately requires const-evaluating + checking `FOO` again
+ = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle
note: cycle used when const-evaluating + checking `main::{constant#0}`
--> $DIR/issue-17252.rs:4:18
|
diff --git a/tests/ui/issues/issue-21763.stderr b/tests/ui/issues/issue-21763.stderr
index df50118..a887635 100644
--- a/tests/ui/issues/issue-21763.stderr
+++ b/tests/ui/issues/issue-21763.stderr
@@ -9,9 +9,6 @@
= note: required for `hashbrown::raw::RawTable<(Rc<()>, Rc<()>)>` to implement `Send`
note: required because it appears within the type `HashMap<Rc<()>, Rc<()>, RandomState>`
--> $HASHBROWN_SRC_LOCATION
- |
-LL | pub struct HashMap<K, V, S = DefaultHashBuilder, A: Allocator + Clone = Global> {
- | ^^^^^^^
note: required because it appears within the type `HashMap<Rc<()>, Rc<()>>`
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
note: required by a bound in `foo`
diff --git a/tests/ui/issues/issue-23302-1.stderr b/tests/ui/issues/issue-23302-1.stderr
index d807e24..d753bde 100644
--- a/tests/ui/issues/issue-23302-1.stderr
+++ b/tests/ui/issues/issue-23302-1.stderr
@@ -1,10 +1,15 @@
-error[E0391]: cycle detected when const-evaluating + checking `X::A::{constant#0}`
+error[E0391]: cycle detected when simplifying constant for the type system `X::A::{constant#0}`
--> $DIR/issue-23302-1.rs:4:9
|
LL | A = X::A as isize,
| ^^^^^^^^^^^^^
|
- = note: ...which immediately requires const-evaluating + checking `X::A::{constant#0}` again
+note: ...which requires const-evaluating + checking `X::A::{constant#0}`...
+ --> $DIR/issue-23302-1.rs:4:9
+ |
+LL | A = X::A as isize,
+ | ^^^^^^^^^^^^^
+ = note: ...which again requires simplifying constant for the type system `X::A::{constant#0}`, completing the cycle
note: cycle used when simplifying constant for the type system `X::A::{constant#0}`
--> $DIR/issue-23302-1.rs:4:9
|
diff --git a/tests/ui/issues/issue-23302-2.stderr b/tests/ui/issues/issue-23302-2.stderr
index 91b39db..b756ee1 100644
--- a/tests/ui/issues/issue-23302-2.stderr
+++ b/tests/ui/issues/issue-23302-2.stderr
@@ -1,10 +1,15 @@
-error[E0391]: cycle detected when const-evaluating + checking `Y::A::{constant#0}`
+error[E0391]: cycle detected when simplifying constant for the type system `Y::A::{constant#0}`
--> $DIR/issue-23302-2.rs:4:9
|
LL | A = Y::B as isize,
| ^^^^^^^^^^^^^
|
- = note: ...which immediately requires const-evaluating + checking `Y::A::{constant#0}` again
+note: ...which requires const-evaluating + checking `Y::A::{constant#0}`...
+ --> $DIR/issue-23302-2.rs:4:9
+ |
+LL | A = Y::B as isize,
+ | ^^^^^^^^^^^^^
+ = note: ...which again requires simplifying constant for the type system `Y::A::{constant#0}`, completing the cycle
note: cycle used when simplifying constant for the type system `Y::A::{constant#0}`
--> $DIR/issue-23302-2.rs:4:9
|
diff --git a/tests/ui/issues/issue-23302-3.stderr b/tests/ui/issues/issue-23302-3.stderr
index 6b708d8..6cdc945 100644
--- a/tests/ui/issues/issue-23302-3.stderr
+++ b/tests/ui/issues/issue-23302-3.stderr
@@ -1,15 +1,25 @@
-error[E0391]: cycle detected when const-evaluating + checking `A`
+error[E0391]: cycle detected when simplifying constant for the type system `A`
+ --> $DIR/issue-23302-3.rs:1:1
+ |
+LL | const A: i32 = B;
+ | ^^^^^^^^^^^^
+ |
+note: ...which requires const-evaluating + checking `A`...
--> $DIR/issue-23302-3.rs:1:16
|
LL | const A: i32 = B;
| ^
+note: ...which requires simplifying constant for the type system `B`...
+ --> $DIR/issue-23302-3.rs:3:1
|
+LL | const B: i32 = A;
+ | ^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `B`...
--> $DIR/issue-23302-3.rs:3:16
|
LL | const B: i32 = A;
| ^
- = note: ...which again requires const-evaluating + checking `A`, completing the cycle
+ = note: ...which again requires simplifying constant for the type system `A`, completing the cycle
note: cycle used when simplifying constant for the type system `A`
--> $DIR/issue-23302-3.rs:1:1
|
diff --git a/tests/ui/issues/issue-69602-type-err-during-codegen-ice.stderr b/tests/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
index ba385d8..6f9302b 100644
--- a/tests/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
+++ b/tests/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
@@ -13,7 +13,7 @@
LL | impl TraitB for B {
| ^^^^^^^^^^^^^^^^^ missing `MyA` in implementation
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-69602-type-err-during-codegen-ice.rs:21:17
|
LL | let _ = [0; B::VALUE];
diff --git a/tests/ui/limits/issue-55878.stderr b/tests/ui/limits/issue-55878.stderr
index f0c7210..93716c0 100644
--- a/tests/ui/limits/issue-55878.stderr
+++ b/tests/ui/limits/issue-55878.stderr
@@ -11,7 +11,7 @@
LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-55878.rs:7:26
|
LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
@@ -19,7 +19,7 @@
|
= note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
-note: erroneous constant used
+note: erroneous constant encountered
--> $DIR/issue-55878.rs:7:26
|
LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr
index 0cea607..d63cec4 100644
--- a/tests/ui/mismatched_types/cast-rfc0401.stderr
+++ b/tests/ui/mismatched_types/cast-rfc0401.stderr
@@ -86,7 +86,12 @@
--> $DIR/cast-rfc0401.rs:39:13
|
LL | let _ = 3_i32 as bool;
- | ^^^^^^^^^^^^^ help: compare with zero instead: `3_i32 != 0`
+ | ^^^^^^^^^^^^^
+ |
+help: compare with zero instead
+ |
+LL | let _ = 3_i32 != 0;
+ | ~~~~
error[E0054]: cannot cast `E` as `bool`
--> $DIR/cast-rfc0401.rs:40:13
diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs
index 506f1dc..84dd3f4 100644
--- a/tests/ui/repr/repr-transparent-non-exhaustive.rs
+++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs
@@ -93,4 +93,44 @@
//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
//~| WARN this was previously accepted by the compiler
+#[repr(transparent)]
+pub struct T17(NonExhaustive, Sized);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T18(NonExhaustive, NonExhaustive);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T19(NonExhaustive, Private);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T19Flipped(Private, NonExhaustive);
+//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T20(NonExhaustive);
+// Okay, since it's the only field.
+
+#[repr(transparent)]
+pub struct T21(NonExhaustive, InternalNonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T21Flipped(InternalNonExhaustive, NonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T22(NonExhaustive, InternalPrivate);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
+#[repr(transparent)]
+pub struct T22Flipped(InternalPrivate, NonExhaustive);
+// Okay, since there's only 1 foreign non-exhaustive type.
+
fn main() {}
diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr
index 16edf59..808b9bc 100644
--- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr
+++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr
@@ -123,5 +123,45 @@
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
= note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
-error: aborting due to 12 previous errors
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+ --> $DIR/repr-transparent-non-exhaustive.rs:97:16
+ |
+LL | pub struct T17(NonExhaustive, Sized);
+ | ^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+ = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+ --> $DIR/repr-transparent-non-exhaustive.rs:102:31
+ |
+LL | pub struct T18(NonExhaustive, NonExhaustive);
+ | ^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+ = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+ --> $DIR/repr-transparent-non-exhaustive.rs:107:31
+ |
+LL | pub struct T19(NonExhaustive, Private);
+ | ^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+ = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types
+ --> $DIR/repr-transparent-non-exhaustive.rs:112:32
+ |
+LL | pub struct T19Flipped(Private, NonExhaustive);
+ | ^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+ = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: aborting due to 16 previous errors
diff --git a/tests/ui/thir-print/thir-flat-const-variant.stdout b/tests/ui/thir-print/thir-flat-const-variant.stdout
index af7f2b6..7bddc92 100644
--- a/tests/ui/thir-print/thir-flat-const-variant.stdout
+++ b/tests/ui/thir-print/thir-flat-const-variant.stdout
@@ -1,11 +1,7 @@
DefId(0:8 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR1):
Thir {
body_type: Const(
- Adt(
- Foo,
- [
- ],
- ),
+ Foo,
),
arms: [],
blocks: [],
@@ -50,11 +46,7 @@
base: None,
},
),
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -68,11 +60,7 @@
),
value: e2,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -84,11 +72,7 @@
lint_level: Inherited,
value: e3,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -102,11 +86,7 @@
DefId(0:9 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR2):
Thir {
body_type: Const(
- Adt(
- Foo,
- [
- ],
- ),
+ Foo,
),
arms: [],
blocks: [],
@@ -151,11 +131,7 @@
base: None,
},
),
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -169,11 +145,7 @@
),
value: e2,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -185,11 +157,7 @@
lint_level: Inherited,
value: e3,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -203,11 +171,7 @@
DefId(0:10 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR3):
Thir {
body_type: Const(
- Adt(
- Foo,
- [
- ],
- ),
+ Foo,
),
arms: [],
blocks: [],
@@ -252,11 +216,7 @@
base: None,
},
),
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -270,11 +230,7 @@
),
value: e2,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -286,11 +242,7 @@
lint_level: Inherited,
value: e3,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -304,11 +256,7 @@
DefId(0:11 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR4):
Thir {
body_type: Const(
- Adt(
- Foo,
- [
- ],
- ),
+ Foo,
),
arms: [],
blocks: [],
@@ -353,11 +301,7 @@
base: None,
},
),
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -371,11 +315,7 @@
),
value: e2,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
@@ -387,11 +327,7 @@
lint_level: Inherited,
value: e3,
},
- ty: Adt(
- Foo,
- [
- ],
- ),
+ ty: Foo,
temp_lifetime: Some(
Node(3),
),
diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout
index 0e21b98..3fc130f 100644
--- a/tests/ui/thir-print/thir-tree-match.stdout
+++ b/tests/ui/thir-print/thir-tree-match.stdout
@@ -1,13 +1,13 @@
DefId(0:16 ~ thir_tree_match[fcf8]::has_match):
params: [
Param {
- ty: Adt(Foo, [])
+ ty: Foo
ty_span: Some($DIR/thir-tree-match.rs:15:19: 15:22 (#0))
self_kind: None
hir_id: Some(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).1))
param: Some(
Pat: {
- ty: Adt(Foo, [])
+ ty: Foo
span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0)
kind: PatKind {
Binding {
@@ -15,7 +15,7 @@
name: "foo"
mode: ByValue
var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2))
- ty: Adt(Foo, [])
+ ty: Foo
is_primary: true
subpattern: None
}
@@ -73,7 +73,7 @@
Match {
scrutinee:
Expr {
- ty: Adt(Foo, [])
+ ty: Foo
temp_lifetime: Some(Node(26))
span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
kind:
@@ -82,7 +82,7 @@
lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).4))
value:
Expr {
- ty: Adt(Foo, [])
+ ty: Foo
temp_lifetime: Some(Node(26))
span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
kind:
@@ -96,7 +96,7 @@
Arm {
pattern:
Pat: {
- ty: Adt(Foo, [])
+ ty: Foo
span: $DIR/thir-tree-match.rs:17:9: 17:32 (#0)
kind: PatKind {
Variant {
@@ -110,7 +110,7 @@
variant_index: 0
subpatterns: [
Pat: {
- ty: Adt(Bar, [])
+ ty: Bar
span: $DIR/thir-tree-match.rs:17:21: 17:31 (#0)
kind: PatKind {
Variant {
@@ -169,7 +169,7 @@
Arm {
pattern:
Pat: {
- ty: Adt(Foo, [])
+ ty: Foo
span: $DIR/thir-tree-match.rs:18:9: 18:23 (#0)
kind: PatKind {
Variant {
@@ -183,7 +183,7 @@
variant_index: 0
subpatterns: [
Pat: {
- ty: Adt(Bar, [])
+ ty: Bar
span: $DIR/thir-tree-match.rs:18:21: 18:22 (#0)
kind: PatKind {
Wild
@@ -232,7 +232,7 @@
Arm {
pattern:
Pat: {
- ty: Adt(Foo, [])
+ ty: Foo
span: $DIR/thir-tree-match.rs:19:9: 19:20 (#0)
kind: PatKind {
Variant {
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 0000000..baa22e1
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,29 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+#![feature(impl_trait_in_assoc_type)]
+
+use core::future::Future;
+
+trait Recur {
+ type Recur: Future<Output = ()>;
+
+ fn recur(self) -> Self::Recur;
+}
+
+async fn recur(t: impl Recur) {
+ t.recur().await;
+}
+
+impl Recur for () {
+ type Recur = impl Future<Output = ()>;
+
+ fn recur(self) -> Self::Recur {
+ async move { recur(self).await; }
+ }
+}
+
+fn main() {
+ recur(());
+}
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 0000000..0238694
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
new file mode 100644
index 0000000..1ccd1b0
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
@@ -0,0 +1,40 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as B>::Assoc == _`
+
+#![feature(rustc_attrs)]
+#![feature(impl_trait_in_assoc_type)]
+
+#[rustc_coinductive]
+trait A {
+ type Assoc;
+
+ fn test() -> Self::Assoc;
+}
+
+#[rustc_coinductive]
+trait B {
+ type Assoc;
+
+ fn test() -> Self::Assoc;
+}
+
+impl<T: A> B for T {
+ type Assoc = impl Sized;
+
+ fn test() -> <Self as B>::Assoc {
+ <T as A>::test()
+ }
+}
+
+fn main() {
+ <() as A>::test();
+}
+
+impl<T: B> A for T {
+ type Assoc = impl Sized;
+
+ fn test() -> <Self as A>::Assoc {
+ <T as B>::test()
+ }
+}
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
new file mode 100644
index 0000000..49c59f7
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as B>::Assoc == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/triagebot.toml b/triagebot.toml
index d9d523b..5b4e653 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -585,7 +585,7 @@
[assign]
warn_non_default_branch = true
contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
-users_on_vacation = ["jyn514"]
+users_on_vacation = ["jyn514", "jackh726"]
[assign.adhoc_groups]
compiler-team = [
@@ -621,6 +621,7 @@
]
infra-ci = [
"@Mark-Simulacrum",
+ "@Kobzol",
]
rustdoc = [
"@jsha",