Make compare_impl_item into a query
This commit is contained in:
parent
e48ddd8a0b
commit
a3623f20ae
16 changed files with 84 additions and 228 deletions
|
@ -33,7 +33,7 @@ use tracing::{debug, instrument};
|
||||||
use ty::TypingMode;
|
use ty::TypingMode;
|
||||||
use {rustc_attr as attr, rustc_hir as hir};
|
use {rustc_attr as attr, rustc_hir as hir};
|
||||||
|
|
||||||
use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_impl_ty};
|
use super::compare_impl_item::check_type_bounds;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::check::intrinsicck::InlineAsmCtxt;
|
use crate::check::intrinsicck::InlineAsmCtxt;
|
||||||
|
|
||||||
|
@ -1044,20 +1044,8 @@ fn check_impl_items_against_trait<'tcx>(
|
||||||
tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait");
|
tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match ty_impl_item.kind {
|
|
||||||
ty::AssocKind::Const => {
|
let _ = tcx.ensure().compare_impl_item(impl_item.expect_local());
|
||||||
tcx.ensure().compare_impl_const((
|
|
||||||
impl_item.expect_local(),
|
|
||||||
ty_impl_item.trait_item_def_id.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ty::AssocKind::Fn => {
|
|
||||||
compare_impl_method(tcx, ty_impl_item, ty_trait_item, trait_ref);
|
|
||||||
}
|
|
||||||
ty::AssocKind::Type => {
|
|
||||||
compare_impl_ty(tcx, ty_impl_item, ty_trait_item, trait_ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check_specialization_validity(
|
check_specialization_validity(
|
||||||
tcx,
|
tcx,
|
||||||
|
|
|
@ -35,6 +35,24 @@ use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
|
||||||
|
|
||||||
mod refine;
|
mod refine;
|
||||||
|
|
||||||
|
/// Call the query `tcx.compare_impl_item()` directly instead.
|
||||||
|
pub(super) fn compare_impl_item(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
impl_item_def_id: LocalDefId,
|
||||||
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
|
let impl_item = tcx.associated_item(impl_item_def_id);
|
||||||
|
let trait_item = tcx.associated_item(impl_item.trait_item_def_id.unwrap());
|
||||||
|
let impl_trait_ref =
|
||||||
|
tcx.impl_trait_ref(impl_item.container_id(tcx)).unwrap().instantiate_identity();
|
||||||
|
debug!(?impl_trait_ref);
|
||||||
|
|
||||||
|
match impl_item.kind {
|
||||||
|
ty::AssocKind::Fn => compare_impl_method(tcx, impl_item, trait_item, impl_trait_ref),
|
||||||
|
ty::AssocKind::Type => compare_impl_ty(tcx, impl_item, trait_item, impl_trait_ref),
|
||||||
|
ty::AssocKind::Const => compare_impl_const(tcx, impl_item, trait_item, impl_trait_ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that a method from an impl conforms to the signature of
|
/// Checks that a method from an impl conforms to the signature of
|
||||||
/// the same method as declared in the trait.
|
/// the same method as declared in the trait.
|
||||||
///
|
///
|
||||||
|
@ -44,22 +62,21 @@ mod refine;
|
||||||
/// - `trait_m`: the method in the trait
|
/// - `trait_m`: the method in the trait
|
||||||
/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
|
/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
|
||||||
#[instrument(level = "debug", skip(tcx))]
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
pub(super) fn compare_impl_method<'tcx>(
|
fn compare_impl_method<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl_m: ty::AssocItem,
|
impl_m: ty::AssocItem,
|
||||||
trait_m: ty::AssocItem,
|
trait_m: ty::AssocItem,
|
||||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||||
) {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
let _: Result<_, ErrorGuaranteed> = try {
|
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
|
||||||
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
|
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
|
||||||
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
|
refine::check_refining_return_position_impl_trait_in_trait(
|
||||||
refine::check_refining_return_position_impl_trait_in_trait(
|
tcx,
|
||||||
tcx,
|
impl_m,
|
||||||
impl_m,
|
trait_m,
|
||||||
trait_m,
|
impl_trait_ref,
|
||||||
impl_trait_ref,
|
);
|
||||||
);
|
Ok(())
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks a bunch of different properties of the impl/trait methods for
|
/// Checks a bunch of different properties of the impl/trait methods for
|
||||||
|
@ -1721,17 +1738,12 @@ fn compare_generic_param_kinds<'tcx>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use `tcx.compare_impl_const` instead
|
fn compare_impl_const<'tcx>(
|
||||||
pub(super) fn compare_impl_const_raw(
|
tcx: TyCtxt<'tcx>,
|
||||||
tcx: TyCtxt<'_>,
|
impl_const_item: ty::AssocItem,
|
||||||
(impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
|
trait_const_item: ty::AssocItem,
|
||||||
|
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
let impl_const_item = tcx.associated_item(impl_const_item_def);
|
|
||||||
let trait_const_item = tcx.associated_item(trait_const_item_def);
|
|
||||||
let impl_trait_ref =
|
|
||||||
tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
|
|
||||||
debug!(?impl_trait_ref);
|
|
||||||
|
|
||||||
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
|
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
|
||||||
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
|
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
|
||||||
check_region_bounds_on_impl_item(tcx, impl_const_item, trait_const_item, false)?;
|
check_region_bounds_on_impl_item(tcx, impl_const_item, trait_const_item, false)?;
|
||||||
|
@ -1862,19 +1874,17 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(tcx))]
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
pub(super) fn compare_impl_ty<'tcx>(
|
fn compare_impl_ty<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl_ty: ty::AssocItem,
|
impl_ty: ty::AssocItem,
|
||||||
trait_ty: ty::AssocItem,
|
trait_ty: ty::AssocItem,
|
||||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||||
) {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
let _: Result<(), ErrorGuaranteed> = try {
|
compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
|
||||||
compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
|
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
|
||||||
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
|
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
|
||||||
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
|
compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?;
|
||||||
compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?;
|
check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)
|
||||||
check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)?;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The equivalent of [compare_method_predicate_entailment], but for associated types
|
/// The equivalent of [compare_method_predicate_entailment], but for associated types
|
||||||
|
|
|
@ -108,7 +108,7 @@ pub fn provide(providers: &mut Providers) {
|
||||||
adt_async_destructor,
|
adt_async_destructor,
|
||||||
region_scope_tree,
|
region_scope_tree,
|
||||||
collect_return_position_impl_trait_in_trait_tys,
|
collect_return_position_impl_trait_in_trait_tys,
|
||||||
compare_impl_const: compare_impl_item::compare_impl_const_raw,
|
compare_impl_item: compare_impl_item::compare_impl_item,
|
||||||
check_coroutine_obligations: check::check_coroutine_obligations,
|
check_coroutine_obligations: check::check_coroutine_obligations,
|
||||||
..*providers
|
..*providers
|
||||||
};
|
};
|
||||||
|
|
|
@ -2311,10 +2311,13 @@ rustc_queries! {
|
||||||
desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 }
|
desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
query compare_impl_const(
|
/// This takes the def-id of an associated item from a impl of a trait,
|
||||||
key: (LocalDefId, DefId)
|
/// and checks its validity against the trait item it corresponds to.
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
///
|
||||||
desc { |tcx| "checking assoc const `{}` has the same type as trait item", tcx.def_path_str(key.0) }
|
/// Any other def id will ICE.
|
||||||
|
query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
|
||||||
|
desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) }
|
||||||
|
ensure_forwards_result_if_red
|
||||||
}
|
}
|
||||||
|
|
||||||
query deduced_param_attrs(def_id: DefId) -> &'tcx [ty::DeducedParamAttrs] {
|
query deduced_param_attrs(def_id: DefId) -> &'tcx [ty::DeducedParamAttrs] {
|
||||||
|
|
|
@ -216,15 +216,13 @@ fn resolve_associated_item<'tcx>(
|
||||||
|
|
||||||
let args = tcx.erase_regions(args);
|
let args = tcx.erase_regions(args);
|
||||||
|
|
||||||
// Check if we just resolved an associated `const` declaration from
|
// We check that the impl item is compatible with the trait item
|
||||||
// a `trait` to an associated `const` definition in an `impl`, where
|
// because otherwise we may ICE in const eval due to type mismatches,
|
||||||
// the definition in the `impl` has the wrong type (for which an
|
// signature incompatibilities, etc.
|
||||||
// error has already been/will be emitted elsewhere).
|
if trait_item_id != leaf_def.item.def_id
|
||||||
if leaf_def.item.kind == ty::AssocKind::Const
|
|
||||||
&& trait_item_id != leaf_def.item.def_id
|
|
||||||
&& let Some(leaf_def_item) = leaf_def.item.def_id.as_local()
|
&& let Some(leaf_def_item) = leaf_def.item.def_id.as_local()
|
||||||
{
|
{
|
||||||
tcx.compare_impl_const((leaf_def_item, trait_item_id))?;
|
tcx.ensure().compare_impl_item(leaf_def_item)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ty::Instance::new(leaf_def.item.def_id, args))
|
Some(ty::Instance::new(leaf_def.item.def_id, args))
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
//@ known-bug: #119701
|
|
||||||
#![feature(const_trait_impl, generic_const_exprs)]
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _ = process::<()>([()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process<T: const Trait>() -> [(); T::make(2)] {
|
|
||||||
input
|
|
||||||
}
|
|
||||||
|
|
||||||
#[const_trait]
|
|
||||||
trait Trait {
|
|
||||||
fn make(input: u8) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl const Trait for () {
|
|
||||||
fn make(input: usize) -> usize {
|
|
||||||
input / 2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
//@ known-bug: #121127
|
|
||||||
//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes -C debuginfo=2
|
|
||||||
// Note that as of PR#123949 this only crashes with debuginfo enabled
|
|
||||||
|
|
||||||
#![feature(specialization)]
|
|
||||||
|
|
||||||
pub trait Foo {
|
|
||||||
fn abc() -> u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Marker {}
|
|
||||||
|
|
||||||
impl<T> Foo for T {
|
|
||||||
default fn abc(f: fn(&T), t: &T) -> u32 {
|
|
||||||
16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Marker> Foo for T {
|
|
||||||
fn def() -> u32 {
|
|
||||||
Self::abc()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
//@ known-bug: #121411
|
|
||||||
#![feature(const_trait_impl)]
|
|
||||||
|
|
||||||
#[const_trait]
|
|
||||||
trait Foo {
|
|
||||||
fn into_iter(&self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl const Foo for () {
|
|
||||||
fn into_iter(a: u32, b: u32) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _: () = Foo::into_iter(&());
|
|
|
@ -1,16 +0,0 @@
|
||||||
//@ known-bug: rust-lang/rust#129075
|
|
||||||
//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes
|
|
||||||
|
|
||||||
struct Foo<T>([T; 2]);
|
|
||||||
|
|
||||||
impl<T: Default + Copy> Default for Foo<T> {
|
|
||||||
fn default(&mut self) -> Self {
|
|
||||||
Foo([Default::default(); 2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field_array() {
|
|
||||||
let a: i32;
|
|
||||||
let b;
|
|
||||||
Foo([a, b]) = Default::default();
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
//@ known-bug: rust-lang/rust#129127
|
|
||||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir -Zcross-crate-inline-threshold=always
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Rows<'a>();
|
|
||||||
|
|
||||||
impl<'a> Iterator for Rows<'a> {
|
|
||||||
type Item = ();
|
|
||||||
|
|
||||||
fn next() -> Option<Self::Item> {
|
|
||||||
let mut rows = Rows();
|
|
||||||
rows.map(|row| row).next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut rows = Rows();
|
|
||||||
rows.next();
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
//@ known-bug: rust-lang/rust#129214
|
|
||||||
//@ compile-flags: -Zvalidate-mir -Copt-level=3 --crate-type=lib
|
|
||||||
|
|
||||||
trait to_str {}
|
|
||||||
|
|
||||||
trait map<T> {
|
|
||||||
fn map<U, F>(&self, f: F) -> Vec<U>
|
|
||||||
where
|
|
||||||
F: FnMut(&Box<usize>) -> U;
|
|
||||||
}
|
|
||||||
impl<T> map<T> for Vec<T> {
|
|
||||||
fn map<U, F>(&self, mut f: F) -> Vec<U>
|
|
||||||
where
|
|
||||||
F: FnMut(&T) -> U,
|
|
||||||
{
|
|
||||||
let mut r = Vec::new();
|
|
||||||
for i in self {
|
|
||||||
r.push(f(i));
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn foo<U, T: map<U>>(x: T) -> Vec<String> {
|
|
||||||
x.map(|_e| "hi".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
assert_eq!(foo(vec![1]), ["hi".to_string()]);
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
//@ known-bug: #131294
|
|
||||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir -Zcross-crate-inline-threshold=always
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust/issues/131294#issuecomment-2395088049 second comment
|
|
||||||
struct Rows;
|
|
||||||
|
|
||||||
impl Iterator for Rows {
|
|
||||||
type Item = String;
|
|
||||||
|
|
||||||
fn next() -> Option<String> {
|
|
||||||
let args = format_args!("Hello world");
|
|
||||||
|
|
||||||
{
|
|
||||||
match args.as_str() {
|
|
||||||
Some(t) => t.to_owned(),
|
|
||||||
None => String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Rows.next();
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
//@ known-bug: #131294
|
|
||||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir -Zcross-crate-inline-threshold=always
|
|
||||||
|
|
||||||
struct Rows;
|
|
||||||
|
|
||||||
impl Iterator for Rows {
|
|
||||||
type Item = String;
|
|
||||||
|
|
||||||
fn next() -> Option<Self::Item> {
|
|
||||||
std::fmt::format(format_args!("Hello world")).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Rows.next();
|
|
||||||
}
|
|
|
@ -29,11 +29,11 @@ note: ...which requires computing candidate for `<LazyUpdim<'_, T, <T as TensorD
|
||||||
LL | trait TensorDimension {
|
LL | trait TensorDimension {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: ...which again requires resolving instance `<LazyUpdim<'_, T, <T as TensorDimension>::DIM, DIM> as TensorDimension>::DIM`, completing the cycle
|
= note: ...which again requires resolving instance `<LazyUpdim<'_, T, <T as TensorDimension>::DIM, DIM> as TensorDimension>::DIM`, completing the cycle
|
||||||
note: cycle used when checking that `<impl at $DIR/issue-83765.rs:56:1: 56:97>` is well-formed
|
note: cycle used when checking assoc item `<impl at $DIR/issue-83765.rs:56:1: 56:97>::bget` is compatible with trait definition
|
||||||
--> $DIR/issue-83765.rs:56:1
|
--> $DIR/issue-83765.rs:58:5
|
||||||
|
|
|
|
||||||
LL | impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> {
|
LL | fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||||
|
|
||||||
error[E0308]: method not compatible with trait
|
error[E0308]: method not compatible with trait
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//@ known-bug: #112623
|
// Make sure we don't ICE when evaluating a trait whose impl has a bad signature.
|
||||||
|
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ struct FortyTwo;
|
||||||
|
|
||||||
impl const Value for FortyTwo {
|
impl const Value for FortyTwo {
|
||||||
fn value() -> i64 {
|
fn value() -> i64 {
|
||||||
|
//~^ ERROR method `value` has an incompatible type for trait
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
}
|
}
|
21
tests/ui/traits/const-traits/eval-bad-signature.stderr
Normal file
21
tests/ui/traits/const-traits/eval-bad-signature.stderr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
error[E0053]: method `value` has an incompatible type for trait
|
||||||
|
--> $DIR/eval-bad-signature.rs:17:19
|
||||||
|
|
|
||||||
|
LL | fn value() -> i64 {
|
||||||
|
| ^^^ expected `u32`, found `i64`
|
||||||
|
|
|
||||||
|
note: type in trait
|
||||||
|
--> $DIR/eval-bad-signature.rs:7:19
|
||||||
|
|
|
||||||
|
LL | fn value() -> u32;
|
||||||
|
| ^^^
|
||||||
|
= note: expected signature `fn() -> u32`
|
||||||
|
found signature `fn() -> i64`
|
||||||
|
help: change the output type to match the trait
|
||||||
|
|
|
||||||
|
LL | fn value() -> u32 {
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0053`.
|
Loading…
Add table
Add a link
Reference in a new issue