Auto merge of #110978 - Dylan-DPC:rollup-xclzwax, r=Dylan-DPC

Rollup of 6 pull requests

Successful merges:

 - #110614 (Clear response values for overflow in new solver)
 - #110894 (Bump libffi-sys to 2.3.0)
 - #110932 (include source error for LoadLibraryExW)
 - #110958 (Make sure that some stdlib method signatures aren't accidental refinements)
 - #110962 (Make drop_flags an IndexVec.)
 - #110965 (Don't duplicate anonymous lifetimes for async fn in traits)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-04-29 06:17:37 +00:00
commit af2c7e0f9b
21 changed files with 389 additions and 62 deletions

View File

@ -1956,9 +1956,9 @@ dependencies = [
[[package]]
name = "libffi-sys"
version = "2.2.1"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc65067b78c0fc069771e8b9a9e02df71e08858bec92c1f101377c67b9dca7c7"
checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c"
dependencies = [
"cc",
]

View File

@ -27,6 +27,7 @@ use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::{PanicStrategy, TargetTriple};
use proc_macro::bridge::client::ProcMacro;
use std::error::Error;
use std::ops::Fn;
use std::path::Path;
use std::time::Duration;
@ -1094,5 +1095,12 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
}
debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);
Err(format!("{} (retried {} times)", last_error.unwrap(), max_attempts))
let last_error = last_error.unwrap();
let message = if let Some(src) = last_error.source() {
format!("{last_error} ({src}) (retried {max_attempts} times)")
} else {
format!("{last_error} (retried {max_attempts} times)")
};
Err(message)
}

View File

@ -1,7 +1,7 @@
use crate::deref_separator::deref_finder;
use crate::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};
@ -84,12 +84,13 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
let reachable = traversal::reachable_as_bitset(body);
let drop_flags = IndexVec::from_elem(None, &env.move_data.move_paths);
ElaborateDropsCtxt {
tcx,
body,
env: &env,
init_data: InitializationData { inits, uninits },
drop_flags: Default::default(),
drop_flags,
patch: MirPatch::new(body),
un_derefer: un_derefer,
reachable,
@ -293,7 +294,7 @@ struct ElaborateDropsCtxt<'a, 'tcx> {
body: &'a Body<'tcx>,
env: &'a MoveDataParamEnv<'tcx>,
init_data: InitializationData<'a, 'tcx>,
drop_flags: FxHashMap<MovePathIndex, Local>,
drop_flags: IndexVec<MovePathIndex, Option<Local>>,
patch: MirPatch<'tcx>,
un_derefer: UnDerefer<'tcx>,
reachable: BitSet<BasicBlock>,
@ -312,11 +313,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
let tcx = self.tcx;
let patch = &mut self.patch;
debug!("create_drop_flag({:?})", self.body.span);
self.drop_flags.entry(index).or_insert_with(|| patch.new_internal(tcx.types.bool, span));
self.drop_flags[index].get_or_insert_with(|| patch.new_internal(tcx.types.bool, span));
}
fn drop_flag(&mut self, index: MovePathIndex) -> Option<Place<'tcx>> {
self.drop_flags.get(&index).map(|t| Place::from(*t))
self.drop_flags[index].map(Place::from)
}
/// create a patch that elaborates all drops in the input
@ -463,7 +464,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
}
fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) {
if let Some(&flag) = self.drop_flags.get(&path) {
if let Some(flag) = self.drop_flags[path] {
let span = self.patch.source_info_for_location(self.body, loc).span;
let val = self.constant_bool(span, val.value());
self.patch.add_assign(loc, Place::from(flag), val);
@ -474,7 +475,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
let loc = Location::START;
let span = self.patch.source_info_for_location(self.body, loc).span;
let false_ = self.constant_bool(span, false);
for flag in self.drop_flags.values() {
for flag in self.drop_flags.iter().flatten() {
self.patch.add_assign(loc, Place::from(*flag), false_.clone());
}
}

View File

@ -859,13 +859,9 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
&sig.decl.output,
);
this.record_lifetime_params_for_async(
fn_id,
sig.header.asyncness.opt_return_id(),
);
},
);
self.record_lifetime_params_for_async(fn_id, sig.header.asyncness.opt_return_id());
return;
}
FnKind::Fn(..) => {

View File

@ -3,7 +3,8 @@ use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin,
TyCtxtInferExt,
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
@ -223,18 +224,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
{
debug!("rerunning goal to check result is stable");
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
let new_canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
if !canonical_response.value.var_values.is_identity() {
if !new_canonical_response.value.var_values.is_identity() {
bug!(
"unstable result: re-canonicalized goal={canonical_goal:#?} \
response={canonical_response:#?}"
first_response={canonical_response:#?} \
second_response={new_canonical_response:#?}"
);
}
if certainty != canonical_response.value.certainty {
if certainty != new_canonical_response.value.certainty {
bug!(
"unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \
response={canonical_response:#?}"
first_response={canonical_response:#?} \
second_response={new_canonical_response:#?}"
);
}
}
@ -434,6 +437,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
})
}
pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> {
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
}
pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> {
self.infcx.next_const_var(
ty,

View File

@ -16,7 +16,7 @@ use rustc_infer::infer::canonical::query_response::make_query_region_constraints
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause};
use rustc_middle::ty::{self, BoundVar, GenericArgKind};
use rustc_span::DUMMY_SP;
use std::iter;
@ -60,9 +60,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let certainty = certainty.unify_with(goals_certainty);
let external_constraints = self.compute_external_query_constraints()?;
let response = match certainty {
Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => {
let external_constraints = self.compute_external_query_constraints()?;
Response { var_values: self.var_values, external_constraints, certainty }
}
Certainty::Maybe(MaybeCause::Overflow) => {
// If we have overflow, it's probable that we're substituting a type
// into itself infinitely and any partial substitutions in the query
// response are probably not useful anyways, so just return an empty
// query response.
//
// This may prevent us from potentially useful inference, e.g.
// 2 candidates, one ambiguous and one overflow, which both
// have the same inference constraints.
//
// Changing this to retain some constraints in the future
// won't be a breaking change, so this is good enough for now.
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
}
};
let response = Response { var_values: self.var_values, external_constraints, certainty };
let canonical = Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
@ -72,6 +90,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(canonical)
}
/// Constructs a totally unconstrained, ambiguous response to a goal.
///
/// Take care when using this, since often it's useful to respond with
/// ambiguity but return constrained variables to guide inference.
pub(in crate::solve) fn make_ambiguous_response_no_constraints(
&self,
maybe_cause: MaybeCause,
) -> CanonicalResponse<'tcx> {
let unconstrained_response = Response {
var_values: CanonicalVarValues {
var_values: self.tcx().mk_substs_from_iter(self.var_values.var_values.iter().map(
|arg| -> ty::GenericArg<'tcx> {
match arg.unpack() {
GenericArgKind::Lifetime(_) => self.next_region_infer().into(),
GenericArgKind::Type(_) => self.next_ty_infer().into(),
GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
}
},
)),
},
external_constraints: self
.tcx()
.mk_external_constraints(ExternalConstraintsData::default()),
certainty: Certainty::Maybe(maybe_cause),
};
Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
&mut Default::default(),
unconstrained_response,
)
}
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
// Cannot use `take_registered_region_obligations` as we may compute the response

View File

@ -340,17 +340,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if responses.is_empty() {
return Err(NoSolution);
}
let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_with(response.value.certainty)
});
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
if let Ok(response) = response {
assert!(response.has_no_inference_or_external_constraints());
Ok(response)
} else {
bug!("failed to make floundered response: {responses:?}");
}
let Certainty::Maybe(maybe_cause) = responses.iter().fold(
Certainty::AMBIGUOUS,
|certainty, response| {
certainty.unify_with(response.value.certainty)
},
) else {
bug!("expected flounder response to be ambiguous")
};
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
}
}

View File

@ -1543,11 +1543,17 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
self.next_back()
}
fn min(mut self) -> Option<(&'a K, &'a V)> {
fn min(mut self) -> Option<(&'a K, &'a V)>
where
(&'a K, &'a V): Ord,
{
self.next()
}
fn max(mut self) -> Option<(&'a K, &'a V)> {
fn max(mut self) -> Option<(&'a K, &'a V)>
where
(&'a K, &'a V): Ord,
{
self.next_back()
}
}
@ -1612,11 +1618,17 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> {
self.next_back()
}
fn min(mut self) -> Option<(&'a K, &'a mut V)> {
fn min(mut self) -> Option<(&'a K, &'a mut V)>
where
(&'a K, &'a mut V): Ord,
{
self.next()
}
fn max(mut self) -> Option<(&'a K, &'a mut V)> {
fn max(mut self) -> Option<(&'a K, &'a mut V)>
where
(&'a K, &'a mut V): Ord,
{
self.next_back()
}
}
@ -1779,11 +1791,17 @@ impl<'a, K, V> Iterator for Keys<'a, K, V> {
self.next_back()
}
fn min(mut self) -> Option<&'a K> {
fn min(mut self) -> Option<&'a K>
where
&'a K: Ord,
{
self.next()
}
fn max(mut self) -> Option<&'a K> {
fn max(mut self) -> Option<&'a K>
where
&'a K: Ord,
{
self.next_back()
}
}
@ -2008,11 +2026,17 @@ impl<'a, K, V> Iterator for Range<'a, K, V> {
self.next_back()
}
fn min(mut self) -> Option<(&'a K, &'a V)> {
fn min(mut self) -> Option<(&'a K, &'a V)>
where
(&'a K, &'a V): Ord,
{
self.next()
}
fn max(mut self) -> Option<(&'a K, &'a V)> {
fn max(mut self) -> Option<(&'a K, &'a V)>
where
(&'a K, &'a V): Ord,
{
self.next_back()
}
}
@ -2081,11 +2105,17 @@ impl<K, V, A: Allocator + Clone> Iterator for IntoKeys<K, V, A> {
self.next_back()
}
fn min(mut self) -> Option<K> {
fn min(mut self) -> Option<K>
where
K: Ord,
{
self.next()
}
fn max(mut self) -> Option<K> {
fn max(mut self) -> Option<K>
where
K: Ord,
{
self.next_back()
}
}
@ -2204,11 +2234,17 @@ impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
self.next_back()
}
fn min(mut self) -> Option<(&'a K, &'a mut V)> {
fn min(mut self) -> Option<(&'a K, &'a mut V)>
where
(&'a K, &'a mut V): Ord,
{
self.next()
}
fn max(mut self) -> Option<(&'a K, &'a mut V)> {
fn max(mut self) -> Option<(&'a K, &'a mut V)>
where
(&'a K, &'a mut V): Ord,
{
self.next_back()
}
}

View File

@ -1501,11 +1501,17 @@ impl<'a, T> Iterator for Iter<'a, T> {
self.next_back()
}
fn min(mut self) -> Option<&'a T> {
fn min(mut self) -> Option<&'a T>
where
&'a T: Ord,
{
self.next()
}
fn max(mut self) -> Option<&'a T> {
fn max(mut self) -> Option<&'a T>
where
&'a T: Ord,
{
self.next_back()
}
}
@ -1604,11 +1610,17 @@ impl<'a, T> Iterator for Range<'a, T> {
self.next_back()
}
fn min(mut self) -> Option<&'a T> {
fn min(mut self) -> Option<&'a T>
where
&'a T: Ord,
{
self.next()
}
fn max(mut self) -> Option<&'a T> {
fn max(mut self) -> Option<&'a T>
where
&'a T: Ord,
{
self.next_back()
}
}

View File

@ -2815,7 +2815,7 @@ impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> {
}
#[inline]
fn extend_one(&mut self, &elem: &T) {
fn extend_one(&mut self, &elem: &'a T) {
self.push_back(elem);
}

View File

@ -248,7 +248,7 @@ where
impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] {
type Error = TryFromSliceError;
fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> {
fn try_from(slice: &'a [T]) -> Result<&'a [T; N], TryFromSliceError> {
if slice.len() == N {
let ptr = slice.as_ptr() as *const [T; N];
// SAFETY: ok because we just checked that the length fits
@ -275,7 +275,7 @@ impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] {
impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] {
type Error = TryFromSliceError;
fn try_from(slice: &mut [T]) -> Result<&mut [T; N], TryFromSliceError> {
fn try_from(slice: &'a mut [T]) -> Result<&'a mut [T; N], TryFromSliceError> {
if slice.len() == N {
let ptr = slice.as_mut_ptr() as *mut [T; N];
// SAFETY: ok because we just checked that the length fits

View File

@ -732,12 +732,18 @@ impl<A: Step> Iterator for ops::Range<A> {
}
#[inline]
fn min(mut self) -> Option<A> {
fn min(mut self) -> Option<A>
where
A: Ord,
{
self.next()
}
#[inline]
fn max(mut self) -> Option<A> {
fn max(mut self) -> Option<A>
where
A: Ord,
{
self.next_back()
}
@ -1158,12 +1164,18 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
}
#[inline]
fn min(mut self) -> Option<A> {
fn min(mut self) -> Option<A>
where
A: Ord,
{
self.next()
}
#[inline]
fn max(mut self) -> Option<A> {
fn max(mut self) -> Option<A>
where
A: Ord,
{
self.next_back()
}

View File

@ -71,7 +71,7 @@ macro_rules! deref_ops {
#[inline]
#[must_use = "operator returns a new vector without mutating the inputs"]
fn $call(self, rhs: &$simd) -> Self::Output {
fn $call(self, rhs: &'rhs $simd) -> Self::Output {
(*self).$call(*rhs)
}
}

View File

@ -22,9 +22,9 @@
}
bb0: {
+ _9 = const false; // scope 0 at $DIR/issue_41888.rs:+1:9: +1:10
+ _7 = const false; // scope 0 at $DIR/issue_41888.rs:+1:9: +1:10
+ _8 = const false; // scope 0 at $DIR/issue_41888.rs:+1:9: +1:10
+ _9 = const false; // scope 0 at $DIR/issue_41888.rs:+1:9: +1:10
StorageLive(_1); // scope 0 at $DIR/issue_41888.rs:+1:9: +1:10
StorageLive(_2); // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14
_2 = cond() -> [return: bb1, unwind: bb11]; // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14

View File

@ -1,7 +1,5 @@
// edition: 2021
// known-bug: #105197
// failure-status:101
// dont-check-compiler-stderr
// check-pass
#![feature(async_fn_in_trait)]
#![feature(return_position_impl_trait_in_trait)]

View File

@ -0,0 +1,48 @@
// edition: 2021
// known-bug: #110963
#![feature(return_type_notation)]
#![feature(async_fn_in_trait)]
trait HealthCheck {
async fn check<'a: 'a>(&'a mut self) -> bool;
}
async fn do_health_check_par<HC>(hc: HC)
where
HC: HealthCheck<check(): Send> + Send + 'static,
{
spawn(async move {
let mut hc = hc;
if !hc.check().await {
log_health_check_failure().await;
}
});
}
async fn log_health_check_failure() {}
fn main() {}
// Fake tokio spawn
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
fn spawn<F>(future: F) -> JoinHandle
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
loop {}
}
struct JoinHandle;
impl Future for JoinHandle {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {}
}
}

View File

@ -0,0 +1,45 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-110963-early.rs:4:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-110963-early.rs:5:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
error: higher-ranked lifetime error
--> $DIR/issue-110963-early.rs:15:5
|
LL | / spawn(async move {
LL | | let mut hc = hc;
LL | | if !hc.check().await {
LL | | log_health_check_failure().await;
LL | | }
LL | | });
| |______^
|
= note: could not prove `[async block@$DIR/issue-110963-early.rs:15:11: 20:6]: Send`
error: higher-ranked lifetime error
--> $DIR/issue-110963-early.rs:15:5
|
LL | / spawn(async move {
LL | | let mut hc = hc;
LL | | if !hc.check().await {
LL | | log_health_check_failure().await;
LL | | }
LL | | });
| |______^
|
= note: could not prove `[async block@$DIR/issue-110963-early.rs:15:11: 20:6]: Send`
error: aborting due to 2 previous errors; 2 warnings emitted

View File

@ -0,0 +1,50 @@
// edition: 2021
// check-pass
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
#![feature(async_fn_in_trait)]
//~^ WARN the feature `async_fn_in_trait` is incomplete
trait HealthCheck {
async fn check(&mut self) -> bool;
}
async fn do_health_check_par<HC>(hc: HC)
where
HC: HealthCheck<check(): Send> + Send + 'static,
{
spawn(async move {
let mut hc = hc;
if !hc.check().await {
log_health_check_failure().await;
}
});
}
async fn log_health_check_failure() {}
fn main() {}
// Fake tokio spawn
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
fn spawn<F>(future: F) -> JoinHandle
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
loop {}
}
struct JoinHandle;
impl Future for JoinHandle {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {}
}
}

View File

@ -0,0 +1,19 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-110963-late.rs:4:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-110963-late.rs:6:12
|
LL | #![feature(async_fn_in_trait)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
warning: 2 warnings emitted

View File

@ -0,0 +1,20 @@
// compile-flags: -Ztrait-solver=next
trait Trait {}
struct W<T>(T);
impl<T, U> Trait for W<(W<T>, W<U>)>
where
W<T>: Trait,
W<U>: Trait,
{
}
fn impls<T: Trait>() {}
fn main() {
impls::<W<_>>();
//~^ ERROR type annotations needed
//~| ERROR overflow evaluating the requirement `W<_>: Trait`
}

View File

@ -0,0 +1,23 @@
error[E0282]: type annotations needed
--> $DIR/exponential-trait-goals.rs:17:5
|
LL | impls::<W<_>>();
| ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls`
error[E0275]: overflow evaluating the requirement `W<_>: Trait`
--> $DIR/exponential-trait-goals.rs:17:5
|
LL | impls::<W<_>>();
| ^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`exponential_trait_goals`)
note: required by a bound in `impls`
--> $DIR/exponential-trait-goals.rs:14:13
|
LL | fn impls<T: Trait>() {}
| ^^^^^ required by this bound in `impls`
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0275, E0282.
For more information about an error, try `rustc --explain E0275`.