Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-05-25 05:01:19 +00:00
commit da39cbec73
289 changed files with 3997 additions and 2428 deletions

View file

@ -234,8 +234,8 @@ jobs:
fi
exit ${STATUS}
env:
AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}
AWS_ACCESS_KEY_ID: ${{ (github.repository == 'rust-lang/rust' && secrets.CACHES_AWS_ACCESS_KEY_ID) || env.CACHES_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ (github.repository == 'rust-lang/rust' && secrets.CACHES_AWS_SECRET_ACCESS_KEY) || secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}
- name: create github artifacts
run: src/ci/scripts/create-doc-artifacts.sh
@ -257,8 +257,8 @@ jobs:
- name: upload artifacts to S3
run: src/ci/scripts/upload-artifacts.sh
env:
AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}
AWS_ACCESS_KEY_ID: ${{ (github.repository == 'rust-lang/rust' && secrets.ARTIFACTS_AWS_ACCESS_KEY_ID) || env.ARTIFACTS_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ (github.repository == 'rust-lang/rust' && secrets.ARTIFACTS_AWS_SECRET_ACCESS_KEY) || secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}
# Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy
# builders *should* have the AWS credentials available. Still, explicitly
# adding the condition is helpful as this way CI will not silently skip

View file

@ -5,7 +5,7 @@ and we appreciate all of them.
The best way to get started is by asking for help in the [#new
members](https://rust-lang.zulipchat.com/#narrow/stream/122652-new-members)
Zulip stream. We have lots of docs below of how to get started on your own, but
Zulip stream. We have a lot of documentation below on how to get started on your own, but
the Zulip stream is the best place to *ask* for help.
Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
@ -14,7 +14,7 @@ standard library in the [Standard library developers Guide][std-dev-guide], comm
## Making changes to subtrees and submodules
For submodules, changes need to be made against the repository corresponding the
For submodules, changes need to be made against the repository corresponding to the
submodule, and not the main `rust-lang/rust` repository.
For subtrees, prefer sending a PR against the subtree's repository if it does
@ -25,7 +25,7 @@ rustc-dev-guide change that does not accompany a compiler change).
The [rustc-dev-guide] is meant to help document how rustc the Rust compiler works,
as well as to help new contributors get involved in rustc development. It is recommended
to read and understand the [rustc-dev-guide] before making a contribution. This guide
that you read and understand the [rustc-dev-guide] before making a contribution. This guide
talks about the different bots in the Rust ecosystem, the Rust development tools,
bootstrapping, the compiler architecture, source code representation, and more.
@ -33,7 +33,7 @@ bootstrapping, the compiler architecture, source code representation, and more.
There are many ways you can get help when you're stuck. Rust has many platforms for this:
[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on
the [rust-zulip], but any of these platforms are a great way to seek help and even
the [rust-zulip], but any of these platforms are great ways to seek help and even
find a mentor! You can learn more about asking questions and getting help in the
[Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide].

View file

@ -183,7 +183,20 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
dependencies = [
"askama_derive",
"askama_derive 0.13.1",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
dependencies = [
"askama_derive 0.14.0",
"itoa",
"percent-encoding",
"serde",
@ -196,7 +209,24 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
dependencies = [
"askama_parser",
"askama_parser 0.13.0",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash 2.1.1",
"serde",
"serde_derive",
"syn 2.0.101",
]
[[package]]
name = "askama_derive"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
dependencies = [
"askama_parser 0.14.0",
"basic-toml",
"memchr",
"proc-macro2",
@ -219,6 +249,18 @@ dependencies = [
"winnow 0.7.10",
]
[[package]]
name = "askama_parser"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358"
dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.10",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@ -540,7 +582,7 @@ name = "clippy"
version = "0.1.89"
dependencies = [
"anstream",
"askama",
"askama 0.13.1",
"cargo_metadata 0.18.1",
"clippy_config",
"clippy_lints",
@ -1389,7 +1431,7 @@ name = "generate-copyright"
version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"askama 0.14.0",
"cargo_metadata 0.18.1",
"serde",
"serde_json",
@ -4622,7 +4664,7 @@ name = "rustdoc"
version = "0.0.0"
dependencies = [
"arrayvec",
"askama",
"askama 0.14.0",
"base64",
"expect-test",
"indexmap",

View file

@ -6,7 +6,7 @@ use rustc_ast::*;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
use rustc_session::config::FmtDebug;
use rustc_span::{Ident, Span, Symbol, kw, sym};
use rustc_span::{Ident, Span, Symbol, sym};
use super::LoweringContext;
@ -418,7 +418,7 @@ fn expand_format_args<'hir>(
&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(ctx.expr_str(fmt.span, kw::Empty))
Some(ctx.expr_str(fmt.span, sym::empty))
} else {
None
}

View file

@ -131,7 +131,8 @@ impl UniversalRegionRelations<'_> {
assert!(self.universal_regions.is_universal_region(fr0));
let mut external_parents = vec![];
let mut queue = vec![fr0];
let mut queue = vec![relation.minimal_scc_representative(fr0)];
// Keep expanding `fr` into its parents until we reach
// non-local regions.

View file

@ -154,7 +154,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): set link section.
}
if attrs.flags.contains(CodegenFnAttrFlags::USED)
if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|| attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
{
self.add_used_global(global.to_rvalue());

View file

@ -527,7 +527,7 @@ impl<'ll> CodegenCx<'ll, '_> {
base::set_variable_sanitizer_attrs(g, attrs);
if attrs.flags.contains(CodegenFnAttrFlags::USED) {
if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
@ -551,7 +551,7 @@ impl<'ll> CodegenCx<'ll, '_> {
}
if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED));
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER));
self.add_used_global(g);
}

View file

@ -1,5 +1,7 @@
codegen_ssa_L4Bender_exporting_symbols_unimplemented = exporting symbols not implemented yet for L4Bender
codegen_ssa_aarch64_softfloat_neon = enabling the `neon` target feature on the current target is unsound due to ABI issues
codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error}
codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work

View file

@ -128,7 +128,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
} else {
SymbolExportKind::Text
},
used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|| used,
};

View file

@ -189,7 +189,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
)
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER;
}
Some(_) => {
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
@ -220,7 +220,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|| tcx.sess.target.is_like_windows
|| tcx.sess.target.is_like_wasm);
codegen_fn_attrs.flags |= if is_like_elf {
CodegenFnAttrFlags::USED
CodegenFnAttrFlags::USED_COMPILER
} else {
CodegenFnAttrFlags::USED_LINKER
};
@ -299,6 +299,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
from_target_feature_attr(
tcx,
did,
attr,
rust_target_features,
&mut codegen_fn_attrs.target_features,

View file

@ -1316,3 +1316,7 @@ pub(crate) struct XcrunSdkPathWarning {
pub sdk_name: &'static str,
pub stderr: String,
}
#[derive(LintDiagnostic)]
#[diag(codegen_ssa_aarch64_softfloat_neon)]
pub(crate) struct Aarch64SoftfloatNeon;

View file

@ -1181,6 +1181,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(_, Some(llfn)) => llfn,
_ => span_bug!(span, "no instance or llfn for call"),
};
self.set_debug_loc(bx, mir::SourceInfo { span: fn_span, ..source_info });
helper.do_call(
self,
bx,

View file

@ -10,7 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{Instance, Ty};
use rustc_middle::{bug, mir, ty};
use rustc_session::config::DebugInfo;
use rustc_span::{BytePos, Span, Symbol, hygiene, kw};
use rustc_span::{BytePos, Span, Symbol, hygiene, sym};
use super::operand::{OperandRef, OperandValue};
use super::place::{PlaceRef, PlaceValue};
@ -283,7 +283,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// (after #67586 gets fixed).
None
} else {
let name = kw::Empty;
let name = sym::empty;
let decl = &self.mir.local_decls[local];
let dbg_var = if full_debug_info {
self.adjusted_span_and_dbg_scope(decl.source_info).map(
@ -318,7 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
None
} else {
Some(match whole_local_var.or(fallback_var.clone()) {
Some(var) if var.name != kw::Empty => var.name.to_string(),
Some(var) if var.name != sym::empty => var.name.to_string(),
_ => format!("{local:?}"),
})
};

View file

@ -8,6 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use rustc_target::target_features::{self, Stability};
@ -18,6 +19,7 @@ use crate::errors;
/// Enabled target features are added to `target_features`.
pub(crate) fn from_target_feature_attr(
tcx: TyCtxt<'_>,
did: LocalDefId,
attr: &hir::Attribute,
rust_target_features: &UnordMap<String, target_features::Stability>,
target_features: &mut Vec<TargetFeature>,
@ -92,11 +94,22 @@ pub(crate) fn from_target_feature_attr(
// generating code so "it's fine".
if !tcx.sess.opts.actually_rustdoc {
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature: name.as_str(),
reason: "this feature is incompatible with the target ABI",
});
// For "neon" specifically, we emit an FCW instead of a hard error.
// See <https://github.com/rust-lang/rust/issues/134375>.
if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" {
tcx.emit_node_span_lint(
AARCH64_SOFTFLOAT_NEON,
tcx.local_def_id_to_hir_id(did),
item.span(),
errors::Aarch64SoftfloatNeon,
);
} else {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature: name.as_str(),
reason: "this feature is incompatible with the target ABI",
});
}
}
}
target_features.push(TargetFeature { name, implied: name != feature_sym })

View file

@ -354,6 +354,20 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
.collect()
}
/// Given an element A, elements B with the lowest index such that `A R B`
/// and `B R A`, or `A` if no such element exists.
pub fn minimal_scc_representative(&self, a: T) -> T {
match self.index(a) {
Some(a_i) => self.with_closure(|closure| {
closure
.iter(a_i.0)
.find(|i| closure.contains(*i, a_i.0))
.map_or(a, |i| self.elements[i])
}),
None => a,
}
}
fn with_closure<OP, R>(&self, op: OP) -> R
where
OP: FnOnce(&BitMatrix<usize, usize>) -> R,

View file

@ -376,3 +376,44 @@ fn parent() {
let p = relation.postdom_parent(3);
assert_eq!(p, Some(0));
}
#[test]
fn minimal_scc_representative_1() {
// +---------+
// v |
// a -> c -> d -> e
// ^ ^
// | |
// b ---+
// "digraph { a -> c -> d -> e -> c; b -> d; b -> e; }",
let mut relation = TransitiveRelationBuilder::default();
relation.add("a", "c");
relation.add("c", "d");
relation.add("d", "e");
relation.add("e", "c");
relation.add("b", "d");
relation.add("b", "e");
let relation = relation.freeze();
assert_eq!(relation.minimal_scc_representative("a"), "a");
assert_eq!(relation.minimal_scc_representative("b"), "b");
assert_eq!(relation.minimal_scc_representative("c"), "c");
assert_eq!(relation.minimal_scc_representative("d"), "c");
assert_eq!(relation.minimal_scc_representative("e"), "c");
}
#[test]
fn minimal_scc_representative_2() {
// "digraph { a -> b; a -> a; b -> a; c -> c}",
let mut relation = TransitiveRelationBuilder::default();
relation.add("a", "b");
relation.add("b", "a");
relation.add("a", "a");
relation.add("c", "c");
let relation = relation.freeze();
assert_eq!(relation.minimal_scc_representative("a"), "a");
assert_eq!(relation.minimal_scc_representative("b"), "a");
assert_eq!(relation.minimal_scc_representative("c"), "c");
}

View file

@ -85,7 +85,7 @@ impl From<Ident> for LifetimeSyntax {
fn from(ident: Ident) -> Self {
let name = ident.name;
if name == kw::Empty {
if name == sym::empty {
unreachable!("A lifetime name should never be empty");
} else if name == kw::UnderscoreLifetime {
LifetimeSyntax::Anonymous

View file

@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the {$what} of a trait with uninferred generic parameters
.suggestion = use a fully qualified path with inferred lifetimes
hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes

View file

@ -44,7 +44,6 @@ use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument};
use crate::errors;
use crate::hir_ty_lowering::errors::assoc_tag_str;
use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
pub(crate) mod dump;
@ -89,6 +88,7 @@ pub(crate) fn provide(providers: &mut Providers) {
opaque_ty_origin,
rendered_precise_capturing_args,
const_param_default,
anon_const_kind,
..*providers
};
}
@ -444,13 +444,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident))
}
fn lower_assoc_shared(
fn lower_assoc_item_path(
&self,
span: Span,
item_def_id: DefId,
item_segment: &rustc_hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
assoc_tag: ty::AssocTag,
) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
let item_args = self.lowerer().lower_generic_args_of_assoc_item(
@ -525,7 +524,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
inferred_sugg,
bound,
mpart_sugg,
what: assoc_tag_str(assoc_tag),
what: self.tcx.def_descr(item_def_id),
}))
}
}
@ -1828,3 +1827,27 @@ fn const_param_default<'tcx>(
.lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id(), identity_args));
ty::EarlyBinder::bind(ct)
}
fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind {
let hir_id = tcx.local_def_id_to_hir_id(def);
let const_arg_id = tcx.parent_hir_id(hir_id);
match tcx.hir_node(const_arg_id) {
hir::Node::ConstArg(_) => {
if tcx.features().generic_const_exprs() {
ty::AnonConstKind::GCE
} else if tcx.features().min_generic_const_args() {
ty::AnonConstKind::MCG
} else if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Repeat(_, repeat_count),
..
}) = tcx.hir_node(tcx.parent_hir_id(const_arg_id))
&& repeat_count.hir_id == const_arg_id
{
ty::AnonConstKind::RepeatExprCount
} else {
ty::AnonConstKind::MCG
}
}
_ => ty::AnonConstKind::NonTypeSystem,
}
}

View file

@ -104,19 +104,27 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
}
if in_param_ty {
// We do not allow generic parameters in anon consts if we are inside
// of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
None
} else if tcx.features().generic_const_exprs() {
let parent_node = tcx.parent_hir_node(hir_id);
debug!(?parent_node);
if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node
&& constant.hir_id == hir_id
{
// enum variant discriminants are not allowed to use any kind of generics
None
} else if let Some(param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
match tcx.anon_const_kind(def_id) {
// Stable: anon consts are not able to use any generic parameters...
ty::AnonConstKind::MCG => None,
// we provide generics to repeat expr counts as a backwards compatibility hack. #76200
ty::AnonConstKind::RepeatExprCount => Some(parent_did),
// Even GCE anon const should not be allowed to use generic parameters as it would be
// trivially forward declared uses once desugared. E.g. `const N: [u8; ANON::<N>]`.
//
// We could potentially mirror the hack done for defaults of generic parameters but
// this case just doesn't come up much compared to `const N: u32 = ...`. Long term the
// hack for defaulted parameters should be removed eventually anyway.
ty::AnonConstKind::GCE if in_param_ty => None,
// GCE anon consts as a default for a generic parameter should have their provided generics
// "truncated" up to whatever generic parameter this anon const is within the default of.
//
// FIXME(generic_const_exprs): This only handles `const N: usize = /*defid*/` but not type
// parameter defaults, e.g. `T = Foo</*defid*/>`.
ty::AnonConstKind::GCE
if let Some(param_id) =
tcx.hir_opt_const_param_default_param_def_id(hir_id) =>
{
// If the def_id we are calling generics_of on is an anon ct default i.e:
//
@ -160,36 +168,17 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
has_self: generics.has_self,
has_late_bound_regions: generics.has_late_bound_regions,
};
} else {
// HACK(eddyb) this provides the correct generics when
// `feature(generic_const_expressions)` is enabled, so that const expressions
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
//
// Note that we do not supply the parent generics when using
// `min_const_generics`.
}
ty::AnonConstKind::GCE => Some(parent_did),
// Field defaults are allowed to use generic parameters, e.g. `field: u32 = /*defid: N + 1*/`
ty::AnonConstKind::NonTypeSystem
if matches!(tcx.parent_hir_node(hir_id), Node::TyPat(_) | Node::Field(_)) =>
{
Some(parent_did)
}
} else {
let parent_node = tcx.parent_hir_node(hir_id);
let parent_node = match parent_node {
Node::ConstArg(ca) => tcx.parent_hir_node(ca.hir_id),
_ => parent_node,
};
match parent_node {
// HACK(eddyb) this provides the correct generics for repeat
// expressions' count (i.e. `N` in `[x; N]`), and explicit
// `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
// as they shouldn't be able to cause query cycle errors.
Node::Expr(Expr { kind: ExprKind::Repeat(_, ct), .. })
if ct.anon_const_hir_id() == Some(hir_id) =>
{
Some(parent_did)
}
Node::TyPat(_) => Some(parent_did),
// Field default values inherit the ADT's generics.
Node::Field(_) => Some(parent_did),
_ => None,
}
// Default to no generic parameters for other kinds of anon consts
ty::AnonConstKind::NonTypeSystem => None,
}
}
Node::ConstBlock(_)

View file

@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::AmbigArg;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{AmbigArg, HirId};
use rustc_middle::bug;
use rustc_middle::ty::{
self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
@ -309,7 +309,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
false => "`?Sized`",
};
// There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
tcx.dcx().span_err(
self.dcx().span_err(
unbound.span,
format!(
"relaxing a default bound only does something for {}; \
@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Good error for `where Trait::method(..): Send`.
let Some(self_ty) = opt_self_ty else {
let guar = self.error_missing_qpath_self_ty(
let guar = self.report_missing_self_ty_for_resolved_path(
trait_def_id,
hir_ty.span,
item_segment,
@ -713,21 +713,45 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Err(guar) => Ty::new_error(tcx, guar),
}
}
hir::QPath::TypeRelative(qself, item_segment)
if item_segment.args.is_some_and(|args| {
hir::QPath::TypeRelative(hir_self_ty, segment)
if segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
match self
.resolve_type_relative_return_type_notation(
qself,
item_segment,
hir_ty.hir_id,
hir_ty.span,
)
.and_then(|(candidate, item_def_id)| {
self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span)
}) {
let self_ty = self.lower_ty(hir_self_ty);
let (item_def_id, bound) = match self.resolve_type_relative_path(
self_ty,
hir_self_ty,
ty::AssocTag::Fn,
segment,
hir_ty.hir_id,
hir_ty.span,
None,
) {
Ok(result) => result,
Err(guar) => return Ty::new_error(tcx, guar),
};
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
// which may happen via a higher-ranked where clause or supertrait.
// This is the same restrictions as associated types; even though we could
// support it, it just makes things a lot more difficult to support in
// `resolve_bound_vars`, since we'd need to introduce those as elided
// bound vars on the where clause too.
if bound.has_bound_vars() {
return Ty::new_error(
tcx,
self.dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span: hir_ty.span,
inferred_sugg: Some(hir_ty.span.with_hi(segment.ident.span.lo())),
bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder()),
mpart_sugg: None,
what: tcx.def_descr(item_def_id),
}),
);
}
match self.lower_return_type_notation_ty(bound, item_def_id, hir_ty.span) {
Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty),
Err(guar) => Ty::new_error(tcx, guar),
}
@ -736,99 +760,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Perform type-dependent lookup for a *method* for return type notation.
/// This generally mirrors `<dyn HirTyLowerer>::lower_assoc_path`.
fn resolve_type_relative_return_type_notation(
&self,
qself: &'tcx hir::Ty<'tcx>,
item_segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
) -> Result<(ty::PolyTraitRef<'tcx>, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
let qself_ty = self.lower_ty(qself);
let assoc_ident = item_segment.ident;
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
let bound = match (qself_ty.kind(), qself_res) {
(_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
// `Self` in an impl of a trait -- we have a concrete self type and a
// trait reference.
let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
// A cycle error occurred, most likely.
self.dcx().span_bug(span, "expected cycle error");
};
self.probe_single_bound_for_assoc_item(
|| {
traits::supertraits(
tcx,
ty::Binder::dummy(trait_ref.instantiate_identity()),
)
},
AssocItemQSelf::SelfTyAlias,
ty::AssocTag::Fn,
assoc_ident,
span,
None,
)?
}
(
&ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(),
qself.span,
ty::AssocTag::Fn,
assoc_ident,
span,
)?,
_ => {
if let Err(reported) = qself_ty.error_reported() {
return Err(reported);
} else {
// FIXME(return_type_notation): Provide some structured suggestion here.
let err = struct_span_code_err!(
self.dcx(),
span,
E0223,
"ambiguous associated function"
);
return Err(err.emit());
}
}
};
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
// which may happen via a higher-ranked where clause or supertrait.
// This is the same restrictions as associated types; even though we could
// support it, it just makes things a lot more difficult to support in
// `resolve_bound_vars`, since we'd need to introduce those as elided
// bound vars on the where clause too.
if bound.has_bound_vars() {
return Err(self.tcx().dcx().emit_err(
errors::AssociatedItemTraitUninferredGenericParams {
span,
inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())),
bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),),
mpart_sugg: None,
what: "function",
},
));
}
let trait_def_id = bound.def_id();
let assoc_ty = self
.probe_assoc_item(assoc_ident, ty::AssocTag::Fn, qpath_hir_id, span, trait_def_id)
.expect("failed to find associated type");
Ok((bound, assoc_ty.def_id))
}
/// Do the common parts of lowering an RTN type. This involves extending the
/// candidate binder to include all of the early- and late-bound vars that are
/// defined on the function itself, and constructing a projection to the RPITIT

View file

@ -35,7 +35,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
_ => tcx.hir_span(hir_id),
};
struct_span_code_err!(
tcx.dcx(),
dcx,
span,
E0781,
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"

View file

@ -78,15 +78,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// We don't support empty trait objects.
if regular_traits.is_empty() && auto_traits.is_empty() {
let guar = self.report_trait_object_with_no_traits_error(
span,
user_written_bounds.iter().copied(),
);
let guar =
self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied());
return Ty::new_error(tcx, guar);
}
// We don't support >1 principal
if regular_traits.len() > 1 {
let guar = self.report_trait_object_addition_traits_error(&regular_traits);
let guar = self.report_trait_object_addition_traits(&regular_traits);
return Ty::new_error(tcx, guar);
}
// Don't create a dyn trait if we have errors in the principal.
@ -132,7 +130,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if references_self {
// With trait alias and type alias combined, type resolver
// may not be able to catch all illegal `Self` usages (issue 139082)
let guar = tcx.dcx().emit_err(SelfInTypeAlias { span });
let guar = self.dcx().emit_err(SelfInTypeAlias { span });
b.term = replace_dummy_self_with_error(tcx, b.term, guar);
}
}
@ -360,7 +358,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
self.complain_about_missing_type_params(
self.report_missing_type_params(
missing_type_params,
trait_ref.def_id,
span,

View file

@ -3,11 +3,12 @@ use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, ErrorGuaranteed, MultiSpan, listify, pluralize, struct_span_code_err,
Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, listify, pluralize,
struct_span_code_err,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId};
use rustc_middle::bug;
use rustc_middle::ty::fast_reject::{TreatParams, simplify_type};
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@ -23,6 +24,7 @@ use rustc_trait_selection::traits::{
FulfillmentError, dyn_compatibility_violations_for_assoc_item,
};
use smallvec::SmallVec;
use tracing::debug;
use crate::errors::{
self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
@ -34,7 +36,7 @@ use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
/// the type parameter's name as a placeholder.
pub(crate) fn complain_about_missing_type_params(
pub(crate) fn report_missing_type_params(
&self,
missing_type_params: Vec<Symbol>,
def_id: DefId,
@ -56,7 +58,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
/// an error and attempt to build a reasonable structured suggestion.
pub(crate) fn complain_about_internal_fn_trait(
pub(crate) fn report_internal_fn_trait(
&self,
span: Span,
trait_def_id: DefId,
@ -112,7 +114,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
pub(super) fn complain_about_assoc_item_not_found<I>(
pub(super) fn report_unresolved_assoc_item<I>(
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
@ -132,7 +134,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.filter_by_name_unhygienic(assoc_ident.name)
.find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id()))
}) {
return self.complain_about_assoc_kind_mismatch(
return self.report_assoc_kind_mismatch(
assoc_item,
assoc_tag,
assoc_ident,
@ -331,7 +333,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.dcx().emit_err(err)
}
fn complain_about_assoc_kind_mismatch(
fn report_assoc_kind_mismatch(
&self,
assoc_item: &ty::AssocItem,
assoc_tag: ty::AssocTag,
@ -396,12 +398,177 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
}
pub(super) fn report_ambiguous_assoc(
pub(crate) fn report_missing_self_ty_for_resolved_path(
&self,
trait_def_id: DefId,
span: Span,
item_segment: &hir::PathSegment<'tcx>,
assoc_tag: ty::AssocTag,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
self.report_ambiguous_assoc_item_path(
span,
&type_names,
&[path_str],
item_segment.ident,
assoc_tag,
)
}
pub(super) fn report_unresolved_type_relative_path(
&self,
self_ty: Ty<'tcx>,
hir_self_ty: &hir::Ty<'_>,
assoc_tag: ty::AssocTag,
ident: Ident,
qpath_hir_id: HirId,
span: Span,
variant_def_id: Option<DefId>,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let kind_str = assoc_tag_str(assoc_tag);
if variant_def_id.is_some() {
// Variant in type position
let msg = format!("expected {kind_str}, found variant `{ident}`");
self.dcx().span_err(span, msg)
} else if self_ty.is_enum() {
let mut err = self.dcx().create_err(errors::NoVariantNamed {
span: ident.span,
ident,
ty: self_ty,
});
let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT");
if let Some(variant_name) = find_best_match_for_name(
&adt_def.variants().iter().map(|variant| variant.name).collect::<Vec<Symbol>>(),
ident.name,
None,
) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == variant_name)
{
let mut suggestion = vec![(ident.span, variant_name.to_string())];
if let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(expr), .. })
| hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id)
&& let hir::ExprKind::Struct(..) = expr.kind
{
match variant.ctor {
None => {
// struct
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
if variant.fields.is_empty() {
format!("{variant_name} {{}}")
} else {
format!(
"{variant_name} {{ {} }}",
variant
.fields
.iter()
.map(|f| format!("{}: /* value */", f.name))
.collect::<Vec<_>>()
.join(", ")
)
},
)];
}
Some((hir::def::CtorKind::Fn, def_id)) => {
// tuple
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
let inputs = fn_sig.inputs().skip_binder();
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
format!(
"{variant_name}({})",
inputs
.iter()
.map(|i| format!("/* {i} */"))
.collect::<Vec<_>>()
.join(", ")
),
)];
}
Some((hir::def::CtorKind::Const, _)) => {
// unit
suggestion = vec![(
ident.span.with_hi(expr.span.hi()),
variant_name.to_string(),
)];
}
}
}
err.multipart_suggestion_verbose(
"there is a variant with a similar name",
suggestion,
Applicability::HasPlaceholders,
);
} else {
err.span_label(ident.span, format!("variant not found in `{self_ty}`"));
}
if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
err.span_label(sp, format!("variant `{ident}` not found here"));
}
err.emit()
} else if let Err(reported) = self_ty.error_reported() {
reported
} else {
match self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty) {
Ok(()) => {}
Err(reported) => return reported,
}
let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident);
self.report_ambiguous_assoc_item_path(
span,
&[self_ty.to_string()],
&traits,
ident,
assoc_tag,
)
}
}
pub(super) fn report_ambiguous_assoc_item_path(
&self,
span: Span,
types: &[String],
traits: &[String],
name: Symbol,
ident: Ident,
assoc_tag: ty::AssocTag,
) -> ErrorGuaranteed {
let kind_str = assoc_tag_str(assoc_tag);
@ -421,6 +588,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Applicability::MachineApplicable,
);
} else {
let sugg_sp = span.until(ident.span);
let mut types = types.to_vec();
types.sort();
let mut traits = traits.to_vec();
@ -428,76 +597,79 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
match (&types[..], &traits[..]) {
([], []) => {
err.span_suggestion_verbose(
span,
sugg_sp,
format!(
"if there were a type named `Type` that implements a trait named \
`Trait` with associated {kind_str} `{name}`, you could use the \
`Trait` with associated {kind_str} `{ident}`, you could use the \
fully-qualified path",
),
format!("<Type as Trait>::{name}"),
"<Type as Trait>::",
Applicability::HasPlaceholders,
);
}
([], [trait_str]) => {
err.span_suggestion_verbose(
span,
sugg_sp,
format!(
"if there were a type named `Example` that implemented `{trait_str}`, \
you could use the fully-qualified path",
),
format!("<Example as {trait_str}>::{name}"),
format!("<Example as {trait_str}>::"),
Applicability::HasPlaceholders,
);
}
([], traits) => {
err.span_suggestions(
span,
err.span_suggestions_with_style(
sugg_sp,
format!(
"if there were a type named `Example` that implemented one of the \
traits with associated {kind_str} `{name}`, you could use the \
traits with associated {kind_str} `{ident}`, you could use the \
fully-qualified path",
),
traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")),
traits.iter().map(|trait_str| format!("<Example as {trait_str}>::")),
Applicability::HasPlaceholders,
SuggestionStyle::ShowAlways,
);
}
([type_str], []) => {
err.span_suggestion_verbose(
span,
sugg_sp,
format!(
"if there were a trait named `Example` with associated {kind_str} `{name}` \
"if there were a trait named `Example` with associated {kind_str} `{ident}` \
implemented for `{type_str}`, you could use the fully-qualified path",
),
format!("<{type_str} as Example>::{name}"),
format!("<{type_str} as Example>::"),
Applicability::HasPlaceholders,
);
}
(types, []) => {
err.span_suggestions(
span,
err.span_suggestions_with_style(
sugg_sp,
format!(
"if there were a trait named `Example` with associated {kind_str} `{name}` \
"if there were a trait named `Example` with associated {kind_str} `{ident}` \
implemented for one of the types, you could use the fully-qualified \
path",
),
types
.into_iter()
.map(|type_str| format!("<{type_str} as Example>::{name}")),
.map(|type_str| format!("<{type_str} as Example>::")),
Applicability::HasPlaceholders,
SuggestionStyle::ShowAlways,
);
}
(types, traits) => {
let mut suggestions = vec![];
for type_str in types {
for trait_str in traits {
suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
suggestions.push(format!("<{type_str} as {trait_str}>::"));
}
}
err.span_suggestions(
span,
err.span_suggestions_with_style(
sugg_sp,
"use fully-qualified syntax",
suggestions,
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
}
}
@ -505,7 +677,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub(crate) fn complain_about_ambiguous_inherent_assoc(
pub(crate) fn report_ambiguous_inherent_assoc_item(
&self,
name: Ident,
candidates: Vec<DefId>,
@ -518,12 +690,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
"multiple applicable items in scope"
);
err.span_label(name.span, format!("multiple `{name}` found"));
self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span);
self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span);
err.emit()
}
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
fn note_ambiguous_inherent_assoc_ty(
fn note_ambiguous_inherent_assoc_item(
&self,
err: &mut Diag<'_>,
candidates: Vec<DefId>,
@ -566,7 +738,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
// FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
pub(crate) fn complain_about_inherent_assoc_not_found(
pub(crate) fn report_unresolved_inherent_assoc_item(
&self,
name: Ident,
self_ty: Ty<'tcx>,
@ -1046,7 +1218,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
pub fn report_prohibit_generics_error<'a>(
pub fn report_prohibited_generic_args<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
args_visitors: impl Iterator<Item = &'a hir::GenericArg<'a>> + Clone,
@ -1128,7 +1300,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub fn report_trait_object_addition_traits_error(
pub fn report_trait_object_addition_traits(
&self,
regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>,
) -> ErrorGuaranteed {
@ -1171,7 +1343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.emit()
}
pub fn report_trait_object_with_no_traits_error(
pub fn report_trait_object_with_no_traits(
&self,
span: Span,
user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,

View file

@ -103,7 +103,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// In case there is an associated type with the same name
// Add the suggestion to this error
if let Some(mut sugg) =
tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion)
self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion)
&& let Suggestions::Enabled(ref mut s1) = diag.suggestions
&& let Suggestions::Enabled(ref mut s2) = sugg.suggestions
{

View file

@ -38,22 +38,20 @@ use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
use rustc_middle::ty::{
self, AssocTag, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty,
TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions,
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
TypeVisitableExt, TypingMode, Upcast, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, instrument};
use self::errors::assoc_tag_str;
use crate::check::check_abi_fn_ptr;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@ -150,7 +148,7 @@ pub trait HirTyLowerer<'tcx> {
assoc_ident: Ident,
) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
/// Lower an associated type/const (from a trait) to a projection.
/// Lower a path to an associated item (of a trait) to a projection.
///
/// This method has to be defined by the concrete lowering context because
/// dealing with higher-ranked trait references depends on its capabilities:
@ -162,13 +160,12 @@ pub trait HirTyLowerer<'tcx> {
///
/// The canonical example of this is associated type `T::P` where `T` is a type
/// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`.
fn lower_assoc_shared(
fn lower_assoc_item_path(
&self,
span: Span,
item_def_id: DefId,
item_segment: &hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
assoc_tag: ty::AssocTag,
) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>;
fn lower_fn_sig(
@ -245,39 +242,46 @@ pub enum FeedConstTy<'a, 'tcx> {
}
#[derive(Debug, Clone, Copy)]
enum LowerAssocMode {
Type { permit_variants: bool },
enum LowerTypeRelativePathMode {
Type(PermitVariants),
Const,
}
impl LowerAssocMode {
impl LowerTypeRelativePathMode {
fn assoc_tag(self) -> ty::AssocTag {
match self {
LowerAssocMode::Type { .. } => ty::AssocTag::Type,
LowerAssocMode::Const => ty::AssocTag::Const,
Self::Type(_) => ty::AssocTag::Type,
Self::Const => ty::AssocTag::Const,
}
}
fn def_kind(self) -> DefKind {
match self {
LowerAssocMode::Type { .. } => DefKind::AssocTy,
LowerAssocMode::Const => DefKind::AssocConst,
Self::Type(_) => DefKind::AssocTy,
Self::Const => DefKind::AssocConst,
}
}
fn permit_variants(self) -> bool {
fn permit_variants(self) -> PermitVariants {
match self {
LowerAssocMode::Type { permit_variants } => permit_variants,
Self::Type(permit_variants) => permit_variants,
// FIXME(mgca): Support paths like `Option::<T>::None` or `Option::<T>::Some` which
// resolve to const ctors/fn items respectively.
LowerAssocMode::Const => false,
Self::Const => PermitVariants::No,
}
}
}
/// Whether to permit a path to resolve to an enum variant.
#[derive(Debug, Clone, Copy)]
enum LoweredAssoc<'tcx> {
Term(DefId, GenericArgsRef<'tcx>),
pub enum PermitVariants {
Yes,
No,
}
#[derive(Debug, Clone, Copy)]
enum TypeRelativePath<'tcx> {
AssocItem(DefId, GenericArgsRef<'tcx>),
Variant { adt: Ty<'tcx>, variant_did: DefId },
}
@ -745,7 +749,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_ref.path.segments.split_last().unwrap().1.iter(),
GenericsArgsErrExtend::None,
);
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
self.report_internal_fn_trait(span, trait_def_id, trait_segment, false);
let (generic_args, arg_count) = self.lower_generic_args_of_path(
trait_ref.path.span,
@ -920,7 +924,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_segment: &hir::PathSegment<'tcx>,
is_impl: bool,
) -> ty::TraitRef<'tcx> {
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
let (generic_args, _) =
self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty));
@ -933,7 +937,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn probe_trait_that_defines_assoc_item(
&self,
trait_def_id: DefId,
assoc_tag: AssocTag,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
) -> bool {
self.tcx()
@ -976,7 +980,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&self,
ty_param_def_id: LocalDefId,
ty_param_span: Span,
assoc_tag: AssocTag,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@ -1011,7 +1015,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&self,
all_candidates: impl Fn() -> I,
qself: AssocItemQSelf,
assoc_tag: AssocTag,
assoc_tag: ty::AssocTag,
assoc_ident: Ident,
span: Span,
constraint: Option<&hir::AssocItemConstraint<'tcx>>,
@ -1026,15 +1030,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
});
let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found(
return Err(self.report_unresolved_assoc_item(
all_candidates,
qself,
assoc_tag,
assoc_ident,
span,
constraint,
);
return Err(reported);
));
};
debug!(?bound);
@ -1127,7 +1130,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ok(bound)
}
/// Lower a [type-relative] path referring to an associated type or to an enum variant.
/// Lower a [type-relative](hir::QPath::TypeRelative) path in type position to a type.
///
/// If the path refers to an enum variant and `permit_variants` holds,
/// the returned type is simply the provided self type `qself_ty`.
@ -1148,61 +1151,63 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// described in the previous paragraph and their modeling of projections would likely be
/// very similar in nature.
///
/// [type-relative]: hir::QPath::TypeRelative
/// [#22519]: https://github.com/rust-lang/rust/issues/22519
/// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403
//
// NOTE: When this function starts resolving `Trait::AssocTy` successfully
// it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
#[instrument(level = "debug", skip_all, ret)]
pub fn lower_assoc_path_ty(
pub fn lower_type_relative_ty_path(
&self,
hir_ref_id: HirId,
self_ty: Ty<'tcx>,
hir_self_ty: &'tcx hir::Ty<'tcx>,
segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
qself_ty: Ty<'tcx>,
qself: &'tcx hir::Ty<'tcx>,
assoc_segment: &'tcx hir::PathSegment<'tcx>,
permit_variants: bool,
permit_variants: PermitVariants,
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
match self.lower_assoc_path_shared(
hir_ref_id,
match self.lower_type_relative_path(
self_ty,
hir_self_ty,
segment,
qpath_hir_id,
span,
qself_ty,
qself,
assoc_segment,
LowerAssocMode::Type { permit_variants },
LowerTypeRelativePathMode::Type(permit_variants),
)? {
LoweredAssoc::Term(def_id, args) => {
TypeRelativePath::AssocItem(def_id, args) => {
let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args);
let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty);
Ok((ty, tcx.def_kind(def_id), def_id))
}
LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)),
TypeRelativePath::Variant { adt, variant_did } => {
Ok((adt, DefKind::Variant, variant_did))
}
}
}
/// Lower a [type-relative][hir::QPath::TypeRelative] path to a (type-level) constant.
#[instrument(level = "debug", skip_all, ret)]
fn lower_assoc_path_const(
fn lower_type_relative_const_path(
&self,
hir_ref_id: HirId,
self_ty: Ty<'tcx>,
hir_self_ty: &'tcx hir::Ty<'tcx>,
segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
qself_ty: Ty<'tcx>,
qself: &'tcx hir::Ty<'tcx>,
assoc_segment: &'tcx hir::PathSegment<'tcx>,
) -> Result<Const<'tcx>, ErrorGuaranteed> {
let tcx = self.tcx();
let (def_id, args) = match self.lower_assoc_path_shared(
hir_ref_id,
let (def_id, args) = match self.lower_type_relative_path(
self_ty,
hir_self_ty,
segment,
qpath_hir_id,
span,
qself_ty,
qself,
assoc_segment,
LowerAssocMode::Const,
LowerTypeRelativePathMode::Const,
)? {
LoweredAssoc::Term(def_id, args) => {
TypeRelativePath::AssocItem(def_id, args) => {
if !tcx.associated_item(def_id).is_type_const_capable(tcx) {
let mut err = tcx.dcx().struct_span_err(
let mut err = self.dcx().struct_span_err(
span,
"use of trait associated const without `#[type_const]`",
);
@ -1213,75 +1218,134 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
// FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
// not just const ctors
LoweredAssoc::Variant { .. } => {
TypeRelativePath::Variant { .. } => {
span_bug!(span, "unexpected variant res for type associated const path")
}
};
Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args)))
}
/// Lower a [type-relative][hir::QPath::TypeRelative] (and type-level) path.
#[instrument(level = "debug", skip_all, ret)]
fn lower_assoc_path_shared(
fn lower_type_relative_path(
&self,
hir_ref_id: HirId,
self_ty: Ty<'tcx>,
hir_self_ty: &'tcx hir::Ty<'tcx>,
segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
qself_ty: Ty<'tcx>,
qself: &'tcx hir::Ty<'tcx>,
assoc_segment: &'tcx hir::PathSegment<'tcx>,
mode: LowerAssocMode,
) -> Result<LoweredAssoc<'tcx>, ErrorGuaranteed> {
debug!(%qself_ty, ?assoc_segment.ident);
mode: LowerTypeRelativePathMode,
) -> Result<TypeRelativePath<'tcx>, ErrorGuaranteed> {
debug!(%self_ty, ?segment.ident);
let tcx = self.tcx();
let assoc_ident = assoc_segment.ident;
// Check if we have an enum variant or an inherent associated type.
let mut variant_resolution = None;
if let Some(adt_def) = self.probe_adt(span, qself_ty) {
let mut variant_def_id = None;
if let Some(adt_def) = self.probe_adt(span, self_ty) {
if adt_def.is_enum() {
let variant_def = adt_def
.variants()
.iter()
.find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
.find(|vd| tcx.hygienic_eq(segment.ident, vd.ident(tcx), adt_def.did()));
if let Some(variant_def) = variant_def {
if mode.permit_variants() {
tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
if let PermitVariants::Yes = mode.permit_variants() {
tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
let _ = self.prohibit_generic_args(
slice::from_ref(assoc_segment).iter(),
GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
slice::from_ref(segment).iter(),
GenericsArgsErrExtend::EnumVariant {
qself: hir_self_ty,
assoc_segment: segment,
adt_def,
},
);
return Ok(LoweredAssoc::Variant {
adt: qself_ty,
return Ok(TypeRelativePath::Variant {
adt: self_ty,
variant_did: variant_def.def_id,
});
} else {
variant_resolution = Some(variant_def.def_id);
variant_def_id = Some(variant_def.def_id);
}
}
}
// FIXME(inherent_associated_types, #106719): Support self types other than ADTs.
if let Some((did, args)) = self.probe_inherent_assoc_shared(
assoc_segment,
if let Some((did, args)) = self.probe_inherent_assoc_item(
segment,
adt_def.did(),
qself_ty,
hir_ref_id,
self_ty,
qpath_hir_id,
span,
mode.assoc_tag(),
)? {
return Ok(LoweredAssoc::Term(did, args));
return Ok(TypeRelativePath::AssocItem(did, args));
}
}
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
let (item_def_id, bound) = self.resolve_type_relative_path(
self_ty,
hir_self_ty,
mode.assoc_tag(),
segment,
qpath_hir_id,
span,
variant_def_id,
)?;
let (item_def_id, args) = self.lower_assoc_item_path(span, item_def_id, segment, bound)?;
if let Some(variant_def_id) = variant_def_id {
tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, |lint| {
lint.primary_message("ambiguous associated item");
let mut could_refer_to = |kind: DefKind, def_id, also| {
let note_msg = format!(
"`{}` could{} refer to the {} defined here",
segment.ident,
also,
tcx.def_kind_descr(kind, def_id)
);
lint.span_note(tcx.def_span(def_id), note_msg);
};
could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(mode.def_kind(), item_def_id, " also");
lint.span_suggestion(
span,
"use fully-qualified syntax",
format!(
"<{} as {}>::{}",
self_ty,
tcx.item_name(bound.def_id()),
segment.ident
),
Applicability::MachineApplicable,
);
});
}
Ok(TypeRelativePath::AssocItem(item_def_id, args))
}
/// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path.
fn resolve_type_relative_path(
&self,
self_ty: Ty<'tcx>,
hir_self_ty: &'tcx hir::Ty<'tcx>,
assoc_tag: ty::AssocTag,
segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
variant_def_id: Option<DefId>,
) -> Result<(DefId, ty::PolyTraitRef<'tcx>), ErrorGuaranteed> {
let tcx = self.tcx();
let self_ty_res = match hir_self_ty.kind {
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => path.res,
_ => Res::Err,
};
// Find the type of the associated item, and the trait where the associated
// item is declared.
let bound = match (qself_ty.kind(), qself_res) {
// Find the type of the assoc item, and the trait where the associated item is declared.
let bound = match (self_ty.kind(), self_ty_res) {
(_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
// `Self` in an impl of a trait -- we have a concrete self type and a
// trait reference.
@ -1292,14 +1356,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.probe_single_bound_for_assoc_item(
|| {
traits::supertraits(
tcx,
ty::Binder::dummy(trait_ref.instantiate_identity()),
)
let trait_ref = ty::Binder::dummy(trait_ref.instantiate_identity());
traits::supertraits(tcx, trait_ref)
},
AssocItemQSelf::SelfTyAlias,
mode.assoc_tag(),
assoc_ident,
assoc_tag,
segment.ident,
span,
None,
)?
@ -1309,167 +1371,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(),
qself.span,
mode.assoc_tag(),
assoc_ident,
hir_self_ty.span,
assoc_tag,
segment.ident,
span,
)?,
_ => {
let kind_str = assoc_tag_str(mode.assoc_tag());
let reported = if variant_resolution.is_some() {
// Variant in type position
let msg = format!("expected {kind_str}, found variant `{assoc_ident}`");
self.dcx().span_err(span, msg)
} else if qself_ty.is_enum() {
let mut err = self.dcx().create_err(NoVariantNamed {
span: assoc_ident.span,
ident: assoc_ident,
ty: qself_ty,
});
let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
if let Some(variant_name) = find_best_match_for_name(
&adt_def
.variants()
.iter()
.map(|variant| variant.name)
.collect::<Vec<Symbol>>(),
assoc_ident.name,
None,
) && let Some(variant) =
adt_def.variants().iter().find(|s| s.name == variant_name)
{
let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())];
if let hir::Node::Stmt(&hir::Stmt {
kind: hir::StmtKind::Semi(expr), ..
})
| hir::Node::Expr(expr) = tcx.parent_hir_node(hir_ref_id)
&& let hir::ExprKind::Struct(..) = expr.kind
{
match variant.ctor {
None => {
// struct
suggestion = vec![(
assoc_ident.span.with_hi(expr.span.hi()),
if variant.fields.is_empty() {
format!("{variant_name} {{}}")
} else {
format!(
"{variant_name} {{ {} }}",
variant
.fields
.iter()
.map(|f| format!("{}: /* value */", f.name))
.collect::<Vec<_>>()
.join(", ")
)
},
)];
}
Some((hir::def::CtorKind::Fn, def_id)) => {
// tuple
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
let inputs = fn_sig.inputs().skip_binder();
suggestion = vec![(
assoc_ident.span.with_hi(expr.span.hi()),
format!(
"{variant_name}({})",
inputs
.iter()
.map(|i| format!("/* {i} */"))
.collect::<Vec<_>>()
.join(", ")
),
)];
}
Some((hir::def::CtorKind::Const, _)) => {
// unit
suggestion = vec![(
assoc_ident.span.with_hi(expr.span.hi()),
variant_name.to_string(),
)];
}
}
}
err.multipart_suggestion_verbose(
"there is a variant with a similar name",
suggestion,
Applicability::HasPlaceholders,
);
} else {
err.span_label(
assoc_ident.span,
format!("variant not found in `{qself_ty}`"),
);
}
if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
err.span_label(sp, format!("variant `{assoc_ident}` not found here"));
}
err.emit()
} else if let Err(reported) = qself_ty.error_reported() {
reported
} else {
self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
let traits: Vec<_> =
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
// Don't print `ty::Error` to the user.
self.report_ambiguous_assoc(
span,
&[qself_ty.to_string()],
&traits,
assoc_ident.name,
mode.assoc_tag(),
)
};
return Err(reported);
return Err(self.report_unresolved_type_relative_path(
self_ty,
hir_self_ty,
assoc_tag,
segment.ident,
qpath_hir_id,
span,
variant_def_id,
));
}
};
let trait_did = bound.def_id();
let assoc_item = self
.probe_assoc_item(assoc_ident, mode.assoc_tag(), hir_ref_id, span, trait_did)
.probe_assoc_item(segment.ident, assoc_tag, qpath_hir_id, span, bound.def_id())
.expect("failed to find associated item");
let (def_id, args) = self.lower_assoc_shared(
span,
assoc_item.def_id,
assoc_segment,
bound,
mode.assoc_tag(),
)?;
let result = LoweredAssoc::Term(def_id, args);
if let Some(variant_def_id) = variant_resolution {
tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
lint.primary_message("ambiguous associated item");
let mut could_refer_to = |kind: DefKind, def_id, also| {
let note_msg = format!(
"`{}` could{} refer to the {} defined here",
assoc_ident,
also,
tcx.def_kind_descr(kind, def_id)
);
lint.span_note(tcx.def_span(def_id), note_msg);
};
could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(mode.def_kind(), assoc_item.def_id, " also");
lint.span_suggestion(
span,
"use fully-qualified syntax",
format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
Applicability::MachineApplicable,
);
});
}
Ok(result)
Ok((assoc_item.def_id, bound))
}
fn probe_inherent_assoc_shared(
/// Search for inherent associated items for use at the type level.
fn probe_inherent_assoc_item(
&self,
segment: &hir::PathSegment<'tcx>,
adt_did: DefId,
@ -1624,7 +1552,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.collect();
match &applicable_candidates[..] {
&[] => Err(self.complain_about_inherent_assoc_not_found(
&[] => Err(self.report_unresolved_inherent_assoc_item(
name,
self_ty,
candidates,
@ -1635,7 +1563,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&[applicable_candidate] => Ok(applicable_candidate),
&[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc(
&[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
name,
applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
span,
@ -1763,9 +1691,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.collect()
}
/// Lower a qualified path to a type.
/// Lower a [resolved][hir::QPath::Resolved] associated type path to a projection.
#[instrument(level = "debug", skip_all)]
fn lower_qpath_ty(
fn lower_resolved_assoc_ty_path(
&self,
span: Span,
opt_self_ty: Option<Ty<'tcx>>,
@ -1773,7 +1701,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_segment: Option<&hir::PathSegment<'tcx>>,
item_segment: &hir::PathSegment<'tcx>,
) -> Ty<'tcx> {
match self.lower_qpath_shared(
match self.lower_resolved_assoc_item_path(
span,
opt_self_ty,
item_def_id,
@ -1788,9 +1716,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Lower a qualified path to a const.
/// Lower a [resolved][hir::QPath::Resolved] associated const path to a (type-level) constant.
#[instrument(level = "debug", skip_all)]
fn lower_qpath_const(
fn lower_resolved_assoc_const_path(
&self,
span: Span,
opt_self_ty: Option<Ty<'tcx>>,
@ -1798,7 +1726,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_segment: Option<&hir::PathSegment<'tcx>>,
item_segment: &hir::PathSegment<'tcx>,
) -> Const<'tcx> {
match self.lower_qpath_shared(
match self.lower_resolved_assoc_item_path(
span,
opt_self_ty,
item_def_id,
@ -1814,8 +1742,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path.
#[instrument(level = "debug", skip_all)]
fn lower_qpath_shared(
fn lower_resolved_assoc_item_path(
&self,
span: Span,
opt_self_ty: Option<Ty<'tcx>>,
@ -1830,7 +1759,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else {
return Err(self.error_missing_qpath_self_ty(
return Err(self.report_missing_self_ty_for_resolved_path(
trait_def_id,
span,
item_segment,
@ -1849,57 +1778,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ok((item_def_id, item_args))
}
fn error_missing_qpath_self_ty(
&self,
trait_def_id: DefId,
span: Span,
item_segment: &hir::PathSegment<'tcx>,
assoc_tag: ty::AssocTag,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
self.report_ambiguous_assoc(
span,
&type_names,
&[path_str],
item_segment.ident.name,
assoc_tag,
)
}
pub fn prohibit_generic_args<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
@ -1908,7 +1786,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let args_visitors = segments.clone().flat_map(|segment| segment.args().args);
let mut result = Ok(());
if let Some(_) = args_visitors.clone().next() {
result = Err(self.report_prohibit_generics_error(
result = Err(self.report_prohibited_generic_args(
segments.clone(),
args_visitors,
err_extend,
@ -2069,14 +1947,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
generic_segments
}
/// Lower a type `Path` to a type.
/// Lower a [resolved][hir::QPath::Resolved] path to a type.
#[instrument(level = "debug", skip_all)]
pub fn lower_path(
pub fn lower_resolved_ty_path(
&self,
opt_self_ty: Option<Ty<'tcx>>,
path: &hir::Path<'tcx>,
hir_id: HirId,
permit_variants: bool,
permit_variants: PermitVariants,
) -> Ty<'tcx> {
debug!(?path.res, ?opt_self_ty, ?path.segments);
let tcx = self.tcx();
@ -2107,7 +1985,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
self.lower_path_segment(span, did, path.segments.last().unwrap())
}
Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
Res::Def(kind @ DefKind::Variant, def_id)
if let PermitVariants::Yes = permit_variants =>
{
// Lower "variant type" as if it were a real type.
// The resulting `Ty` is type of the variant's enum for now.
assert_eq!(opt_self_ty, None);
@ -2202,7 +2082,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} else {
None
};
self.lower_qpath_ty(
self.lower_resolved_assoc_ty_path(
span,
opt_self_ty,
def_id,
@ -2298,7 +2178,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Convert a [`hir::ConstArg`] to a [`ty::Const`](Const).
/// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`](Const).
#[instrument(skip(self), level = "debug")]
pub fn lower_const_arg(
&self,
@ -2323,7 +2203,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if tcx.features().generic_const_parameter_types()
&& (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions())
{
let e = tcx.dcx().span_err(
let e = self.dcx().span_err(
const_arg.span(),
"anonymous constants with lifetimes in their type are not yet supported",
);
@ -2334,7 +2214,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// use this type to feed the `type_of` and query results must not contain inference
// variables otherwise we will ICE.
if anon_const_type.has_non_region_infer() {
let e = tcx.dcx().span_err(
let e = self.dcx().span_err(
const_arg.span(),
"anonymous constants with inferred types are not yet supported",
);
@ -2344,7 +2224,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// We error when the type contains unsubstituted generics since we do not currently
// give the anon const any of the generics from the parent.
if anon_const_type.has_non_region_param() {
let e = tcx.dcx().span_err(
let e = self.dcx().span_err(
const_arg.span(),
"anonymous constants referencing generics are not yet supported",
);
@ -2363,13 +2243,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::ConstArgKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_const_path_resolved(opt_self_ty, path, hir_id)
self.lower_resolved_const_path(opt_self_ty, path, hir_id)
}
hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.lower_ty(qself);
self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment)
.unwrap_or_else(|guar| Const::new_error(tcx, guar))
hir::ConstArgKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
debug!(?hir_self_ty, ?segment);
let self_ty = self.lower_ty(hir_self_ty);
self.lower_type_relative_const_path(
self_ty,
hir_self_ty,
segment,
hir_id,
const_arg.span(),
)
.unwrap_or_else(|guar| Const::new_error(tcx, guar))
}
hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => {
ty::Const::new_error_with_message(
@ -2383,7 +2269,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
fn lower_const_path_resolved(
/// Lower a [resolved][hir::QPath::Resolved] path to a (type-level) constant.
fn lower_resolved_const_path(
&self,
opt_self_ty: Option<Ty<'tcx>>,
path: &hir::Path<'tcx>,
@ -2420,7 +2307,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} else {
None
};
self.lower_qpath_const(
self.lower_resolved_assoc_const_path(
span,
opt_self_ty,
did,
@ -2630,7 +2517,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_path(opt_self_ty, path, hir_ty.hir_id, false)
self.lower_resolved_ty_path(opt_self_ty, path, hir_ty.hir_id, PermitVariants::No)
}
&hir::TyKind::OpaqueDef(opaque_ty) => {
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
@ -2684,12 +2571,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
Ty::new_error(tcx, guar)
}
hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.lower_ty(qself);
self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false)
.map(|(ty, _, _)| ty)
.unwrap_or_else(|guar| Ty::new_error(tcx, guar))
hir::TyKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
debug!(?hir_self_ty, ?segment);
let self_ty = self.lower_ty(hir_self_ty);
self.lower_type_relative_ty_path(
self_ty,
hir_self_ty,
segment,
hir_ty.hir_id,
hir_ty.span,
PermitVariants::No,
)
.map(|(ty, _, _)| ty)
.unwrap_or_else(|guar| Ty::new_error(tcx, guar))
}
&hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => {
let def_id = tcx.require_lang_item(lang_item, Some(span));

View file

@ -1854,17 +1854,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fcx.err_ctxt().report_mismatched_types(cause, fcx.param_env, expected, found, ty_err);
let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..));
let parent_id = fcx.tcx.parent_hir_id(block_or_return_id);
let parent = fcx.tcx.hir_node(parent_id);
let parent = fcx.tcx.parent_hir_node(block_or_return_id);
if let Some(expr) = expression
&& let hir::Node::Expr(&hir::Expr {
kind: hir::ExprKind::Closure(&hir::Closure { body, .. }),
..
}) = parent
&& !matches!(fcx.tcx.hir_body(body).value.kind, hir::ExprKind::Block(..))
{
fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
let needs_block =
!matches!(fcx.tcx.hir_body(body).value.kind, hir::ExprKind::Block(..));
fcx.suggest_missing_semicolon(&mut err, expr, expected, needs_block, true);
}
// Verify that this is a tail expression of a function, otherwise the
// label pointing out the cause for the type coercion will be wrong
@ -1872,7 +1871,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
if let Some(expr) = expression
&& due_to_block
{
fcx.suggest_missing_semicolon(&mut err, expr, expected, false);
fcx.suggest_missing_semicolon(&mut err, expr, expected, false, false);
let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail(
&mut err,
expr,

View file

@ -911,7 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self,
&cause,
|mut err| {
self.suggest_missing_semicolon(&mut err, expr, e_ty, false);
self.suggest_missing_semicolon(&mut err, expr, e_ty, false, false);
self.suggest_mismatched_types_on_tail(
&mut err, expr, ty, e_ty, target_id,
);
@ -1900,62 +1900,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We defer checking whether the element type is `Copy` as it is possible to have
// an inference variable as a repeat count and it seems unlikely that `Copy` would
// have inference side effects required for type checking to succeed.
if tcx.features().generic_arg_infer() {
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
// If the length is 0, we don't create any elements, so we don't copy any.
// If the length is 1, we don't copy that one element, we move it. Only check
// for `Copy` if the length is larger, or unevaluated.
} else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
}
self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
let ty = Ty::new_array_with_const_len(tcx, t, count);
self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None));
ty
}
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
pub(super) fn enforce_repeat_element_needs_copy_bound(
&self,
element: &hir::Expr<'_>,
element_ty: Ty<'tcx>,
) {
let tcx = self.tcx;
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
match &element.kind {
hir::ExprKind::ConstBlock(..) => return,
hir::ExprKind::Path(qpath) => {
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
{
return;
}
}
_ => {}
}
// If someone calls a const fn or constructs a const value, they can extract that
// out into a separate constant (or a const block in the future), so we check that
// to tell them that in the diagnostic. Does not affect typeck.
let is_constable = match element.kind {
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
_ => traits::IsConstable::No,
},
hir::ExprKind::Path(qpath) => {
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
_ => traits::IsConstable::No,
}
}
_ => traits::IsConstable::No,
};
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
let code =
traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
self.require_type_meets(element_ty, element.span, code, lang_item);
}
fn check_expr_tuple(
&self,
elts: &'tcx [hir::Expr<'tcx>],

View file

@ -4,12 +4,12 @@ use itertools::Itertools;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize};
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{ExprKind, HirId, Node, QPath};
use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath};
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants};
use rustc_index::IndexVec;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace};
use rustc_middle::ty::adjustment::AllowTwoPhase;
@ -104,24 +104,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_repeat_exprs(&self) {
let mut deferred_repeat_expr_checks = self.deferred_repeat_expr_checks.borrow_mut();
debug!("FnCtxt::check_repeat_exprs: {} deferred checks", deferred_repeat_expr_checks.len());
for (element, element_ty, count) in deferred_repeat_expr_checks.drain(..) {
// We want to emit an error if the const is not structurally resolveable as otherwise
// we can find up conservatively proving `Copy` which may infer the repeat expr count
// to something that never required `Copy` in the first place.
let count =
self.structurally_resolve_const(element.span, self.normalize(element.span, count));
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the repeat expr count
// is erroneous/unknown. The user might wind up specifying a repeat count of 0/1.
if count.references_error() {
continue;
}
let deferred_repeat_expr_checks = deferred_repeat_expr_checks
.drain(..)
.flat_map(|(element, element_ty, count)| {
// Actual constants as the repeat element are inserted repeatedly instead
// of being copied via `Copy`, so we don't need to attempt to structurally
// resolve the repeat count which may unnecessarily error.
match &element.kind {
hir::ExprKind::ConstBlock(..) => return None,
hir::ExprKind::Path(qpath) => {
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
if let Res::Def(DefKind::Const | DefKind::AssocConst, _) = res {
return None;
}
}
_ => {}
}
// If the length is 0, we don't create any elements, so we don't copy any.
// If the length is 1, we don't copy that one element, we move it. Only check
// for `Copy` if the length is larger.
if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) {
self.enforce_repeat_element_needs_copy_bound(element, element_ty);
// We want to emit an error if the const is not structurally resolveable
// as otherwise we can wind up conservatively proving `Copy` which may
// infer the repeat expr count to something that never required `Copy` in
// the first place.
let count = self
.structurally_resolve_const(element.span, self.normalize(element.span, count));
// Avoid run on "`NotCopy: Copy` is not implemented" errors when the
// repeat expr count is erroneous/unknown. The user might wind up
// specifying a repeat count of 0/1.
if count.references_error() {
return None;
}
Some((element, element_ty, count))
})
// We collect to force the side effects of structurally resolving the repeat
// count to happen in one go, to avoid side effects from proving `Copy`
// affecting whether repeat counts are known or not. If we did not do this we
// would get results that depend on the order that we evaluate each repeat
// expr's `Copy` check.
.collect::<Vec<_>>();
let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| {
// If someone calls a const fn or constructs a const value, they can extract that
// out into a separate constant (or a const block in the future), so we check that
// to tell them that in the diagnostic. Does not affect typeck.
let is_constable = match element.kind {
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => {
traits::IsConstable::Fn
}
_ => traits::IsConstable::No,
},
hir::ExprKind::Path(qpath) => {
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
_ => traits::IsConstable::No,
}
}
_ => traits::IsConstable::No,
};
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
let code = traits::ObligationCauseCode::RepeatElementCopy {
is_constable,
elt_span: element.span,
};
self.require_type_meets(element_ty, element.span, code, lang_item);
};
for (element, element_ty, count) in deferred_repeat_expr_checks {
match count.kind() {
ty::ConstKind::Value(val) => {
if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) {
enforce_copy_bound(element, element_ty)
} else {
// If the length is 0 or 1 we don't actually copy the element, we either don't create it
// or we just use the one value.
}
}
// If the length is a generic parameter or some rigid alias then conservatively
// require `element_ty: Copy` as it may wind up being `>1` after monomorphization.
ty::ConstKind::Param(_)
| ty::ConstKind::Expr(_)
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty),
ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
unreachable!()
}
}
}
}
@ -2105,15 +2177,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match *qpath {
QPath::Resolved(ref maybe_qself, path) => {
let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw);
let ty = self.lowerer().lower_path(self_ty, path, hir_id, true);
let ty = self.lowerer().lower_resolved_ty_path(
self_ty,
path,
hir_id,
PermitVariants::Yes,
);
(path.res, LoweredTy::from_raw(self, path_span, ty))
}
QPath::TypeRelative(qself, segment) => {
let ty = self.lower_ty(qself);
QPath::TypeRelative(hir_self_ty, segment) => {
let self_ty = self.lower_ty(hir_self_ty);
let result = self
.lowerer()
.lower_assoc_path_ty(hir_id, path_span, ty.raw, qself, segment, true);
let result = self.lowerer().lower_type_relative_ty_path(
self_ty.raw,
hir_self_ty,
segment,
hir_id,
path_span,
PermitVariants::Yes,
);
let ty = result
.map(|(ty, _, _)| ty)
.unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));

View file

@ -308,17 +308,16 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
))
}
fn lower_assoc_shared(
fn lower_assoc_item_path(
&self,
span: Span,
item_def_id: DefId,
item_segment: &rustc_hir::PathSegment<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
_assoc_tag: ty::AssocTag,
) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
let trait_ref = self.instantiate_binder_with_fresh_vars(
span,
// FIXME(mgca): this should be assoc const if that is the `kind`
// FIXME(mgca): `item_def_id` can be an AssocConst; rename this variant.
infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id),
poly_trait_ref,
);

View file

@ -764,6 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expression: &'tcx hir::Expr<'tcx>,
expected: Ty<'tcx>,
needs_block: bool,
parent_is_closure: bool,
) {
if expected.is_unit() {
// `BlockTailExpression` only relevant if the tail expr would be
@ -799,6 +800,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
}
ExprKind::Path(..) | ExprKind::Lit(_)
if parent_is_closure
&& !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
{
err.span_suggestion_verbose(
expression.span.shrink_to_lo(),
"consider ignoring the value",
"_ = ",
Applicability::MachineApplicable,
);
}
_ => (),
}
}

View file

@ -196,13 +196,16 @@ fn typeck_with_inspect<'tcx>(
fcx.write_ty(id, expected_type);
};
// Whether to check repeat exprs before/after inference fallback is somewhat arbitrary of a decision
// as neither option is strictly more permissive than the other. However, we opt to check repeat exprs
// first as errors from not having inferred array lengths yet seem less confusing than errors from inference
// fallback arbitrarily inferring something incompatible with `Copy` inference side effects.
// Whether to check repeat exprs before/after inference fallback is somewhat
// arbitrary of a decision as neither option is strictly more permissive than
// the other. However, we opt to check repeat exprs first as errors from not
// having inferred array lengths yet seem less confusing than errors from inference
// fallback arbitrarily inferring something incompatible with `Copy` inference
// side effects.
//
// This should also be forwards compatible with moving repeat expr checks to a custom goal kind or using
// marker traits in the future.
// FIXME(#140855): This should also be forwards compatible with moving
// repeat expr checks to a custom goal kind or using marker traits in
// the future.
fcx.check_repeat_exprs();
fcx.type_inference_fallback();

View file

@ -291,6 +291,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
probe::ObjectPick => {
let trait_def_id = pick.item.container_id(self.tcx);
// If the trait is not object safe (specifically, we care about when
// the receiver is not valid), then there's a chance that we will not
// actually be able to recover the object by derefing the receiver like
// we should if it were valid.
if !self.tcx.is_dyn_compatible(trait_def_id) {
return ty::GenericArgs::extend_with_error(self.tcx, trait_def_id, &[]);
}
// This shouldn't happen for non-region error kinds, but may occur
// when we have error regions. Specifically, since we canonicalize
// during method steps, we may successfully deref when we assemble

View file

@ -17,8 +17,7 @@ use tracing::debug;
use crate::infer::InferCtxt;
use crate::infer::canonical::{
Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind,
OriginalQueryValues,
Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarKind, OriginalQueryValues,
};
impl<'tcx> InferCtxt<'tcx> {
@ -174,10 +173,8 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
match r.kind() {
ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) },
r,
),
ty::RePlaceholder(placeholder) => canonicalizer
.canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r),
ty::ReVar(vid) => {
let universe = infcx
@ -186,10 +183,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
.unwrap_region_constraints()
.probe_value(vid)
.unwrap_err();
canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
r,
)
canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r)
}
_ => {
@ -294,7 +288,7 @@ struct Canonicalizer<'cx, 'tcx> {
/// Set to `None` to disable the resolution of inference variables.
infcx: Option<&'cx InferCtxt<'tcx>>,
tcx: TyCtxt<'tcx>,
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
variables: SmallVec<[CanonicalVarKind<'tcx>; 8]>,
query_state: &'cx mut OriginalQueryValues<'tcx>,
// Note that indices is only used once `var_values` is big enough to be
// heap-allocated.
@ -368,9 +362,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
ui = ty::UniverseIndex::ROOT;
}
self.canonicalize_ty_var(
CanonicalVarInfo {
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
},
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
t,
)
}
@ -382,10 +374,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t)
}
}
ty::Infer(ty::FloatVar(vid)) => {
@ -393,10 +382,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if nt != t {
return self.fold_ty(nt);
} else {
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t)
}
}
@ -408,10 +394,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
if !self.canonicalize_mode.preserve_universes() {
placeholder.universe = ty::UniverseIndex::ROOT;
}
self.canonicalize_ty_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) },
t,
)
self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
}
ty::Bound(debruijn, _) => {
@ -483,10 +466,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
// FIXME: perf problem described in #55921.
ui = ty::UniverseIndex::ROOT;
}
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
ct,
);
return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct);
}
}
}
@ -501,10 +481,8 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}
ty::ConstKind::Placeholder(placeholder) => {
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) },
ct,
);
return self
.canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct);
}
_ => {}
}
@ -595,7 +573,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
let canonical_variables =
tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables());
let max_universe = canonical_variables
.iter()
@ -610,18 +588,22 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// or returns an existing variable if `kind` has already been
/// seen. `kind` is expected to be an unbound variable (or
/// potentially a free region).
fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
fn canonical_var(
&mut self,
var_kind: CanonicalVarKind<'tcx>,
value: GenericArg<'tcx>,
) -> BoundVar {
let Canonicalizer { variables, query_state, indices, .. } = self;
let var_values = &mut query_state.var_values;
let universe = info.universe();
let universe = var_kind.universe();
if universe != ty::UniverseIndex::ROOT {
assert!(self.canonicalize_mode.preserve_universes());
// Insert universe into the universe map. To preserve the order of the
// universes in the value being canonicalized, we don't update the
// universe in `info` until we have finished canonicalizing.
// universe in `var_kind` until we have finished canonicalizing.
match query_state.universe_map.binary_search(&universe) {
Err(idx) => query_state.universe_map.insert(idx, universe),
Ok(_) => {}
@ -636,14 +618,14 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
if !var_values.spilled() {
// `var_values` is stack-allocated. `indices` isn't used yet. Do a
// direct linear search of `var_values`.
if let Some(idx) = var_values.iter().position(|&k| k == kind) {
if let Some(idx) = var_values.iter().position(|&v| v == value) {
// `kind` is already present in `var_values`.
BoundVar::new(idx)
} else {
// `kind` isn't present in `var_values`. Append it. Likewise
// for `info` and `variables`.
variables.push(info);
var_values.push(kind);
// for `var_kind` and `variables`.
variables.push(var_kind);
var_values.push(value);
assert_eq!(variables.len(), var_values.len());
// If `var_values` has become big enough to be heap-allocated,
@ -653,7 +635,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
*indices = var_values
.iter()
.enumerate()
.map(|(i, &kind)| (kind, BoundVar::new(i)))
.map(|(i, &value)| (value, BoundVar::new(i)))
.collect();
}
// The cv is the index of the appended element.
@ -661,9 +643,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
}
} else {
// `var_values` is large. Do a hashmap search via `indices`.
*indices.entry(kind).or_insert_with(|| {
variables.push(info);
var_values.push(kind);
*indices.entry(value).or_insert_with(|| {
variables.push(var_kind);
var_values.push(value);
assert_eq!(variables.len(), var_values.len());
BoundVar::new(variables.len() - 1)
})
@ -673,7 +655,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// Replaces the universe indexes used in `var_values` with their index in
/// `query_state.universe_map`. This minimizes the maximum universe used in
/// the canonicalized value.
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> {
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> {
if self.query_state.universe_map.len() == 1 {
return self.variables;
}
@ -688,37 +670,33 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
self.variables
.iter()
.map(|v| CanonicalVarInfo {
kind: match v.kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
return *v;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
}
CanonicalVarKind::Region(u) => {
CanonicalVarKind::Region(reverse_universe_map[&u])
}
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
},
.map(|&kind| match kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
return kind;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
}
CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(ty::Placeholder {
universe: reverse_universe_map[&placeholder.universe],
..placeholder
})
}
})
.collect()
}
@ -740,20 +718,17 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
&mut self,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
self.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) },
r,
)
self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
}
/// Creates a canonical variable (with the given `info`)
/// representing the region `r`; return a region referencing it.
fn canonical_var_for_region(
&mut self,
info: CanonicalVarInfo<'tcx>,
var_kind: CanonicalVarKind<'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
let var = self.canonical_var(info, r.into());
let var = self.canonical_var(var_kind, r.into());
let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon };
ty::Region::new_bound(self.cx(), self.binder_index, br)
}
@ -762,9 +737,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// if `ty_var` is bound to anything; if so, canonicalize
/// *that*. Otherwise, create a new canonical variable for
/// `ty_var`.
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
fn canonicalize_ty_var(
&mut self,
var_kind: CanonicalVarKind<'tcx>,
ty_var: Ty<'tcx>,
) -> Ty<'tcx> {
debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
let var = self.canonical_var(info, ty_var.into());
let var = self.canonical_var(var_kind, ty_var.into());
Ty::new_bound(self.tcx, self.binder_index, var.into())
}
@ -774,13 +753,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// `const_var`.
fn canonicalize_const_var(
&mut self,
info: CanonicalVarInfo<'tcx>,
const_var: ty::Const<'tcx>,
var_kind: CanonicalVarKind<'tcx>,
ct_var: ty::Const<'tcx>,
) -> ty::Const<'tcx> {
debug_assert!(
!self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var))
!self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
);
let var = self.canonical_var(info, const_var.into());
let var = self.canonical_var(var_kind, ct_var.into());
ty::Const::new_bound(self.tcx, self.binder_index, var)
}
}

View file

@ -81,14 +81,14 @@ impl<'tcx> InferCtxt<'tcx> {
fn instantiate_canonical_vars(
&self,
span: Span,
variables: &List<CanonicalVarInfo<'tcx>>,
variables: &List<CanonicalVarKind<'tcx>>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> CanonicalVarValues<'tcx> {
CanonicalVarValues {
var_values: self.tcx.mk_args_from_iter(
variables
.iter()
.map(|info| self.instantiate_canonical_var(span, info, &universe_map)),
.map(|kind| self.instantiate_canonical_var(span, kind, &universe_map)),
),
}
}
@ -104,10 +104,10 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn instantiate_canonical_var(
&self,
span: Span,
cv_info: CanonicalVarInfo<'tcx>,
kind: CanonicalVarKind<'tcx>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> GenericArg<'tcx> {
match cv_info.kind {
match kind {
CanonicalVarKind::Ty(ty_kind) => {
let ty = match ty_kind {
CanonicalTyVarKind::General(ui) => {

View file

@ -445,17 +445,17 @@ impl<'tcx> InferCtxt<'tcx> {
// a fresh inference variable.
let result_args = CanonicalVarValues {
var_values: self.tcx.mk_args_from_iter(
query_response.variables.iter().enumerate().map(|(index, info)| {
if info.universe() != ty::UniverseIndex::ROOT {
query_response.variables.iter().enumerate().map(|(index, var_kind)| {
if var_kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all, we have to deal with them for now.
self.instantiate_canonical_var(cause.span, info, |u| {
self.instantiate_canonical_var(cause.span, var_kind, |u| {
universe_map[u.as_usize()]
})
} else if info.is_existential() {
} else if var_kind.is_existential() {
match opt_values[BoundVar::new(index)] {
Some(k) => k,
None => self.instantiate_canonical_var(cause.span, info, |u| {
None => self.instantiate_canonical_var(cause.span, var_kind, |u| {
universe_map[u.as_usize()]
}),
}

View file

@ -837,6 +837,7 @@ lint_unexpected_cfg_name_similar_name = there is a config with a similar name
lint_unexpected_cfg_name_similar_name_different_values = there is a config with a similar name and different values
lint_unexpected_cfg_name_similar_name_no_value = there is a config with a similar name and no value
lint_unexpected_cfg_name_similar_name_value = there is a config with a similar name and value
lint_unexpected_cfg_name_version_syntax = there is a similar config predicate: `version("..")`
lint_unexpected_cfg_name_with_similar_value = found config with similar value
lint_unexpected_cfg_value = unexpected `cfg` condition value: {$has_value ->

View file

@ -140,6 +140,14 @@ pub(super) fn unexpected_cfg_name(
let code_sugg = if is_feature_cfg && is_from_cargo {
lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures
// Suggest correct `version("..")` predicate syntax
} else if let Some((_value, value_span)) = value
&& name == sym::version
{
lints::unexpected_cfg_name::CodeSuggestion::VersionSyntax {
between_name_and_value: name_span.between(value_span),
after_value: value_span.shrink_to_hi(),
}
// Suggest the most probable if we found one
} else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
is_feature_cfg |= best_match == sym::feature;

View file

@ -2299,6 +2299,16 @@ pub(crate) mod unexpected_cfg_name {
pub(crate) enum CodeSuggestion {
#[help(lint_unexpected_cfg_define_features)]
DefineFeatures,
#[multipart_suggestion(
lint_unexpected_cfg_name_version_syntax,
applicability = "machine-applicable"
)]
VersionSyntax {
#[suggestion_part(code = "(")]
between_name_and_value: Span,
#[suggestion_part(code = ")")]
after_value: Span,
},
#[suggestion(
lint_unexpected_cfg_name_similar_name_value,
applicability = "maybe-incorrect",

View file

@ -16,6 +16,7 @@ declare_lint_pass! {
/// that are used by other parts of the compiler.
HardwiredLints => [
// tidy-alphabetical-start
AARCH64_SOFTFLOAT_NEON,
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
AMBIGUOUS_ASSOCIATED_ITEMS,
AMBIGUOUS_GLOB_IMPORTS,
@ -5043,14 +5044,14 @@ declare_lint! {
///
/// ```text
/// error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType`
/// --> $DIR/wasm_c_abi_transition.rs:17:1
/// |
/// | pub extern "C" fn my_fun(_x: MyType) {}
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// |
/// = 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 #138762 <https://github.com/rust-lang/rust/issues/138762>
/// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
/// --> $DIR/wasm_c_abi_transition.rs:17:1
/// |
/// | pub extern "C" fn my_fun(_x: MyType) {}
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// |
/// = 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 #138762 <https://github.com/rust-lang/rust/issues/138762>
/// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
/// ```
///
/// ### Explanation
@ -5067,3 +5068,44 @@ declare_lint! {
reference: "issue #138762 <https://github.com/rust-lang/rust/issues/138762>",
};
}
declare_lint! {
/// The `aarch64_softfloat_neon` lint detects usage of `#[target_feature(enable = "neon")]` on
/// softfloat aarch64 targets. Enabling this target feature causes LLVM to alter the ABI of
/// function calls, making this attribute unsound to use.
///
/// ### Example
///
/// ```rust,ignore (needs aarch64-unknown-none-softfloat)
/// #[target_feature(enable = "neon")]
/// fn with_neon() {}
/// ```
///
/// This will produce:
///
/// ```text
/// error: enabling the `neon` target feature on the current target is unsound due to ABI issues
/// --> $DIR/abi-incompatible-target-feature-attribute-fcw.rs:11:18
/// |
/// | #[target_feature(enable = "neon")]
/// | ^^^^^^^^^^^^^^^
/// |
/// = 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 #134375 <https://github.com/rust-lang/rust/issues/134375>
/// ```
///
/// ### Explanation
///
/// If a function like `with_neon` above ends up containing calls to LLVM builtins, those will
/// not use the correct ABI. This is caused by a lack of support in LLVM for mixing code with
/// and without the `neon` target feature. The target feature should never have been stabilized
/// on this target due to this issue, but the problem was not known at the time of
/// stabilization.
pub AARCH64_SOFTFLOAT_NEON,
Warn,
"detects code that could be affected by ABI issues on aarch64 softfloat targets",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
reference: "issue #134375 <https://github.com/rust-lang/rust/issues/134375>",
};
}

View file

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

View file

@ -1569,6 +1569,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
<- tcx.explicit_implied_const_bounds(def_id).skip_binder());
}
}
if let DefKind::AnonConst = def_kind {
record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id));
}
if tcx.impl_method_has_trait_impl_trait_tys(def_id)
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
{

View file

@ -480,6 +480,7 @@ define_tables! {
doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
opaque_ty_origin: Table<DefIndex, LazyValue<hir::OpaqueTyOrigin<DefId>>>,
anon_const_kind: Table<DefIndex, LazyValue<ty::AnonConstKind>>,
}
#[derive(TyEncodable, TyDecodable)]

View file

@ -27,7 +27,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lock;
use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
pub use rustc_type_ir as ir;
pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
pub use rustc_type_ir::CanonicalTyVarKind;
use smallvec::SmallVec;
use crate::mir::ConstraintCategory;
@ -35,9 +35,9 @@ use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}
pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput<TyCtxt<'tcx>, V>;
pub type Canonical<'tcx, V> = ir::Canonical<TyCtxt<'tcx>, V>;
pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo<TyCtxt<'tcx>>;
pub type CanonicalVarKind<'tcx> = ir::CanonicalVarKind<TyCtxt<'tcx>>;
pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues<TyCtxt<'tcx>>;
pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
pub type CanonicalVarKinds<'tcx> = &'tcx List<CanonicalVarKind<'tcx>>;
/// When we canonicalize a value to form a query, we wind up replacing
/// various parts of it with canonical variables. This struct stores

View file

@ -96,49 +96,46 @@ bitflags::bitflags! {
/// `#[cold]`: a hint to LLVM that this function, when called, is never on
/// the hot path.
const COLD = 1 << 0;
/// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
/// function is never null and the function has no side effects other than allocating.
const ALLOCATOR = 1 << 1;
/// An indicator that function will never unwind. Will become obsolete
/// once C-unwind is fully stabilized.
const NEVER_UNWIND = 1 << 3;
/// `#[rustc_nounwind]`: An indicator that function will never unwind.
const NEVER_UNWIND = 1 << 1;
/// `#[naked]`: an indicator to LLVM that no function prologue/epilogue
/// should be generated.
const NAKED = 1 << 4;
const NAKED = 1 << 2;
/// `#[no_mangle]`: an indicator that the function's name should be the same
/// as its symbol.
const NO_MANGLE = 1 << 5;
const NO_MANGLE = 1 << 3;
/// `#[rustc_std_internal_symbol]`: an indicator that this symbol is a
/// "weird symbol" for the standard library in that it has slightly
/// different linkage, visibility, and reachability rules.
const RUSTC_STD_INTERNAL_SYMBOL = 1 << 6;
const RUSTC_STD_INTERNAL_SYMBOL = 1 << 4;
/// `#[thread_local]`: indicates a static is actually a thread local
/// piece of memory
const THREAD_LOCAL = 1 << 8;
/// `#[used]`: indicates that LLVM can't eliminate this function (but the
const THREAD_LOCAL = 1 << 5;
/// `#[used(compiler)]`: indicates that LLVM can't eliminate this function (but the
/// linker can!).
const USED = 1 << 9;
/// `#[track_caller]`: allow access to the caller location
const TRACK_CALLER = 1 << 10;
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
/// declaration.
const FFI_PURE = 1 << 11;
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
/// declaration.
const FFI_CONST = 1 << 12;
// (Bit 13 was used for `#[cmse_nonsecure_entry]`, but is now unused.)
// (Bit 14 was used for `#[coverage(off)]`, but is now unused.)
const USED_COMPILER = 1 << 6;
/// `#[used(linker)]`:
/// indicates that neither LLVM nor the linker will eliminate this function.
const USED_LINKER = 1 << 15;
const USED_LINKER = 1 << 7;
/// `#[track_caller]`: allow access to the caller location
const TRACK_CALLER = 1 << 8;
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
/// declaration.
const FFI_PURE = 1 << 9;
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
/// declaration.
const FFI_CONST = 1 << 10;
/// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
/// function is never null and the function has no side effects other than allocating.
const ALLOCATOR = 1 << 11;
/// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory.
const DEALLOCATOR = 1 << 16;
const DEALLOCATOR = 1 << 12;
/// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory.
const REALLOCATOR = 1 << 17;
const REALLOCATOR = 1 << 13;
/// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory.
const ALLOCATOR_ZEROED = 1 << 18;
const ALLOCATOR_ZEROED = 1 << 14;
/// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function.
const NO_BUILTINS = 1 << 19;
const NO_BUILTINS = 1 << 15;
}
}
rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }

View file

@ -6,7 +6,7 @@ use rustc_span::ErrorGuaranteed;
use crate::query::CyclePlaceholder;
use crate::ty::adjustment::CoerceUnsizedInfo;
use crate::ty::{self, Ty};
use crate::ty::{self, Ty, TyCtxt};
use crate::{mir, traits};
#[derive(Copy, Clone)]
@ -207,6 +207,11 @@ impl EraseType for ty::Binder<'_, ty::FnSig<'_>> {
type Result = [u8; size_of::<ty::Binder<'static, ty::FnSig<'static>>>()];
}
impl EraseType for ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>> {
type Result =
[u8; size_of::<ty::Binder<'static, ty::CoroutineWitnessTypes<TyCtxt<'static>>>>()];
}
impl EraseType for ty::Binder<'_, &'_ ty::List<Ty<'_>>> {
type Result = [u8; size_of::<ty::Binder<'static, &'static ty::List<Ty<'static>>>>()];
}
@ -311,6 +316,7 @@ trivial! {
rustc_middle::ty::Asyncness,
rustc_middle::ty::AsyncDestructor,
rustc_middle::ty::BoundVariableKind,
rustc_middle::ty::AnonConstKind,
rustc_middle::ty::DeducedParamAttrs,
rustc_middle::ty::Destructor,
rustc_middle::ty::fast_reject::SimplifiedType,

View file

@ -922,6 +922,12 @@ rustc_queries! {
separate_provide_extern
}
query coroutine_hidden_types(
def_id: DefId
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
desc { "looking up the hidden types stored across await points in a coroutine" }
}
/// Gets a map with the variances of every item in the local crate.
///
/// <div class="warning">
@ -2586,6 +2592,11 @@ rustc_queries! {
desc { "estimating codegen size of `{}`", key }
cache_on_disk_if { true }
}
query anon_const_kind(def_id: DefId) -> ty::AnonConstKind {
desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) }
separate_provide_extern
}
}
rustc_with_all_queries! { define_callbacks! }

View file

@ -18,7 +18,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::{Span, SpanDecoder, SpanEncoder};
use crate::arena::ArenaAllocatable;
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds};
use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance};
use crate::mir::mono::MonoItem;
use crate::mir::{self};
@ -310,11 +310,11 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Region<'tcx> {
}
}
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarInfos<'tcx> {
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for CanonicalVarKinds<'tcx> {
fn decode(decoder: &mut D) -> Self {
let len = decoder.read_usize();
decoder.interner().mk_canonical_var_infos_from_iter(
(0..len).map::<CanonicalVarInfo<'tcx>, _>(|_| Decodable::decode(decoder)),
(0..len).map::<CanonicalVarKind<'tcx>, _>(|_| Decodable::decode(decoder)),
)
}
}

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use rustc_data_structures::intern::Interned;
use rustc_error_messages::MultiSpan;
use rustc_macros::HashStable;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_type_ir::walk::TypeWalker;
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
@ -259,3 +259,16 @@ impl<'tcx> Const<'tcx> {
TypeWalker::new(self.into())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum AnonConstKind {
/// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope
GCE,
/// stable `min_const_generics` anon consts are not allowed to use any generic parameters
MCG,
/// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters
/// but must not depend on the actual instantiation. See #76200 for more information
RepeatExprCount,
/// anon consts outside of the type system, e.g. enum discriminants
NonTypeSystem,
}

View file

@ -60,7 +60,7 @@ use tracing::{debug, instrument};
use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos};
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds};
use crate::lint::lint_level;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
@ -107,9 +107,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.mk_predefined_opaques_in_body(data)
}
type LocalDefIds = &'tcx ty::List<LocalDefId>;
type CanonicalVars = CanonicalVarInfos<'tcx>;
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
self.mk_canonical_var_infos(infos)
type CanonicalVarKinds = CanonicalVarKinds<'tcx>;
fn mk_canonical_var_kinds(
self,
kinds: &[ty::CanonicalVarKind<Self>],
) -> Self::CanonicalVarKinds {
self.mk_canonical_var_kinds(kinds)
}
type ExternalConstraints = ExternalConstraints<'tcx>;
@ -340,7 +343,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn coroutine_hidden_types(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
self.coroutine_hidden_types(def_id)
}
@ -833,7 +836,7 @@ pub struct CtxtInterners<'tcx> {
const_lists: InternedSet<'tcx, List<ty::Const<'tcx>>>,
args: InternedSet<'tcx, GenericArgs<'tcx>>,
type_lists: InternedSet<'tcx, List<Ty<'tcx>>>,
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
canonical_var_kinds: InternedSet<'tcx, List<CanonicalVarKind<'tcx>>>,
region: InternedSet<'tcx, RegionKind<'tcx>>,
poly_existential_predicates: InternedSet<'tcx, List<PolyExistentialPredicate<'tcx>>>,
predicate: InternedSet<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
@ -872,7 +875,7 @@ impl<'tcx> CtxtInterners<'tcx> {
type_lists: InternedSet::with_capacity(N * 4),
region: InternedSet::with_capacity(N * 4),
poly_existential_predicates: InternedSet::with_capacity(N / 4),
canonical_var_infos: InternedSet::with_capacity(N / 2),
canonical_var_kinds: InternedSet::with_capacity(N / 2),
predicate: InternedSet::with_capacity(N),
clauses: InternedSet::with_capacity(N),
projs: InternedSet::with_capacity(N * 4),
@ -2675,7 +2678,7 @@ slice_interners!(
const_lists: pub mk_const_list(Const<'tcx>),
args: pub mk_args(GenericArg<'tcx>),
type_lists: pub mk_type_list(Ty<'tcx>),
canonical_var_infos: pub mk_canonical_var_infos(CanonicalVarInfo<'tcx>),
canonical_var_kinds: pub mk_canonical_var_kinds(CanonicalVarKind<'tcx>),
poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
projs: pub mk_projs(ProjectionKind),
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
@ -3055,9 +3058,9 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn mk_canonical_var_infos_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<CanonicalVarInfo<'tcx>, &'tcx List<CanonicalVarInfo<'tcx>>>,
T: CollectAndApply<CanonicalVarKind<'tcx>, &'tcx List<CanonicalVarKind<'tcx>>>,
{
T::collect_and_apply(iter, |xs| self.mk_canonical_var_infos(xs))
T::collect_and_apply(iter, |xs| self.mk_canonical_var_kinds(xs))
}
pub fn mk_place_elems_from_iter<I, T>(self, iter: I) -> T::Output

View file

@ -74,8 +74,8 @@ pub use self::closure::{
place_to_string_for_capture,
};
pub use self::consts::{
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, ValTreeKind,
Value,
AnonConstKind, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst,
ValTree, ValTreeKind, Value,
};
pub use self::context::{
CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,

View file

@ -68,6 +68,7 @@ trivially_parameterized_over_tcx! {
ty::AsyncDestructor,
ty::AssocItemContainer,
ty::Asyncness,
ty::AnonConstKind,
ty::DeducedParamAttrs,
ty::Destructor,
ty::Generics,

View file

@ -26,7 +26,7 @@ use crate::query::Providers;
use crate::ty::layout::{FloatExt, IntegerExt};
use crate::ty::{
self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable,
TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, fold_regions,
TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast,
};
#[derive(Copy, Clone, Debug)]
@ -737,40 +737,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state. This properly replaces
/// `ReErased` with new existential bound lifetimes.
pub fn coroutine_hidden_types(
self,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
let coroutine_layout = self.mir_coroutine_witnesses(def_id);
let mut vars = vec![];
let bound_tys = self.mk_type_list_from_iter(
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| {
let ty = fold_regions(self, decl.ty, |re, debruijn| {
assert_eq!(re, self.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty
}),
);
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
bound_tys,
self.mk_bound_variable_kinds(&vars),
))
}
/// Expands the given impl trait type, stopping if the type is recursive.
#[instrument(skip(self), level = "debug", ret)]
pub fn try_expand_impl_trait_type(

View file

@ -382,6 +382,9 @@ fn extend_type_not_partial_eq<'tcx>(
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
match ty.kind() {
ty::Dynamic(..) => return ControlFlow::Break(()),
// Unsafe binders never implement `PartialEq`, so avoid walking into them
// which would require instantiating its binder with placeholders too.
ty::UnsafeBinder(..) => return ControlFlow::Break(()),
ty::FnPtr(..) => return ControlFlow::Continue(()),
ty::Adt(def, _args) => {
let ty_def_id = def.did();

View file

@ -30,6 +30,7 @@ impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> {
function: &Operand<'tcx>,
arg: String,
span: Span,
is_in_const: bool,
) -> Option<Error> {
let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
let [input] = fn_sig.inputs() else { return None };
@ -97,8 +98,14 @@ impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> {
)),
// uNN → fNN
(Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())),
// bool → { x8 }
(Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())),
// bool → { x8 } in const context since `From::from` is not const yet
// FIXME: is it possible to know when the parentheses arent necessary?
// FIXME(const_traits): Remove this when From::from is constified?
(Bool, Int(..) | Uint(..)) if is_in_const => {
err(format!("({arg}) as {}", fn_sig.output()))
}
// " using `x8::from`
(Bool, Int(..) | Uint(..)) => err(format!("{}::from({arg})", fn_sig.output())),
_ => return None,
})
}
@ -114,7 +121,13 @@ impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> {
&& self.tcx.is_intrinsic(func_def_id, sym::transmute)
&& let span = self.body.source_info(location).span
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg)
&& let Some(lint) = self.is_unnecessary_transmute(func, snippet, span)
&& let def_id = self.body.source.def_id()
&& let Some(lint) = self.is_unnecessary_transmute(
func,
snippet,
span,
self.tcx.hir_body_const_context(def_id.expect_local()).is_some(),
)
&& let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes)
{
self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint);

View file

@ -6,7 +6,7 @@ use rustc_middle::mir::{
BasicBlock, BasicBlockData, Body, Local, LocalDecl, MirSource, Operand, Place, Rvalue,
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt};
use super::*;
use crate::patch::MirPatch;
@ -121,9 +121,10 @@ pub(super) fn build_async_drop_shim<'tcx>(
parent_args.as_coroutine().resume_ty(),
)));
body.phase = MirPhase::Runtime(RuntimePhase::Initial);
if !needs_async_drop {
if !needs_async_drop || drop_ty.references_error() {
// Returning noop body for types without `need async drop`
// (or sync Drop in case of !`need async drop` && `need drop`)
// (or sync Drop in case of !`need async drop` && `need drop`).
// And also for error types.
return body;
}

View file

@ -4,8 +4,8 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
use rustc_type_ir::inherent::*;
use rustc_type_ir::solve::{Goal, QueryInput};
use rustc_type_ir::{
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike,
Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarKind, InferCtxtLike, Interner,
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
use crate::delegate::SolverDelegate;
@ -50,7 +50,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
// Mutable fields.
variables: &'a mut Vec<I::GenericArg>,
primitive_var_infos: Vec<CanonicalVarInfo<I>>,
var_kinds: Vec<CanonicalVarKind<I>>,
variable_lookup_table: HashMap<I::GenericArg, usize>,
binder_index: ty::DebruijnIndex,
@ -73,7 +73,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
primitive_var_infos: Vec::new(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
cache: Default::default(),
@ -106,7 +106,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
primitive_var_infos: Vec::new(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
cache: Default::default(),
@ -123,7 +123,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// We're able to reuse the `variable_lookup_table` as whether or not
// it already contains an entry for `'static` does not matter.
variable_lookup_table: env_canonicalizer.variable_lookup_table,
primitive_var_infos: env_canonicalizer.primitive_var_infos,
var_kinds: env_canonicalizer.var_kinds,
binder_index: ty::INNERMOST,
// We do not reuse the cache as it may contain entries whose canonicalized
@ -149,7 +149,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
fn get_or_insert_bound_var(
&mut self,
arg: impl Into<I::GenericArg>,
canonical_var_info: CanonicalVarInfo<I>,
kind: CanonicalVarKind<I>,
) -> ty::BoundVar {
// FIXME: 16 is made up and arbitrary. We should look at some
// perf data here.
@ -162,14 +162,14 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
*self.variable_lookup_table.entry(arg).or_insert_with(|| {
let var = self.variables.len();
self.variables.push(arg);
self.primitive_var_infos.push(canonical_var_info);
self.var_kinds.push(kind);
var
})
} else {
self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
let var = self.variables.len();
self.variables.push(arg);
self.primitive_var_infos.push(canonical_var_info);
self.var_kinds.push(kind);
var
})
};
@ -177,8 +177,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
ty::BoundVar::from(idx)
}
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) {
let mut var_infos = self.primitive_var_infos;
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
let mut var_kinds = self.var_kinds;
// See the rustc-dev-guide section about how we deal with universes
// during canonicalization in the new solver.
match self.canonicalize_mode {
@ -192,25 +192,25 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// information for placeholders and inference variables created inside
// of the query.
CanonicalizeMode::Response { max_input_universe } => {
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
let uv = var.universe();
let new_uv = ty::UniverseIndex::from(
uv.index().saturating_sub(max_input_universe.index()),
);
*var = var.with_updated_universe(new_uv);
}
let max_universe = var_infos
let max_universe = var_kinds
.iter()
.map(|info| info.universe())
.map(|kind| kind.universe())
.max()
.unwrap_or(ty::UniverseIndex::ROOT);
let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
return (max_universe, var_infos);
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
return (max_universe, var_kinds);
}
}
// Given a `var_infos` with existentials `En` and universals `Un` in
// Given a `var_kinds` with existentials `En` and universals `Un` in
// universes `n`, this algorithm compresses them in place so that:
//
// - the new universe indices are as small as possible
@ -219,12 +219,12 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// 2. put a placeholder in the same universe as an existential which cannot name it
//
// Let's walk through an example:
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
// - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
// - var_infos: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
// - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
// - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
// - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
// - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
// - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
//
// This algorithm runs in `O(mn)` where `n` is the number of different universes and
// `m` the number of variables. This should be fine as both are expected to be small.
@ -232,7 +232,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
let mut existential_in_new_uv = None;
let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
while let Some(orig_uv) = next_orig_uv.take() {
let mut update_uv = |var: &mut CanonicalVarInfo<I>, orig_uv, is_existential| {
let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
let uv = var.universe();
match uv.cmp(&orig_uv) {
Ordering::Less => (), // Already updated
@ -284,7 +284,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// Whenever we compress the universe of a placeholder, no existential with
// an already compressed universe can name that placeholder.
for is_existential in [false, true] {
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
// We simply put all regions from the input into the highest
// compressed universe, so we only deal with them at the end.
if !var.is_region() {
@ -298,7 +298,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// We put all regions into a separate universe.
let mut first_region = true;
for var in var_infos.iter_mut() {
for var in var_kinds.iter_mut() {
if var.is_region() {
if first_region {
first_region = false;
@ -309,8 +309,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
}
}
let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
(curr_compressed_uv, var_infos)
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
(curr_compressed_uv, var_kinds)
}
fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
@ -391,7 +391,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
}
};
let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(t, kind);
Ty::new_anon_bound(self.cx(), self.binder_index, var)
}
@ -475,7 +475,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
}
};
let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(r, kind);
Region::new_anon_bound(self.cx(), self.binder_index, var)
}
@ -525,7 +525,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
| ty::ConstKind::Expr(_) => return c.super_fold_with(self),
};
let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind });
let var = self.get_or_insert_bound_var(c, kind);
Const::new_anon_bound(self.cx(), self.binder_index, var)
}

View file

@ -61,7 +61,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
fn instantiate_canonical_var_with_infer(
&self,
cv_info: ty::CanonicalVarInfo<Self::Interner>,
kind: ty::CanonicalVarKind<Self::Interner>,
span: <Self::Interner as Interner>::Span,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> <Self::Interner as Interner>::GenericArg;

View file

@ -83,7 +83,7 @@ where
.cx()
.coroutine_hidden_types(def_id)
.instantiate(cx, args)
.map_bound(|tys| tys.to_vec())),
.map_bound(|bound| bound.types.to_vec())),
ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
@ -249,7 +249,7 @@ where
.cx()
.coroutine_hidden_types(def_id)
.instantiate(ecx.cx(), args)
.map_bound(|tys| tys.to_vec())),
.map_bound(|bound| bound.types.to_vec())),
}
}
@ -327,7 +327,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
// always be called once. It additionally implements `Fn`/`FnMut`
// only if it has no upvars referencing the closure-env lifetime,
// and if the closure kind permits it.
if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() {
if goal_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() {
return Err(NoSolution);
}

View file

@ -360,15 +360,15 @@ where
}
let var_values = delegate.cx().mk_args_from_iter(
response.variables.iter().enumerate().map(|(index, info)| {
if info.universe() != ty::UniverseIndex::ROOT {
response.variables.iter().enumerate().map(|(index, var_kind)| {
if var_kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
delegate.instantiate_canonical_var_with_infer(info, span, |idx| {
delegate.instantiate_canonical_var_with_infer(var_kind, span, |idx| {
prev_universe + idx.index()
})
} else if info.is_existential() {
} else if var_kind.is_existential() {
// As an optimization we sometimes avoid creating a new inference variable here.
//
// All new inference variables we create start out in the current universe of the caller.
@ -379,12 +379,13 @@ where
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
v
} else {
delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe)
delegate
.instantiate_canonical_var_with_infer(var_kind, span, |_| prev_universe)
}
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
original_values[info.expect_placeholder_index()]
original_values[var_kind.expect_placeholder_index()]
}
}),
);

View file

@ -91,7 +91,7 @@ where
/// The variable info for the `var_values`, only used to make an ambiguous response
/// with no constraints.
variables: I::CanonicalVars,
variables: I::CanonicalVarKinds,
/// What kind of goal we're currently computing, see the enum definition
/// for more info.

View file

@ -354,7 +354,7 @@ where
fn response_no_constraints_raw<I: Interner>(
cx: I,
max_universe: ty::UniverseIndex,
variables: I::CanonicalVars,
variables: I::CanonicalVarKinds,
certainty: Certainty,
) -> CanonicalResponse<I> {
ty::Canonical {

View file

@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
};
use rustc_session::parse::feature_err;
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, kw, sym};
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
use rustc_trait_selection::traits::ObligationCtxt;
@ -936,7 +936,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
let attr_str =
&format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
if doc_alias == kw::Empty {
if doc_alias == sym::empty {
tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str });
return;
}
@ -1068,7 +1068,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
let doc_keyword = match meta.value_str() {
Some(value) if value != kw::Empty => value,
Some(value) if value != sym::empty => value,
_ => return self.doc_attr_str_error(meta, "keyword"),
};

View file

@ -707,7 +707,7 @@ fn has_allow_dead_code_or_lang_attr(
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
cg_attrs.contains_extern_indicator()
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
}
}

View file

@ -427,7 +427,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
// `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
// `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
}

View file

@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::UnordSet;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, sym};
use thin_vec::ThinVec;
use tracing::{debug, trace};
@ -157,7 +157,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
};
for fragment in docs {
if fragment.doc == kw::Empty {
if fragment.doc == sym::empty {
continue;
}
@ -177,7 +177,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
///
/// Note: remove the trailing newline where appropriate
pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
if frag.doc == kw::Empty {
if frag.doc == sym::empty {
out.push('\n');
return;
}
@ -514,20 +514,30 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
/// This method does not always work, because markdown bytes don't necessarily match source bytes,
/// like if escapes are used in the string. In this case, it returns `None`.
///
/// This method will return `Some` only if:
/// `markdown` is typically the entire documentation for an item,
/// after combining fragments.
///
/// This method will return `Some` only if one of the following is true:
///
/// - The doc is made entirely from sugared doc comments, which cannot contain escapes
/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal
/// - The doc is entirely from a single doc fragment with a string literal exactly equal to `markdown`.
/// - The doc comes from `include_str!`
/// - The doc includes exactly one substring matching `markdown[md_range]` which is contained in a single doc fragment.
///
/// This function is defined in the compiler so it can be used by
/// both `rustdoc` and `clippy`.
pub fn source_span_for_markdown_range(
tcx: TyCtxt<'_>,
markdown: &str,
md_range: &Range<usize>,
fragments: &[DocFragment],
) -> Option<Span> {
use rustc_span::BytePos;
let map = tcx.sess.source_map();
if let &[fragment] = &fragments
&& fragment.kind == DocFragmentKind::RawDoc
&& let Ok(snippet) = tcx.sess.source_map().span_to_snippet(fragment.span)
&& let Ok(snippet) = map.span_to_snippet(fragment.span)
&& snippet.trim_end() == markdown.trim_end()
&& let Ok(md_range_lo) = u32::try_from(md_range.start)
&& let Ok(md_range_hi) = u32::try_from(md_range.end)
@ -544,10 +554,43 @@ pub fn source_span_for_markdown_range(
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
if !is_all_sugared_doc {
// This case ignores the markdown outside of the range so that it can
// work in cases where the markdown is made from several different
// doc fragments, but the target range does not span across multiple
// fragments.
let mut match_data = None;
let pat = &markdown[md_range.clone()];
// This heirustic doesn't make sense with a zero-sized range.
if pat.is_empty() {
return None;
}
for (i, fragment) in fragments.iter().enumerate() {
if let Ok(snippet) = map.span_to_snippet(fragment.span)
&& let Some(match_start) = snippet.find(pat)
{
// If there is either a match in a previous fragment, or
// multiple matches in this fragment, there is ambiguity.
if match_data.is_none() && !snippet[match_start + 1..].contains(pat) {
match_data = Some((i, match_start));
} else {
// Heirustic produced ambiguity, return nothing.
return None;
}
}
}
if let Some((i, match_start)) = match_data {
let sp = fragments[i].span;
// we need to calculate the span start,
// then use that in our calulations for the span end
let lo = sp.lo() + BytePos(match_start as u32);
return Some(
sp.with_lo(lo).with_hi(lo + BytePos((md_range.end - md_range.start) as u32)),
);
}
return None;
}
let snippet = tcx.sess.source_map().span_to_snippet(span_of_fragments(fragments)?).ok()?;
let snippet = map.span_to_snippet(span_of_fragments(fragments)?).ok()?;
let starting_line = markdown[..md_range.start].matches('\n').count();
let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();

View file

@ -34,17 +34,8 @@ symbols! {
// unnamed method parameters, crate root module, error recovery etc.
// Matching predicates: `is_special`/`is_reserved`
//
// Notes about `kw::Empty`:
// - Its use can blur the lines between "empty symbol" and "no symbol".
// Using `Option<Symbol>` is preferable, where possible, because that
// is unambiguous.
// - For dummy symbols that are never used and absolutely must be
// present, it's better to use `sym::dummy` than `kw::Empty`, because
// it's clearer that it's intended as a dummy value, and more likely
// to be detected if it accidentally does get used.
// tidy-alphabetical-start
DollarCrate: "$crate",
Empty: "",
PathRoot: "{{root}}",
Underscore: "_",
// tidy-alphabetical-end
@ -398,7 +389,6 @@ symbols! {
Wrapping,
Yield,
_DECLS,
_Self,
__D,
__H,
__S,
@ -864,7 +854,7 @@ symbols! {
drop_types_in_const,
dropck_eyepatch,
dropck_parametricity,
dummy: "<!dummy!>", // use this instead of `kw::Empty` for symbols that won't be used
dummy: "<!dummy!>", // use this instead of `sym::empty` for symbols that won't be used
dummy_cgu_name,
dylib,
dyn_compatible_for_dispatch,
@ -883,6 +873,14 @@ symbols! {
emit_enum_variant_arg,
emit_struct,
emit_struct_field,
// Notes about `sym::empty`:
// - It should only be used when it genuinely means "empty symbol". Use
// `Option<Symbol>` when "no symbol" is a possibility.
// - For dummy symbols that are never used and absolutely must be
// present, it's better to use `sym::dummy` than `sym::empty`, because
// it's clearer that it's intended as a dummy value, and more likely
// to be detected if it accidentally does get used.
empty: "",
emscripten_wasm_eh,
enable,
encode,
@ -2362,7 +2360,7 @@ impl Ident {
#[inline]
/// Constructs a new identifier from a symbol and a span.
pub fn new(name: Symbol, span: Span) -> Ident {
debug_assert_ne!(name, kw::Empty);
debug_assert_ne!(name, sym::empty);
Ident { name, span }
}
@ -2584,7 +2582,7 @@ impl Symbol {
}
pub fn is_empty(self) -> bool {
self == kw::Empty
self == sym::empty
}
/// This method is supposed to be used in error messages, so it's expected to be
@ -2593,7 +2591,7 @@ impl Symbol {
/// or edition, so we have to guess the rawness using the global edition.
pub fn to_ident_string(self) -> String {
// Avoid creating an empty identifier, because that asserts in debug builds.
if self == kw::Empty { String::new() } else { Ident::with_dummy_span(self).to_string() }
if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() }
}
}
@ -2773,7 +2771,7 @@ impl Symbol {
/// Returns `true` if this symbol can be a raw identifier.
pub fn can_be_raw(self) -> bool {
self != kw::Empty && self != kw::Underscore && !self.is_path_segment_keyword()
self != sym::empty && self != kw::Underscore && !self.is_path_segment_keyword()
}
/// Was this symbol predefined in the compiler's `symbols!` macro
@ -2823,7 +2821,7 @@ impl Ident {
/// Whether this would be the identifier for a tuple field like `self.0`, as
/// opposed to a named field like `self.thing`.
pub fn is_numeric(self) -> bool {
!self.name.is_empty() && self.as_str().bytes().all(|b| b.is_ascii_digit())
self.as_str().bytes().all(|b| b.is_ascii_digit())
}
}

View file

@ -20,7 +20,7 @@ use rustc_middle::ty::{
self, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, TyCtxt,
TypeVisitable, TypeVisitableExt, UintTy,
};
use rustc_span::kw;
use rustc_span::sym;
pub(super) fn mangle<'tcx>(
tcx: TyCtxt<'tcx>,
@ -902,7 +902,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
print_prefix,
ns,
disambiguated_data.disambiguator as u64,
name.unwrap_or(kw::Empty).as_str(),
name.unwrap_or(sym::empty).as_str(),
)
}

View file

@ -1,6 +1,6 @@
use rustc_abi::Endian;
use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base};
pub(crate) fn target() -> Target {
Target {
@ -16,6 +16,10 @@ pub(crate) fn target() -> Target {
arch: "aarch64".into(),
options: TargetOptions {
features: "+v8a,+outline-atomics".into(),
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
max_atomic_width: Some(128),
stack_probes: StackProbeType::Inline,
mcount: "\u{1}_mcount".into(),

View file

@ -1,6 +1,6 @@
use rustc_abi::Endian;
use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base};
pub(crate) fn target() -> Target {
let mut base = base::linux_gnu::opts();
@ -20,6 +20,10 @@ pub(crate) fn target() -> Target {
options: TargetOptions {
abi: "ilp32".into(),
features: "+v8a,+outline-atomics".into(),
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
stack_probes: StackProbeType::Inline,
mcount: "\u{1}_mcount".into(),
endian: Endian::Big,

View file

@ -1,4 +1,6 @@
use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{
FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base,
};
// See https://developer.android.com/ndk/guides/abis.html#arm64-v8a
// for target ABI requirements.
@ -20,6 +22,10 @@ pub(crate) fn target() -> Target {
// As documented in https://developer.android.com/ndk/guides/cpu-features.html
// the neon (ASIMD) and FP must exist on all android aarch64 targets.
features: "+v8a,+neon,+fp-armv8".into(),
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
stack_probes: StackProbeType::Inline,
supported_sanitizers: SanitizerSet::CFI
| SanitizerSet::HWADDRESS

View file

@ -1,4 +1,6 @@
use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{
FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base,
};
pub(crate) fn target() -> Target {
Target {
@ -14,6 +16,10 @@ pub(crate) fn target() -> Target {
arch: "aarch64".into(),
options: TargetOptions {
features: "+v8a,+outline-atomics".into(),
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
mcount: "\u{1}_mcount".into(),
max_atomic_width: Some(128),
stack_probes: StackProbeType::Inline,

View file

@ -1,4 +1,4 @@
use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base};
pub(crate) fn target() -> Target {
Target {
@ -15,6 +15,10 @@ pub(crate) fn target() -> Target {
options: TargetOptions {
abi: "ilp32".into(),
features: "+v8a,+outline-atomics".into(),
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
max_atomic_width: Some(128),
stack_probes: StackProbeType::Inline,
mcount: "\u{1}_mcount".into(),

View file

@ -1,4 +1,6 @@
use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{
FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base,
};
pub(crate) fn target() -> Target {
let mut base = base::linux_musl::opts();
@ -26,6 +28,12 @@ pub(crate) fn target() -> Target {
pointer_width: 64,
data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(),
options: TargetOptions { mcount: "\u{1}_mcount".into(), ..base },
options: TargetOptions {
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
mcount: "\u{1}_mcount".into(), ..base
},
}
}

View file

@ -1,4 +1,6 @@
use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base};
use crate::spec::{
FramePointer, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base,
};
pub(crate) fn target() -> Target {
let mut base = base::linux_ohos::opts();
@ -16,6 +18,10 @@ pub(crate) fn target() -> Target {
data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
arch: "aarch64".into(),
options: TargetOptions {
// the AAPCS64 expects use of non-leaf frame pointers per
// https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer
// and we tend to encounter interesting bugs in AArch64 unwinding code if we do not
frame_pointer: FramePointer::NonLeaf,
mcount: "\u{1}_mcount".into(),
stack_probes: StackProbeType::Inline,
supported_sanitizers: SanitizerSet::ADDRESS

View file

@ -980,14 +980,16 @@ impl Target {
// the use of soft-float, so all we can do here is some crude hacks.
match &*self.abi {
"softfloat" => {
// This is not fully correct, LLVM actually doesn't let us enforce the softfloat
// ABI properly... see <https://github.com/rust-lang/rust/issues/134375>.
// FIXME: should we forbid "neon" here? But that would be a breaking change.
NOTHING
// LLVM will use float registers when `fp-armv8` is available, e.g. for
// calls to built-ins. The only way to ensure a consistent softfloat ABI
// on aarch64 is to never enable `fp-armv8`, so we enforce that.
// In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the
// feature we have to mark as incompatible.
FeatureConstraints { required: &[], incompatible: &["neon"] }
}
_ => {
// Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled.
// These are Rust feature names and we use "neon" to control both of them.
// `FeatureConstraints` uses Rust feature names, hence only "neon" shows up.
FeatureConstraints { required: &["neon"], incompatible: &[] }
}
}

View file

@ -337,6 +337,8 @@ trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not = expected
.label = unexpected quantity of predicates here
trait_selection_rustc_on_unimplemented_invalid_flag = invalid flag in `on`-clause
.label = expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`
trait_selection_rustc_on_unimplemented_invalid_name = invalid name in `on`-clause
.label = expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`
trait_selection_rustc_on_unimplemented_invalid_predicate = this predicate is invalid
.label = expected one of `any`, `all` or `not` here, not `{$invalid_pred}`
trait_selection_rustc_on_unimplemented_missing_value = this attribute must have a value

View file

@ -841,16 +841,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return None;
};
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
let (closure_def_id, found_args, has_self_borrows) = match *self_ty.kind() {
ty::Closure(def_id, args) => {
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), false)
}
ty::CoroutineClosure(def_id, args) => (
def_id,
args.as_coroutine_closure()
.coroutine_closure_sig()
.map_bound(|sig| sig.tupled_inputs_ty),
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
!args.as_coroutine_closure().tupled_upvars_ty().is_ty_var()
&& args.as_coroutine_closure().has_self_borrows(),
),
_ => return None,
};
@ -884,10 +885,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// If the closure has captures, then perhaps the reason that the trait
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
// if they have captures.
if let Some(by_ref_captures) = by_ref_captures
&& let ty::FnPtr(sig_tys, _) = by_ref_captures.kind()
&& !sig_tys.skip_binder().output().is_unit()
{
if has_self_borrows && expected_kind != ty::ClosureKind::FnOnce {
let mut err = self.dcx().create_err(AsyncClosureNotFn {
span: self.tcx.def_span(closure_def_id),
kind: expected_kind.as_str(),
@ -1503,11 +1501,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
return None;
};
let Ok(Some(ImplSource::UserDefined(impl_data))) = SelectionContext::new(self)
.poly_select(&obligation.with(
self.tcx,
predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)),
))
let trait_ref = self.enter_forall_and_leak_universe(
predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)),
);
let Ok(Some(ImplSource::UserDefined(impl_data))) =
SelectionContext::new(self).select(&obligation.with(self.tcx, trait_ref))
else {
return None;
};

View file

@ -429,7 +429,19 @@ impl<'tcx> OnUnimplementedDirective {
.next()
.ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?;
match OnUnimplementedCondition::parse(cond) {
let generics: Vec<Symbol> = tcx
.generics_of(item_def_id)
.own_params
.iter()
.filter_map(|param| {
if matches!(param.kind, GenericParamDefKind::Lifetime) {
None
} else {
Some(param.name)
}
})
.collect();
match OnUnimplementedCondition::parse(cond, &generics) {
Ok(condition) => Some(condition),
Err(e) => return Err(tcx.dcx().emit_err(e)),
}

View file

@ -26,9 +26,12 @@ impl OnUnimplementedCondition {
})
}
pub(crate) fn parse(input: &MetaItemInner) -> Result<Self, InvalidOnClause> {
pub(crate) fn parse(
input: &MetaItemInner,
generics: &[Symbol],
) -> Result<Self, InvalidOnClause> {
let span = input.span();
let pred = Predicate::parse(input)?;
let pred = Predicate::parse(input, generics)?;
Ok(OnUnimplementedCondition { span, pred })
}
}
@ -52,7 +55,7 @@ enum Predicate {
}
impl Predicate {
fn parse(input: &MetaItemInner) -> Result<Self, InvalidOnClause> {
fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
let meta_item = match input {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(lit) => {
@ -69,10 +72,10 @@ impl Predicate {
match meta_item.kind {
MetaItemKind::List(ref mis) => match predicate.name {
sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis)?)),
sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis)?)),
sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)),
sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)),
sym::not => match &**mis {
[one] => Ok(Predicate::Not(Box::new(Predicate::parse(one)?))),
[one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))),
[first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot {
span: first.span().to(last.span()),
}),
@ -83,7 +86,7 @@ impl Predicate {
}
},
MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => {
let name = Name::parse(predicate);
let name = Name::parse(predicate, generics)?;
let value = FilterFormatString::parse(symbol);
let kv = NameValue { name, value };
Ok(Predicate::Match(kv))
@ -95,8 +98,11 @@ impl Predicate {
}
}
fn parse_sequence(sequence: &[MetaItemInner]) -> Result<Vec<Self>, InvalidOnClause> {
sequence.iter().map(Predicate::parse).collect()
fn parse_sequence(
sequence: &[MetaItemInner],
generics: &[Symbol],
) -> Result<Vec<Self>, InvalidOnClause> {
sequence.iter().map(|item| Predicate::parse(item, generics)).collect()
}
fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
@ -156,14 +162,13 @@ enum Name {
}
impl Name {
fn parse(Ident { name, .. }: Ident) -> Self {
fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
match name {
sym::_Self | kw::SelfUpper => Name::SelfUpper,
sym::from_desugaring => Name::FromDesugaring,
sym::cause => Name::Cause,
// FIXME(mejrs) Perhaps we should start checking that
// this actually is a valid generic parameter?
generic => Name::GenericArg(generic),
kw::SelfUpper => Ok(Name::SelfUpper),
sym::from_desugaring => Ok(Name::FromDesugaring),
sym::cause => Ok(Name::Cause),
generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)),
invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }),
}
}
}

View file

@ -2,8 +2,8 @@ use std::fmt;
use std::ops::Range;
use errors::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::TraitRefPrintSugared;
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_parse_format::{
Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece,
Position,
@ -232,48 +232,16 @@ fn parse_arg<'tcx>(
) -> FormatArg {
let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
| Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx;
let trait_name = tcx.item_ident(*trait_def_id);
let generics = tcx.generics_of(trait_def_id);
let span = slice_span(input_span, arg.position_span.clone());
match arg.position {
// Something like "hello {name}"
Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
// accepted, but deprecated
(Ctx::RustcOnUnimplemented { .. }, sym::_Self) => {
warnings
.push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") });
FormatArg::SelfUpper
}
(
Ctx::RustcOnUnimplemented { .. },
sym::from_desugaring
| sym::crate_local
| sym::direct
| sym::cause
| sym::float
| sym::integer_
| sym::integral,
) => {
warnings.push(FormatWarning::FutureIncompat {
span,
help: String::from("don't use this in a format string"),
});
FormatArg::AsIs(String::new())
}
// Only `#[rustc_on_unimplemented]` can use these
(Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
(Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
(Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
// `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}`
// because that'll be simpler to parse and extend in the future
(Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => {
warnings
.push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") });
FormatArg::This
}
// Any attribute can use these
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
@ -282,7 +250,10 @@ fn parse_arg<'tcx>(
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
generic_param,
) if generics.own_params.iter().any(|param| param.name == generic_param) => {
) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| {
!matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param
}) =>
{
FormatArg::GenericParam { generic_param }
}
@ -375,39 +346,4 @@ pub mod errors {
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
#[help]
pub struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag(trait_selection_ignored_diagnostic_option)]
pub struct IgnoredDiagnosticOption {
pub option_name: &'static str,
#[label]
pub span: Span,
#[label(trait_selection_other_label)]
pub prev_span: Span,
}
impl IgnoredDiagnosticOption {
pub fn maybe_emit_warning<'tcx>(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
new: Option<Span>,
old: Option<Span>,
option_name: &'static str,
) {
if let (Some(new_item), Some(old_item)) = (new, old) {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
new_item,
IgnoredDiagnosticOption {
span: new_item,
prev_span: old_item,
option_name,
},
);
}
}
}
}
}

View file

@ -72,6 +72,13 @@ pub enum InvalidOnClause {
span: Span,
invalid_flag: Symbol,
},
#[diag(trait_selection_rustc_on_unimplemented_invalid_name, code = E0232)]
InvalidName {
#[primary_span]
#[label]
span: Span,
invalid_name: Symbol,
},
}
#[derive(Diagnostic)]

View file

@ -5,7 +5,7 @@ use rustc_hir::LangItem;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
};
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
use rustc_infer::traits::solve::Goal;
@ -190,11 +190,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
fn instantiate_canonical_var_with_infer(
&self,
cv_info: CanonicalVarInfo<'tcx>,
kind: CanonicalVarKind<'tcx>,
span: Span,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> ty::GenericArg<'tcx> {
self.0.instantiate_canonical_var(span, cv_info, universe_map)
self.0.instantiate_canonical_var(span, kind, universe_map)
}
fn add_item_bounds_for_hidden_type(

View file

@ -5,7 +5,7 @@ use rustc_infer::traits::solve::inspect::ProbeKind;
use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal};
use rustc_infer::traits::{
BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause,
PolyTraitObligation, Selection, SelectionError, SelectionResult,
Selection, SelectionError, SelectionResult, TraitObligation,
};
use rustc_macros::extension;
use rustc_middle::{bug, span_bug};
@ -17,7 +17,7 @@ use crate::solve::inspect::{self, ProofTreeInferCtxtExt};
impl<'tcx> InferCtxt<'tcx> {
fn select_in_new_trait_solver(
&self,
obligation: &PolyTraitObligation<'tcx>,
obligation: &TraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
assert!(self.next_trait_solver());

View file

@ -540,10 +540,13 @@ pub fn try_evaluate_const<'tcx>(
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers),
ty::ConstKind::Unevaluated(uv) => {
let opt_anon_const_kind =
(tcx.def_kind(uv.def) == DefKind::AnonConst).then(|| tcx.anon_const_kind(uv.def));
// Postpone evaluation of constants that depend on generic parameters or
// inference variables.
//
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
// to be revealing opaque types here as borrowcheck has not run yet. However,
// CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
// typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
@ -551,65 +554,95 @@ pub fn try_evaluate_const<'tcx>(
//
// FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
// instead of having this logic here
let (args, typing_env) = if tcx.features().generic_const_exprs()
&& uv.has_non_region_infer()
{
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
match tcx.thir_abstract_const(uv.def) {
Ok(Some(ct)) => {
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
if let Err(e) = ct.error_reported() {
return Err(EvaluateConstErr::EvaluationFailure(e));
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
// If the anon const *does* actually use generic parameters or inference variables from
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
return Err(EvaluateConstErr::HasGenericsOrInfers);
} else {
let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args);
let typing_env = infcx
.typing_env(tcx.erase_regions(param_env))
.with_post_analysis_normalized(tcx);
(args, typing_env)
let (args, typing_env) = match opt_anon_const_kind {
// We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system
// completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason
// about if you have to consider gce whatsoever.
Some(ty::AnonConstKind::GCE) => {
if uv.has_non_region_infer() || uv.has_non_region_param() {
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
match tcx.thir_abstract_const(uv.def) {
Ok(Some(ct)) => {
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
if let Err(e) = ct.error_reported() {
return Err(EvaluateConstErr::EvaluationFailure(e));
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
// If the anon const *does* actually use generic parameters or inference variables from
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
return Err(EvaluateConstErr::HasGenericsOrInfers);
} else {
let args =
replace_param_and_infer_args_with_placeholder(tcx, uv.args);
let typing_env = infcx
.typing_env(tcx.erase_regions(param_env))
.with_post_analysis_normalized(tcx);
(args, typing_env)
}
}
Err(_) | Ok(None) => {
let args = GenericArgs::identity_for_item(tcx, uv.def);
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
(args, typing_env)
}
}
}
Err(_) | Ok(None) => {
let args = GenericArgs::identity_for_item(tcx, uv.def);
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
(args, typing_env)
} else {
let typing_env = infcx
.typing_env(tcx.erase_regions(param_env))
.with_post_analysis_normalized(tcx);
(uv.args, typing_env)
}
}
} else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() {
// FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
//
// Diagnostics will sometimes replace the identity args of anon consts in
// array repeat expr counts with inference variables so we have to handle this
// even though it is not something we should ever actually encounter.
//
// Array repeat expr counts are allowed to syntactically use generic parameters
// but must not actually depend on them in order to evalaute successfully. This means
// that it is actually fine to evalaute them in their own environment rather than with
// the actually provided generic arguments.
tcx.dcx().delayed_bug(
"Encountered anon const with inference variable args but no error reported",
);
Some(ty::AnonConstKind::RepeatExprCount) => {
if uv.has_non_region_infer() {
// Diagnostics will sometimes replace the identity args of anon consts in
// array repeat expr counts with inference variables so we have to handle this
// even though it is not something we should ever actually encounter.
//
// Array repeat expr counts are allowed to syntactically use generic parameters
// but must not actually depend on them in order to evalaute successfully. This means
// that it is actually fine to evalaute them in their own environment rather than with
// the actually provided generic arguments.
tcx.dcx().delayed_bug("AnonConst with infer args but no error reported");
}
let args = GenericArgs::identity_for_item(tcx, uv.def);
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
(args, typing_env)
} else {
// FIXME: This codepath is reachable under `associated_const_equality` and in the
// future will be reachable by `min_generic_const_args`. We should handle inference
// variables and generic parameters properly instead of doing nothing.
let typing_env = infcx
.typing_env(tcx.erase_regions(param_env))
.with_post_analysis_normalized(tcx);
(uv.args, typing_env)
// The generic args of repeat expr counts under `min_const_generics` are not supposed to
// affect evaluation of the constant as this would make it a "truly" generic const arg.
// To prevent this we discard all the generic arguments and evalaute with identity args
// and in its own environment instead of the current environment we are normalizing in.
let args = GenericArgs::identity_for_item(tcx, uv.def);
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
(args, typing_env)
}
_ => {
// We are only dealing with "truly" generic/uninferred constants here:
// - GCEConsts have been handled separately
// - Repeat expr count back compat consts have also been handled separately
// So we are free to simply defer evaluation here.
//
// FIXME: This assumes that `args` are normalized which is not necessarily true
//
// Const patterns are converted to type system constants before being
// evaluated. However, we don't care about them here as pattern evaluation
// logic does not go through type system normalization. If it did this would
// be a backwards compatibility problem as we do not enforce "syntactic" non-
// usage of generic parameters like we do here.
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
return Err(EvaluateConstErr::HasGenericsOrInfers);
}
let typing_env = infcx
.typing_env(tcx.erase_regions(param_env))
.with_post_analysis_normalized(tcx);
(uv.args, typing_env)
}
};
let uv = ty::UnevaluatedConst::new(uv.def, args);
let uv = ty::UnevaluatedConst::new(uv.def, args);
let erased_uv = tcx.erase_regions(uv);
use rustc_middle::mir::interpret::ErrorHandled;
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
Ok(Ok(val)) => Ok(ty::Const::new_value(

View file

@ -378,6 +378,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
term: projected_term,
obligations: mut projected_obligations,
})) => {
debug!("opt_normalize_projection_type: progress");
// if projection succeeded, then what we get out of this
// is also non-normalized (consider: it was derived from
// an impl, where-clause etc) and hence we must
@ -408,6 +409,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
Ok(Some(result.value))
}
Ok(Projected::NoProgress(projected_ty)) => {
debug!("opt_normalize_projection_type: no progress");
let result =
Normalized { value: projected_ty, obligations: PredicateObligations::new() };
infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
@ -621,8 +623,17 @@ struct Progress<'tcx> {
}
impl<'tcx> Progress<'tcx> {
fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
Progress { term: Ty::new_error(tcx, guar).into(), obligations: PredicateObligations::new() }
fn error_for_term(
tcx: TyCtxt<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
guar: ErrorGuaranteed,
) -> Self {
let err_term = if alias_term.kind(tcx).is_type() {
Ty::new_error(tcx, guar).into()
} else {
ty::Const::new_error(tcx, guar).into()
};
Progress { term: err_term, obligations: PredicateObligations::new() }
}
fn with_addl_obligations(mut self, mut obligations: PredicateObligations<'tcx>) -> Self {
@ -650,7 +661,11 @@ fn project<'cx, 'tcx>(
}
if let Err(guar) = obligation.predicate.error_reported() {
return Ok(Projected::Progress(Progress::error(selcx.tcx(), guar)));
return Ok(Projected::Progress(Progress::error_for_term(
selcx.tcx(),
obligation.predicate,
guar,
)));
}
let mut candidates = ProjectionCandidateSet::None;
@ -1965,7 +1980,13 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let param_env = obligation.param_env;
let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_term) => assoc_term,
Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
Err(guar) => {
return Ok(Projected::Progress(Progress::error_for_term(
tcx,
obligation.predicate,
guar,
)));
}
};
// This means that the impl is missing a definition for the

View file

@ -265,9 +265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
if self.infcx.next_trait_solver() {
return self.infcx.select_in_new_trait_solver(obligation);
}
assert!(!self.infcx.next_trait_solver());
let candidate = match self.select_from_obligation(obligation) {
Err(SelectionError::Overflow(OverflowError::Canonical)) => {
@ -299,6 +297,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &TraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
if self.infcx.next_trait_solver() {
return self.infcx.select_in_new_trait_solver(obligation);
}
self.poly_select(&Obligation {
cause: obligation.cause.clone(),
param_env: obligation.param_env,
@ -2866,7 +2868,7 @@ fn rebind_coroutine_witness_types<'tcx>(
let shifted_coroutine_types =
tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder());
ty::Binder::bind_with_vars(
ty::EarlyBinder::bind(shifted_coroutine_types.to_vec()).instantiate(tcx, args),
ty::EarlyBinder::bind(shifted_coroutine_types.types.to_vec()).instantiate(tcx, args),
tcx.mk_bound_variable_kinds_from_iter(
bound_vars.iter().chain(bound_coroutine_types.bound_vars()),
),

View file

@ -0,0 +1,37 @@
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, TyCtxt, fold_regions};
/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state. This properly replaces
/// `ReErased` with new existential bound lifetimes.
pub(crate) fn coroutine_hidden_types<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
let coroutine_layout = tcx.mir_coroutine_witnesses(def_id);
let mut vars = vec![];
let bound_tys = tcx.mk_type_list_from_iter(
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| {
let ty = fold_regions(tcx, decl.ty, |re, debruijn| {
assert_eq!(re, tcx.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
tcx,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty
}),
);
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
ty::CoroutineWitnessTypes { types: bound_tys },
tcx.mk_bound_variable_kinds(&vars),
))
}

View file

@ -5,6 +5,7 @@
// tidy-alphabetical-end
mod codegen;
mod coroutine_witnesses;
mod dropck_outlives;
mod evaluate_obligation;
mod implied_outlives_bounds;
@ -24,4 +25,5 @@ pub fn provide(p: &mut Providers) {
normalize_erasing_regions::provide(p);
type_op::provide(p);
p.codegen_select_candidate = codegen::codegen_select_candidate;
p.coroutine_hidden_types = coroutine_witnesses::coroutine_hidden_types;
}

View file

@ -41,7 +41,7 @@ pub struct CanonicalQueryInput<I: Interner, V> {
pub struct Canonical<I: Interner, V> {
pub value: V,
pub max_universe: UniverseIndex,
pub variables: I::CanonicalVars,
pub variables: I::CanonicalVarKinds,
}
impl<I: Interner, V> Canonical<I, V> {
@ -89,63 +89,6 @@ impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
/// a copy of the canonical value in some other inference context,
/// with fresh inference variables replacing the canonical values.
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
)]
pub struct CanonicalVarInfo<I: Interner> {
pub kind: CanonicalVarKind<I>,
}
impl<I: Interner> CanonicalVarInfo<I> {
pub fn universe(self) -> UniverseIndex {
self.kind.universe()
}
#[must_use]
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarInfo<I> {
CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) }
}
pub fn is_existential(&self) -> bool {
match self.kind {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn is_region(&self) -> bool {
match self.kind {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(),
}
}
}
/// Describes the "kind" of the canonical variable. This is a "kind"
/// in the type-theory sense of the term -- i.e., a "meta" type system
/// that analyzes type-like values.
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
@ -214,6 +157,39 @@ impl<I: Interner> CanonicalVarKind<I> {
}
}
}
pub fn is_existential(self) -> bool {
match self {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn is_region(self) -> bool {
match self {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self {
CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(),
}
}
}
/// Rust actually has more than one category of type variables;
@ -306,11 +282,11 @@ impl<I: Interner> CanonicalVarValues<I> {
// Given a list of canonical variables, construct a set of values which are
// the identity response.
pub fn make_identity(cx: I, infos: I::CanonicalVars) -> CanonicalVarValues<I> {
pub fn make_identity(cx: I, infos: I::CanonicalVarKinds) -> CanonicalVarValues<I> {
CanonicalVarValues {
var_values: cx.mk_args_from_iter(infos.iter().enumerate().map(
|(i, info)| -> I::GenericArg {
match info.kind {
|(i, kind)| -> I::GenericArg {
match kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()

View file

@ -64,13 +64,16 @@ pub trait Interner:
+ TypeVisitable<Self>
+ SliceLike<Item = Self::LocalDefId>;
type CanonicalVars: Copy
type CanonicalVarKinds: Copy
+ Debug
+ Hash
+ Eq
+ SliceLike<Item = ty::CanonicalVarInfo<Self>>
+ SliceLike<Item = ty::CanonicalVarKind<Self>>
+ Default;
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
fn mk_canonical_var_kinds(
self,
kinds: &[ty::CanonicalVarKind<Self>],
) -> Self::CanonicalVarKinds;
type ExternalConstraints: Copy
+ Debug
@ -210,7 +213,7 @@ pub trait Interner:
fn coroutine_hidden_types(
self,
def_id: Self::DefId,
) -> ty::EarlyBinder<Self, ty::Binder<Self, Self::Tys>>;
) -> ty::EarlyBinder<Self, ty::Binder<Self, ty::CoroutineWitnessTypes<Self>>>;
fn fn_sig(
self,

View file

@ -1163,3 +1163,13 @@ pub struct FnHeader<I: Interner> {
pub safety: I::Safety,
pub abi: I::Abi,
}
#[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
pub struct CoroutineWitnessTypes<I: Interner> {
pub types: I::Tys,
}

View file

@ -588,7 +588,7 @@ impl<T, const N: usize> [T; N] {
/// Returns a mutable slice containing the entire array. Equivalent to
/// `&mut s[..]`.
#[stable(feature = "array_as_slice", since = "1.57.0")]
#[rustc_const_unstable(feature = "const_array_as_mut_slice", issue = "133333")]
#[rustc_const_stable(feature = "const_array_as_mut_slice", since = "CURRENT_RUSTC_VERSION")]
pub const fn as_mut_slice(&mut self) -> &mut [T] {
self
}

View file

@ -575,7 +575,7 @@ pub trait Into<T>: Sized {
#[rustc_diagnostic_item = "From"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(on(
all(_Self = "&str", T = "alloc::string::String"),
all(Self = "&str", T = "alloc::string::String"),
note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix",
))]
#[doc(search_unbox)]

View file

@ -511,13 +511,8 @@ impl CStr {
/// # Examples
///
/// ```
/// use std::ffi::CStr;
///
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap();
/// assert_eq!(cstr.count_bytes(), 3);
///
/// let cstr = CStr::from_bytes_with_nul(b"\0").unwrap();
/// assert_eq!(cstr.count_bytes(), 0);
/// assert_eq!(c"foo".count_bytes(), 3);
/// assert_eq!(c"".count_bytes(), 0);
/// ```
#[inline]
#[must_use]
@ -533,19 +528,8 @@ impl CStr {
/// # Examples
///
/// ```
/// use std::ffi::CStr;
/// # use std::ffi::FromBytesWithNulError;
///
/// # fn main() { test().unwrap(); }
/// # fn test() -> Result<(), FromBytesWithNulError> {
/// let cstr = CStr::from_bytes_with_nul(b"foo\0")?;
/// assert!(!cstr.is_empty());
///
/// let empty_cstr = CStr::from_bytes_with_nul(b"\0")?;
/// assert!(empty_cstr.is_empty());
/// assert!(!c"foo".is_empty());
/// assert!(c"".is_empty());
/// # Ok(())
/// # }
/// ```
#[inline]
#[stable(feature = "cstr_is_empty", since = "1.71.0")]
@ -569,10 +553,7 @@ impl CStr {
/// # Examples
///
/// ```
/// use std::ffi::CStr;
///
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
/// assert_eq!(cstr.to_bytes(), b"foo");
/// assert_eq!(c"foo".to_bytes(), b"foo");
/// ```
#[inline]
#[must_use = "this returns the result of the operation, \
@ -598,10 +579,7 @@ impl CStr {
/// # Examples
///
/// ```
/// use std::ffi::CStr;
///
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
/// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0");
/// assert_eq!(c"foo".to_bytes_with_nul(), b"foo\0");
/// ```
#[inline]
#[must_use = "this returns the result of the operation, \
@ -623,10 +601,8 @@ impl CStr {
///
/// ```
/// #![feature(cstr_bytes)]
/// use std::ffi::CStr;
///
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
/// assert!(cstr.bytes().eq(*b"foo"));
/// assert!(c"foo".bytes().eq(*b"foo"));
/// ```
#[inline]
#[unstable(feature = "cstr_bytes", issue = "112115")]
@ -645,10 +621,7 @@ impl CStr {
/// # Examples
///
/// ```
/// use std::ffi::CStr;
///
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
/// assert_eq!(cstr.to_str(), Ok("foo"));
/// assert_eq!(c"foo".to_str(), Ok("foo"));
/// ```
#[stable(feature = "cstr_to_str", since = "1.4.0")]
#[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")]

View file

@ -856,10 +856,10 @@ impl Display for Arguments<'_> {
on(
crate_local,
label = "`{Self}` cannot be formatted using `{{:?}}`",
note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {Debug} for {Self}`"
note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {This} for {Self}`"
),
message = "`{Self}` doesn't implement `{Debug}`",
label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`"
message = "`{Self}` doesn't implement `{This}`",
label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`"
)]
#[doc(alias = "{:?}")]
#[rustc_diagnostic_item = "Debug"]
@ -969,12 +969,12 @@ pub use macros::Debug;
/// ```
#[rustc_on_unimplemented(
on(
any(_Self = "std::path::Path", _Self = "std::path::PathBuf"),
any(Self = "std::path::Path", Self = "std::path::PathBuf"),
label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it",
note = "call `.display()` or `.to_string_lossy()` to safely print paths, \
as they may contain non-Unicode data"
),
message = "`{Self}` doesn't implement `{Display}`",
message = "`{Self}` doesn't implement `{This}`",
label = "`{Self}` cannot be formatted with the default formatter",
note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead"
)]

View file

@ -231,7 +231,7 @@ pub const unsafe fn assert_unchecked(cond: bool) {
///
/// # Examples
///
/// ```
/// ```ignore-wasm
/// use std::sync::atomic::{AtomicBool, Ordering};
/// use std::sync::Arc;
/// use std::{hint, thread};

View file

@ -2296,12 +2296,6 @@ pub fn round_ties_even_f16(x: f16) -> f16;
#[rustc_nounwind]
pub fn round_ties_even_f32(x: f32) -> f32;
/// Provided for compatibility with stdarch. DO NOT USE.
#[inline(always)]
pub unsafe fn rintf32(x: f32) -> f32 {
round_ties_even_f32(x)
}
/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number with an even
/// least significant digit.
///
@ -2311,12 +2305,6 @@ pub unsafe fn rintf32(x: f32) -> f32 {
#[rustc_nounwind]
pub fn round_ties_even_f64(x: f64) -> f64;
/// Provided for compatibility with stdarch. DO NOT USE.
#[inline(always)]
pub unsafe fn rintf64(x: f64) -> f64 {
round_ties_even_f64(x)
}
/// Returns the nearest integer to an `f128`. Rounds half-way cases to the number with an even
/// least significant digit.
///

View file

@ -97,32 +97,32 @@ use super::TrustedLen;
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(
on(
_Self = "&[{A}]",
Self = "&[{A}]",
message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere",
label = "try explicitly collecting into a `Vec<{A}>`",
),
on(
all(A = "{integer}", any(_Self = "&[{integral}]",)),
all(A = "{integer}", any(Self = "&[{integral}]",)),
message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere",
label = "try explicitly collecting into a `Vec<{A}>`",
),
on(
_Self = "[{A}]",
Self = "[{A}]",
message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
label = "try explicitly collecting into a `Vec<{A}>`",
),
on(
all(A = "{integer}", any(_Self = "[{integral}]",)),
all(A = "{integer}", any(Self = "[{integral}]",)),
message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
label = "try explicitly collecting into a `Vec<{A}>`",
),
on(
_Self = "[{A}; _]",
Self = "[{A}; _]",
message = "an array of type `{Self}` cannot be built directly from an iterator",
label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
),
on(
all(A = "{integer}", any(_Self = "[{integral}; _]",)),
all(A = "{integer}", any(Self = "[{integral}; _]",)),
message = "an array of type `{Self}` cannot be built directly from an iterator",
label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
),
@ -239,41 +239,38 @@ pub trait FromIterator<A>: Sized {
#[rustc_diagnostic_item = "IntoIterator"]
#[rustc_on_unimplemented(
on(
_Self = "core::ops::range::RangeTo<Idx>",
Self = "core::ops::range::RangeTo<Idx>",
label = "if you meant to iterate until a value, add a starting value",
note = "`..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a \
bounded `Range`: `0..end`"
),
on(
_Self = "core::ops::range::RangeToInclusive<Idx>",
Self = "core::ops::range::RangeToInclusive<Idx>",
label = "if you meant to iterate until a value (including it), add a starting value",
note = "`..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant \
to have a bounded `RangeInclusive`: `0..=end`"
),
on(
_Self = "[]",
Self = "[]",
label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`"
),
on(_Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"),
on(Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"),
on(
_Self = "alloc::vec::Vec<T, A>",
Self = "alloc::vec::Vec<T, A>",
label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`"
),
on(Self = "&str", label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`"),
on(
_Self = "&str",
Self = "alloc::string::String",
label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`"
),
on(
_Self = "alloc::string::String",
label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`"
),
on(
_Self = "{integral}",
Self = "{integral}",
note = "if you want to iterate between `start` until a value `end`, use the exclusive range \
syntax `start..end` or the inclusive range syntax `start..=end`"
),
on(
_Self = "{float}",
Self = "{float}",
note = "if you want to iterate between `start` until a value `end`, use the exclusive range \
syntax `start..end` or the inclusive range syntax `start..=end`"
),

View file

@ -22,11 +22,11 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator<Item = ()>) {}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(
on(
_Self = "core::ops::range::RangeTo<Idx>",
Self = "core::ops::range::RangeTo<Idx>",
note = "you might have meant to use a bounded `Range`"
),
on(
_Self = "core::ops::range::RangeToInclusive<Idx>",
Self = "core::ops::range::RangeToInclusive<Idx>",
note = "you might have meant to use a bounded `RangeInclusive`"
),
label = "`{Self}` is not an iterator",

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