Auto merge of #131495 - matthiaskrgr:rollup-lwf2u4i, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #130625 (Fix a few relative paths in rustc doc)
 - #131397 (fix/update teach_note from 'escaping mutable ref/ptr' const-check)
 - #131479 (Apple: Avoid redundant `-Wl,-dylib` flag when linking)
 - #131480 (Fix hardcoded strip path when cross-compiling from Linux to Darwin)
 - #131482 (structurally resolve adts and tuples expectations too)
 - #131484 (Add myself back to review rotation)
 - #131491 (impossible obligations fast path)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-10-10 11:00:28 +00:00
commit 4cc494bbfe
20 changed files with 171 additions and 87 deletions

View File

@ -1087,7 +1087,9 @@ fn link_natively(
let strip = sess.opts.cg.strip;
if sess.target.is_like_osx {
let stripcmd = "/usr/bin/strip";
// Use system `strip` when running on host macOS.
// <https://github.com/rust-lang/rust/pull/130781>
let stripcmd = if cfg!(target_os = "macos") { "/usr/bin/strip" } else { "strip" };
match (strip, crate_type) {
(Strip::Debuginfo, _) => {
strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S"))

View File

@ -404,12 +404,14 @@ impl<'a> GccLinker<'a> {
fn build_dylib(&mut self, crate_type: CrateType, out_filename: &Path) {
// On mac we need to tell the linker to let this library be rpathed
if self.sess.target.is_like_osx {
if !self.is_ld {
if self.is_cc() {
// `-dynamiclib` makes `cc` pass `-dylib` to the linker.
self.cc_arg("-dynamiclib");
} else {
self.link_arg("-dylib");
// Clang also sets `-dynamic`, but that's implied by `-dylib`, so unnecessary.
}
self.link_arg("-dylib");
// Note that the `osx_rpath_install_name` option here is a hack
// purely to support bootstrap right now, we should get a more
// principled solution at some point to force the compiler to pass

View File

@ -134,14 +134,16 @@ const_eval_incompatible_return_types =
const_eval_incompatible_types =
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
const_eval_interior_mutable_data_refer =
const_eval_interior_mutable_ref_escaping =
{const_eval_const_context}s cannot refer to interior mutable data
.label = this borrow of an interior mutable value may end up in the final value
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
.teach_note =
A constant containing interior mutable data behind a reference can allow you to modify that data.
This would make multiple uses of a constant to be able to see different values and allow circumventing
the `Send` and `Sync` requirements for shared mutable data, which is unsound.
References that escape into the final value of a constant or static must be immutable.
This is to avoid accidentally creating shared mutable state.
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
const_eval_intern_kind = {$kind ->
[static] static
@ -229,6 +231,24 @@ const_eval_modified_global =
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
const_eval_mutable_raw_escaping =
raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
.teach_note =
Pointers that escape into the final value of a constant or static must be immutable.
This is to avoid accidentally creating shared mutable state.
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
const_eval_mutable_ref_escaping =
mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note =
References that escape into the final value of a constant or static must be immutable.
This is to avoid accidentally creating shared mutable state.
If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
const_eval_non_const_fmt_macro_call =
cannot call non-const formatting macro in {const_eval_const_context}s
@ -364,30 +384,11 @@ const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in
const_eval_unallowed_heap_allocations =
allocations are not allowed in {const_eval_const_context}s
.label = allocation not allowed in {const_eval_const_context}s
.teach_note = The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
.teach_note =
The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
const_eval_unallowed_inline_asm =
inline assembly is not allowed in {const_eval_const_context}s
const_eval_unallowed_mutable_raw =
raw mutable pointers are not allowed in the final value of {const_eval_const_context}s
.teach_note =
References in statics and constants may only refer to immutable values.
Statics are shared everywhere, and if they refer to mutable data one might violate memory
safety since holding multiple mutable references to shared data is not allowed.
If you really want global mutable state, try using static mut or a global UnsafeCell.
const_eval_unallowed_mutable_refs =
mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note =
Statics are shared everywhere, and if they refer to mutable data one might violate memory
safety since holding multiple mutable references to shared data is not allowed.
If you really want global mutable state, try using static mut or a global UnsafeCell.
const_eval_unallowed_op_in_const_context =
{$msg}

View File

@ -666,6 +666,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
// This can be called on stable via the `vec!` macro.
if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) {
self.check_op(ops::HeapAllocation);
return;

View File

@ -402,7 +402,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
DiagImportance::Secondary
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::InteriorMutableDataRefer {
ccx.dcx().create_err(errors::InteriorMutableRefEscaping {
span,
opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
kind: ccx.const_kind(),
@ -430,12 +430,12 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
match self.0 {
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw {
hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::MutableRawEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
}),
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::UnallowedMutableRefs {
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),

View File

@ -118,8 +118,8 @@ pub(crate) struct UnstableConstFn {
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_mutable_refs, code = E0764)]
pub(crate) struct UnallowedMutableRefs {
#[diag(const_eval_mutable_ref_escaping, code = E0764)]
pub(crate) struct MutableRefEscaping {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
@ -128,8 +128,8 @@ pub(crate) struct UnallowedMutableRefs {
}
#[derive(Diagnostic)]
#[diag(const_eval_unallowed_mutable_raw, code = E0764)]
pub(crate) struct UnallowedMutableRaw {
#[diag(const_eval_mutable_raw_escaping, code = E0764)]
pub(crate) struct MutableRawEscaping {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
@ -181,8 +181,8 @@ pub(crate) struct UnallowedInlineAsm {
}
#[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_data_refer, code = E0492)]
pub(crate) struct InteriorMutableDataRefer {
#[diag(const_eval_interior_mutable_ref_escaping, code = E0492)]
pub(crate) struct InteriorMutableRefEscaping {
#[primary_span]
#[label]
pub span: Span,

View File

@ -33,6 +33,7 @@ pub mod weak_lang_items;
#[cfg(test)]
mod tests;
#[doc(no_inline)]
pub use hir::*;
pub use hir_id::*;
pub use lang_items::{LangItem, LanguageItems};

View File

@ -1783,7 +1783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let flds = expected.only_has_type(self).and_then(|ty| {
let ty = self.resolve_vars_with_obligations(ty);
let ty = self.try_structurally_resolve_type(expr.span, ty);
match ty.kind() {
ty::Tuple(flds) => Some(&flds[..]),
_ => None,
@ -1861,7 +1861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let tcx = self.tcx;
let adt_ty = self.resolve_vars_with_obligations(adt_ty);
let adt_ty = self.try_structurally_resolve_type(span, adt_ty);
let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
self.fudge_inference_if_ok(|| {
let ocx = ObligationCtxt::new(self);

View File

@ -17,6 +17,7 @@ mod vec;
pub use idx::Idx;
pub use rustc_index_macros::newtype_index;
pub use slice::IndexSlice;
#[doc(no_inline)]
pub use vec::IndexVec;
/// Type size assertion. The first argument is a type and the second argument is its expected size.

View File

@ -18,8 +18,8 @@ use crate::solve::inspect::{self, ProofTreeBuilder};
use crate::solve::search_graph::SearchGraph;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind,
GoalSource, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult,
SolverMode,
GoalSource, HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData,
QueryResult, SolverMode,
};
pub(super) mod canonical;
@ -126,11 +126,31 @@ pub enum GenerateProofTree {
}
pub trait SolverDelegateEvalExt: SolverDelegate {
/// Evaluates a goal from **outside** of the trait solver.
///
/// Using this while inside of the solver is wrong as it uses a new
/// search graph which would break cycle detection.
fn evaluate_root_goal(
&self,
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
generate_proof_tree: GenerateProofTree,
) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<Self::Interner>>);
) -> (
Result<(HasChanged, Certainty), NoSolution>,
Option<inspect::GoalEvaluation<Self::Interner>>,
);
/// Check whether evaluating `goal` with a depth of `root_depth` may
/// succeed. This only returns `false` if the goal is guaranteed to
/// not hold. In case evaluation overflows and fails with ambiguity this
/// returns `true`.
///
/// This is only intended to be used as a performance optimization
/// in coherence checking.
fn root_goal_may_hold_with_depth(
&self,
root_depth: usize,
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
) -> bool;
// FIXME: This is only exposed because we need to use it in `analyse.rs`
// which is not yet uplifted. Once that's done, we should remove this.
@ -139,7 +159,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
generate_proof_tree: GenerateProofTree,
) -> (
Result<(NestedNormalizationGoals<Self::Interner>, bool, Certainty), NoSolution>,
Result<(NestedNormalizationGoals<Self::Interner>, HasChanged, Certainty), NoSolution>,
Option<inspect::GoalEvaluation<Self::Interner>>,
);
}
@ -149,31 +169,41 @@ where
D: SolverDelegate<Interner = I>,
I: Interner,
{
/// Evaluates a goal from **outside** of the trait solver.
///
/// Using this while inside of the solver is wrong as it uses a new
/// search graph which would break cycle detection.
#[instrument(level = "debug", skip(self))]
fn evaluate_root_goal(
&self,
goal: Goal<I, I::Predicate>,
generate_proof_tree: GenerateProofTree,
) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
) -> (Result<(HasChanged, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| {
ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
})
}
fn root_goal_may_hold_with_depth(
&self,
root_depth: usize,
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
) -> bool {
self.probe(|| {
EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, |ecx| {
ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
})
.0
})
.is_ok()
}
#[instrument(level = "debug", skip(self))]
fn evaluate_root_goal_raw(
&self,
goal: Goal<I, I::Predicate>,
generate_proof_tree: GenerateProofTree,
) -> (
Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution>,
Result<(NestedNormalizationGoals<I>, HasChanged, Certainty), NoSolution>,
Option<inspect::GoalEvaluation<I>>,
) {
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| {
ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
})
}
@ -197,10 +227,11 @@ where
/// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]).
pub(super) fn enter_root<R>(
delegate: &D,
root_depth: usize,
generate_proof_tree: GenerateProofTree,
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R,
) -> (R, Option<inspect::GoalEvaluation<I>>) {
let mut search_graph = SearchGraph::new(delegate.solver_mode());
let mut search_graph = SearchGraph::new(delegate.solver_mode(), root_depth);
let mut ecx = EvalCtxt {
delegate,
@ -339,7 +370,7 @@ where
goal_evaluation_kind: GoalEvaluationKind,
source: GoalSource,
goal: Goal<I, I::Predicate>,
) -> Result<(bool, Certainty), NoSolution> {
) -> Result<(HasChanged, Certainty), NoSolution> {
let (normalization_nested_goals, has_changed, certainty) =
self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
assert!(normalization_nested_goals.is_empty());
@ -360,7 +391,7 @@ where
goal_evaluation_kind: GoalEvaluationKind,
_source: GoalSource,
goal: Goal<I, I::Predicate>,
) -> Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution> {
) -> Result<(NestedNormalizationGoals<I>, HasChanged, Certainty), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
let mut goal_evaluation =
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
@ -378,8 +409,13 @@ where
Ok(response) => response,
};
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty();
let has_changed = if !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty()
{
HasChanged::Yes
} else {
HasChanged::No
};
let (normalization_nested_goals, certainty) =
self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
@ -552,7 +588,7 @@ where
for (source, goal) in goals.goals {
let (has_changed, certainty) =
self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?;
if has_changed {
if has_changed == HasChanged::Yes {
unchanged_certainty = None;
}

View File

@ -48,6 +48,14 @@ enum GoalEvaluationKind {
Nested,
}
/// Whether evaluating this goal ended up changing the
/// inference state.
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
pub enum HasChanged {
Yes,
No,
}
// FIXME(trait-system-refactor-initiative#117): we don't detect whether a response
// ended up pulling down any universes.
fn has_no_inference_or_external_constraints<I: Interner>(

View File

@ -40,9 +40,6 @@ where
}
const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize = 4;
fn recursion_limit(cx: I) -> usize {
cx.recursion_limit()
}
fn initial_provisional_result(
cx: I,

View File

@ -6,6 +6,7 @@ pub mod inspect;
mod normalize;
mod select;
pub(crate) use delegate::SolverDelegate;
pub use fulfill::{FulfillmentCtxt, NextSolverError};
pub(crate) use normalize::deeply_normalize_for_diagnostics;
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};

View File

@ -12,7 +12,7 @@ use rustc_infer::traits::{
use rustc_middle::bug;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt};
use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
use tracing::instrument;
use super::Certainty;
@ -86,10 +86,7 @@ impl<'tcx> ObligationStorage<'tcx> {
let result = <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal(goal, GenerateProofTree::No)
.0;
match result {
Ok((has_changed, _)) => has_changed,
_ => false,
}
matches!(result, Ok((HasChanged::Yes, _)))
}));
})
}
@ -113,7 +110,7 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
&self,
infcx: &InferCtxt<'tcx>,
obligation: &PredicateObligation<'tcx>,
result: &Result<(bool, Certainty), NoSolution>,
result: &Result<(HasChanged, Certainty), NoSolution>,
) {
if let Some(inspector) = infcx.obligation_inspector.get() {
let result = match result {
@ -181,7 +178,11 @@ where
continue;
}
};
has_changed |= changed;
if changed == HasChanged::Yes {
has_changed = true;
}
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => self.obligations.register(obligation),

View File

@ -19,6 +19,7 @@ use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use rustc_middle::ty::{self, Ty, TyCtxt};
pub use rustc_next_trait_solver::coherence::*;
use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
use rustc_span::symbol::sym;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument, warn};
@ -28,7 +29,7 @@ use crate::error_reporting::traits::suggest_new_overflow_limit;
use crate::infer::InferOk;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::solve::{deeply_normalize_for_diagnostics, inspect};
use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{
@ -333,6 +334,16 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
let infcx = selcx.infcx;
if infcx.next_trait_solver() {
// A fast path optimization, try evaluating all goals with
// a very low recursion depth and bail if any of them don't
// hold.
if !obligations.iter().all(|o| {
<&SolverDelegate<'tcx>>::from(infcx)
.root_goal_may_hold_with_depth(8, Goal::new(infcx.tcx, o.param_env, o.predicate))
}) {
return IntersectionHasImpossibleObligations::Yes;
}
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
ocx.register_obligations(obligations.iter().cloned());
let errors_and_ambiguities = ocx.select_all_or_error();

View File

@ -81,7 +81,6 @@ pub trait Delegate {
fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool;
const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize;
fn recursion_limit(cx: Self::Cx) -> usize;
fn initial_provisional_result(
cx: Self::Cx,
@ -156,7 +155,7 @@ impl AvailableDepth {
/// the remaining depth of all nested goals to prevent hangs
/// in case there is exponential blowup.
fn allowed_depth_for_nested<D: Delegate>(
cx: D::Cx,
root_depth: AvailableDepth,
stack: &IndexVec<StackDepth, StackEntry<D::Cx>>,
) -> Option<AvailableDepth> {
if let Some(last) = stack.raw.last() {
@ -170,7 +169,7 @@ impl AvailableDepth {
AvailableDepth(last.available_depth.0 - 1)
})
} else {
Some(AvailableDepth(D::recursion_limit(cx)))
Some(root_depth)
}
}
@ -360,6 +359,7 @@ struct ProvisionalCacheEntry<X: Cx> {
pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
mode: SolverMode,
root_depth: AvailableDepth,
/// The stack of goals currently being computed.
///
/// An element is *deeper* in the stack if its index is *lower*.
@ -374,9 +374,10 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
}
impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
pub fn new(mode: SolverMode) -> SearchGraph<D> {
pub fn new(mode: SolverMode, root_depth: usize) -> SearchGraph<D> {
Self {
mode,
root_depth: AvailableDepth(root_depth),
stack: Default::default(),
provisional_cache: Default::default(),
_marker: PhantomData,
@ -460,7 +461,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
inspect: &mut D::ProofTreeBuilder,
mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
) -> X::Result {
let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(cx, &self.stack)
let Some(available_depth) =
AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack)
else {
return self.handle_overflow(cx, input, inspect);
};

View File

@ -1,11 +0,0 @@
//@ known-bug: rust-lang/rust#124894
//@ compile-flags: -Znext-solver=coherence
#![feature(generic_const_exprs)]
pub trait IsTrue<const mem: bool> {}
impl<T> IsZST for T where (): IsTrue<{ std::mem::size_of::<T>() == 0 }> {}
pub trait IsZST {}
impl IsZST for IsZST {}

View File

@ -4,7 +4,7 @@ error[E0010]: allocations are not allowed in constants
LL | const CON: Vec<i32> = vec![1, 2, 3];
| ^^^^^^^^^^^^^ allocation not allowed in constants
|
= note: The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
= note: The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0015]: cannot call non-const fn `slice::<impl [i32]>::into_vec::<std::alloc::Global>` in constants

View File

@ -0,0 +1,32 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Makes sure we structurally normalize before trying to use expectation to guide
// coercion in adt and tuples.
use std::any::Any;
trait Coerce {
type Assoc;
}
struct TupleGuidance;
impl Coerce for TupleGuidance {
type Assoc = (&'static dyn Any,);
}
struct AdtGuidance;
impl Coerce for AdtGuidance {
type Assoc = Adt<&'static dyn Any>;
}
struct Adt<T> {
f: T,
}
fn foo<'a, T: Coerce>(_: T::Assoc) {}
fn main() {
foo::<TupleGuidance>((&0u32,));
foo::<AdtGuidance>(Adt { f: &0u32 });
}

View File

@ -923,7 +923,6 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
users_on_vacation = [
"BoxyUwU",
"fmease",
"jhpratt",
"jyn514",
"oli-obk",
]