Skip to content

Commit f92d49b

Browse files
committedMay 2, 2024
Auto merge of #124529 - compiler-errors:select, r=lcnr
Rewrite select (in the new solver) to use a `ProofTreeVisitor` We can use a proof tree visitor rather than collecting and recomputing all the nested goals ourselves. Based on #124415
2 parents cfb2410 + 9834c83 commit f92d49b

File tree

2 files changed

+165
-328
lines changed

2 files changed

+165
-328
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
use rustc_hir as hir;
1+
use std::ops::ControlFlow;
2+
23
use rustc_hir::def_id::DefId;
3-
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
4+
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
5+
use rustc_infer::traits::solve::inspect::ProbeKind;
6+
use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal};
47
use rustc_infer::traits::{
5-
Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
8+
BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause,
9+
PolyTraitObligation, PredicateObligation, Selection, SelectionError, SelectionResult,
610
};
711
use rustc_macros::extension;
8-
use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal};
9-
use rustc_middle::traits::{
10-
BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
11-
};
12-
use rustc_middle::ty::{self, Ty, TyCtxt};
13-
use rustc_span::DUMMY_SP;
12+
use rustc_span::Span;
1413

15-
use crate::solve::assembly::Candidate;
16-
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
17-
use crate::solve::inspect::ProofTreeBuilder;
18-
use crate::traits::StructurallyNormalizeExt;
19-
use crate::traits::TraitEngineExt;
14+
use crate::solve::inspect::{self, ProofTreeInferCtxtExt};
2015

2116
#[extension(pub trait InferCtxtSelectExt<'tcx>)]
2217
impl<'tcx> InferCtxt<'tcx> {
@@ -26,359 +21,192 @@ impl<'tcx> InferCtxt<'tcx> {
2621
) -> SelectionResult<'tcx, Selection<'tcx>> {
2722
assert!(self.next_trait_solver());
2823

29-
self.enter_forall(obligation.predicate, |pred| {
30-
let trait_goal = Goal::new(self.tcx, obligation.param_env, pred);
31-
32-
let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
33-
let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
34-
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
35-
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
24+
self.visit_proof_tree(
25+
Goal::new(self.tcx, obligation.param_env, obligation.predicate),
26+
&mut Select { span: obligation.cause.span },
27+
)
28+
.break_value()
29+
.unwrap()
30+
}
31+
}
3632

37-
// pseudo-winnow
38-
if candidates.len() == 0 {
39-
return Err(SelectionError::Unimplemented);
40-
} else if candidates.len() > 1 {
41-
let mut i = 0;
42-
while i < candidates.len() {
43-
let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
44-
candidate_should_be_dropped_in_favor_of(
45-
ecx.tcx(),
46-
&candidates[i],
47-
&candidates[j],
48-
)
49-
});
50-
if should_drop_i {
51-
candidates.swap_remove(i);
52-
} else {
53-
i += 1;
54-
if i > 1 {
55-
return Ok(None);
56-
}
57-
}
58-
}
59-
}
33+
struct Select {
34+
span: Span,
35+
}
6036

61-
let candidate = candidates.pop().unwrap();
62-
let (normalization_nested_goals, certainty) = ecx
63-
.instantiate_and_apply_query_response(
64-
trait_goal.param_env,
65-
orig_values,
66-
candidate.result,
67-
);
68-
assert!(normalization_nested_goals.is_empty());
69-
Ok(Some((candidate, certainty)))
70-
});
37+
impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select {
38+
type Result = ControlFlow<SelectionResult<'tcx, Selection<'tcx>>>;
7139

72-
let (candidate, certainty) = match result {
73-
Ok(Some(result)) => result,
74-
Ok(None) => return Ok(None),
75-
Err(e) => return Err(e),
76-
};
40+
fn span(&self) -> Span {
41+
self.span
42+
}
7743

78-
let goal = self.resolve_vars_if_possible(trait_goal);
79-
match (certainty, candidate.source) {
80-
// Rematching the implementation will instantiate the same nested goals that
81-
// would have caused the ambiguity, so we can still make progress here regardless.
82-
(_, CandidateSource::Impl(def_id)) => rematch_impl(self, goal, def_id),
44+
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
45+
let mut candidates = goal.candidates();
46+
candidates.retain(|cand| cand.result().is_ok());
8347

84-
// If an unsize goal is ambiguous, then we can manually rematch it to make
85-
// selection progress for coercion during HIR typeck. If it is *not* ambiguous,
86-
// but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
87-
// and we need to rematch those to detect tuple unsizing and trait upcasting.
88-
// FIXME: This will be wrong if we have param-env or where-clause bounds
89-
// with the unsize goal -- we may need to mark those with different impl
90-
// sources.
91-
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
92-
| (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
93-
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
94-
{
95-
rematch_unsize(self, goal, src, certainty)
96-
}
48+
// No candidates -- not implemented.
49+
if candidates.is_empty() {
50+
return ControlFlow::Break(Err(SelectionError::Unimplemented));
51+
}
9752

98-
// Technically some builtin impls have nested obligations, but if
99-
// `Certainty::Yes`, then they should've all been verified and don't
100-
// need re-checking.
101-
(Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
102-
Ok(Some(ImplSource::Builtin(src, vec![])))
103-
}
53+
// One candidate, no need to winnow.
54+
if candidates.len() == 1 {
55+
return ControlFlow::Break(Ok(to_selection(
56+
self.span,
57+
candidates.into_iter().next().unwrap(),
58+
)));
59+
}
10460

105-
// It's fine not to do anything to rematch these, since there are no
106-
// nested obligations.
107-
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
108-
Ok(Some(ImplSource::Param(vec![])))
61+
// We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`.
62+
let mut i = 0;
63+
while i < candidates.len() {
64+
let should_drop_i = (0..candidates.len())
65+
.filter(|&j| i != j)
66+
.any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]));
67+
if should_drop_i {
68+
candidates.swap_remove(i);
69+
} else {
70+
i += 1;
71+
if i > 1 {
72+
return ControlFlow::Break(Ok(None));
10973
}
110-
111-
(_, CandidateSource::CoherenceUnknowable) => bug!(),
112-
113-
(Certainty::Maybe(_), _) => Ok(None),
11474
}
115-
})
116-
}
117-
}
75+
}
11876

119-
impl<'tcx> EvalCtxt<'_, 'tcx> {
120-
fn compute_canonical_trait_candidates(
121-
&mut self,
122-
canonical_input: CanonicalInput<'tcx>,
123-
) -> Vec<Candidate<'tcx>> {
124-
// This doesn't record the canonical goal on the stack during the
125-
// candidate assembly step, but that's fine. Selection is conceptually
126-
// outside of the solver, and if there were any cycles, we'd encounter
127-
// the cycle anyways one step later.
128-
EvalCtxt::enter_canonical(
129-
self.tcx(),
130-
self.search_graph,
131-
canonical_input,
132-
// FIXME: This is wrong, idk if we even want to track stuff here.
133-
&mut ProofTreeBuilder::new_noop(),
134-
|ecx, goal| {
135-
let trait_goal = Goal {
136-
param_env: goal.param_env,
137-
predicate: goal
138-
.predicate
139-
.to_opt_poly_trait_pred()
140-
.expect("we canonicalized a trait goal")
141-
.no_bound_vars()
142-
.expect("we instantiated all bound vars"),
143-
};
144-
ecx.assemble_and_evaluate_candidates(trait_goal)
145-
},
146-
)
77+
ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap())))
14778
}
14879
}
14980

81+
/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)`
82+
/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity,
83+
/// and should always just gracefully fail here. We shouldn't rely on this incompleteness.
15084
fn candidate_should_be_dropped_in_favor_of<'tcx>(
151-
tcx: TyCtxt<'tcx>,
152-
victim: &Candidate<'tcx>,
153-
other: &Candidate<'tcx>,
85+
victim: &inspect::InspectCandidate<'_, 'tcx>,
86+
other: &inspect::InspectCandidate<'_, 'tcx>,
15487
) -> bool {
155-
match (victim.source, other.source) {
156-
(CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => {
157-
victim_idx >= other_idx
88+
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
89+
// codegen, technically.
90+
if matches!(other.result().unwrap(), Certainty::Maybe(..)) {
91+
return false;
92+
}
93+
94+
let inspect::ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind()
95+
else {
96+
return false;
97+
};
98+
let inspect::ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind()
99+
else {
100+
return false;
101+
};
102+
103+
match (victim_source, other_source) {
104+
(_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => {
105+
bug!("should not have assembled a CoherenceUnknowable candidate")
158106
}
159-
(_, CandidateSource::ParamEnv(_)) => true,
160107

161-
// FIXME: we could prefer earlier vtable bases perhaps...
108+
// Prefer dyn candidates over non-dyn candidates. This is necessary to
109+
// handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
162110
(
163111
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
164112
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
165113
) => false,
166-
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
114+
(
115+
CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound,
116+
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
117+
) => true,
167118

119+
// Prefer specializing candidates over specialized candidates.
168120
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
169-
tcx.specializes((other_def_id, victim_def_id))
170-
&& other.result.value.certainty == Certainty::Yes
121+
victim.goal().infcx().tcx.specializes((other_def_id, victim_def_id))
171122
}
172123

173124
_ => false,
174125
}
175126
}
176127

128+
fn to_selection<'tcx>(
129+
span: Span,
130+
cand: inspect::InspectCandidate<'_, 'tcx>,
131+
) -> Option<Selection<'tcx>> {
132+
if let Certainty::Maybe(..) = cand.shallow_certainty() {
133+
return None;
134+
}
135+
136+
let make_nested = || {
137+
cand.instantiate_nested_goals(span)
138+
.into_iter()
139+
.map(|nested| {
140+
Obligation::new(
141+
nested.infcx().tcx,
142+
ObligationCause::dummy_with_span(span),
143+
nested.goal().param_env,
144+
nested.goal().predicate,
145+
)
146+
})
147+
.collect()
148+
};
149+
150+
Some(match cand.kind() {
151+
ProbeKind::TraitCandidate { source, result: _ } => match source {
152+
CandidateSource::Impl(impl_def_id) => {
153+
// FIXME: Remove this in favor of storing this in the tree
154+
// For impl candidates, we do the rematch manually to compute the args.
155+
ImplSource::UserDefined(rematch_impl(cand.goal(), impl_def_id, span))
156+
}
157+
CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, make_nested()),
158+
CandidateSource::ParamEnv(_) => ImplSource::Param(make_nested()),
159+
CandidateSource::AliasBound => {
160+
ImplSource::Builtin(BuiltinImplSource::Misc, make_nested())
161+
}
162+
CandidateSource::CoherenceUnknowable => {
163+
span_bug!(span, "didn't expect to select an unknowable candidate")
164+
}
165+
},
166+
ProbeKind::TryNormalizeNonRigid { result: _ }
167+
| ProbeKind::NormalizedSelfTyAssembly
168+
| ProbeKind::UnsizeAssembly
169+
| ProbeKind::UpcastProjectionCompatibility
170+
| ProbeKind::OpaqueTypeStorageLookup { result: _ }
171+
| ProbeKind::Root { result: _ } => {
172+
span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind())
173+
}
174+
})
175+
}
176+
177177
fn rematch_impl<'tcx>(
178-
infcx: &InferCtxt<'tcx>,
179-
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
178+
goal: &inspect::InspectGoal<'_, 'tcx>,
180179
impl_def_id: DefId,
181-
) -> SelectionResult<'tcx, Selection<'tcx>> {
182-
let args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
180+
span: Span,
181+
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
182+
let infcx = goal.infcx();
183+
let goal_trait_ref = infcx
184+
.enter_forall_and_leak_universe(goal.goal().predicate.to_opt_poly_trait_pred().unwrap())
185+
.trait_ref;
186+
187+
let args = infcx.fresh_args_for_item(span, impl_def_id);
183188
let impl_trait_ref =
184189
infcx.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(infcx.tcx, args);
185190

186-
let mut nested = infcx
187-
.at(&ObligationCause::dummy(), goal.param_env)
188-
// New solver ignores DefineOpaqueTypes, so choose Yes for consistency
189-
.eq(DefineOpaqueTypes::Yes, goal.predicate.trait_ref, impl_trait_ref)
190-
.map_err(|_| SelectionError::Unimplemented)?
191-
.into_obligations();
191+
let InferOk { value: (), obligations: mut nested } = infcx
192+
.at(&ObligationCause::dummy_with_span(span), goal.goal().param_env)
193+
.eq(DefineOpaqueTypes::Yes, goal_trait_ref, impl_trait_ref)
194+
.expect("rematching impl failed");
195+
196+
// FIXME(-Znext-solver=coinductive): We need to add supertraits here eventually.
192197

193198
nested.extend(
194199
infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, args).into_iter().map(
195-
|(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
196-
),
197-
);
198-
199-
Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
200-
}
201-
202-
/// The `Unsize` trait is particularly important to coercion, so we try rematch it.
203-
/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
204-
/// goal assembly in the solver, both for soundness and in order to avoid ICEs.
205-
fn rematch_unsize<'tcx>(
206-
infcx: &InferCtxt<'tcx>,
207-
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
208-
source: BuiltinImplSource,
209-
certainty: Certainty,
210-
) -> SelectionResult<'tcx, Selection<'tcx>> {
211-
let tcx = infcx.tcx;
212-
let mut nested = vec![];
213-
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
214-
let b_ty = structurally_normalize(
215-
goal.predicate.trait_ref.args.type_at(1),
216-
infcx,
217-
goal.param_env,
218-
&mut nested,
219-
);
220-
221-
match (a_ty.kind(), b_ty.kind()) {
222-
// Don't try to coerce `?0` to `dyn Trait`
223-
(ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None),
224-
// Stall any ambiguous upcasting goals, since we can't rematch those
225-
(ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty {
226-
Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))),
227-
_ => Ok(None),
228-
},
229-
// `T` -> `dyn Trait` upcasting
230-
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
231-
// Check that the type implements all of the predicates of the def-id.
232-
// (i.e. the principal, all of the associated types match, and any auto traits)
233-
nested.extend(data.iter().map(|pred| {
200+
|(clause, _)| {
234201
Obligation::new(
235202
infcx.tcx,
236-
ObligationCause::dummy(),
237-
goal.param_env,
238-
pred.with_self_ty(tcx, a_ty),
203+
ObligationCause::dummy_with_span(span),
204+
goal.goal().param_env,
205+
clause,
239206
)
240-
}));
241-
// The type must be Sized to be unsized.
242-
let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None);
243-
nested.push(Obligation::new(
244-
infcx.tcx,
245-
ObligationCause::dummy(),
246-
goal.param_env,
247-
ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
248-
));
249-
// The type must outlive the lifetime of the `dyn` we're unsizing into.
250-
nested.push(Obligation::new(
251-
infcx.tcx,
252-
ObligationCause::dummy(),
253-
goal.param_env,
254-
ty::OutlivesPredicate(a_ty, region),
255-
));
256-
257-
Ok(Some(ImplSource::Builtin(source, nested)))
258-
}
259-
// `[T; n]` -> `[T]` unsizing
260-
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
261-
nested.extend(
262-
infcx
263-
.at(&ObligationCause::dummy(), goal.param_env)
264-
// New solver ignores DefineOpaqueTypes, so choose Yes for consistency
265-
.eq(DefineOpaqueTypes::Yes, a_elem_ty, b_elem_ty)
266-
.expect("expected rematch to succeed")
267-
.into_obligations(),
268-
);
269-
270-
Ok(Some(ImplSource::Builtin(source, nested)))
271-
}
272-
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
273-
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
274-
if a_def.is_struct() && a_def.did() == b_def.did() =>
275-
{
276-
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
277-
// We must be unsizing some type parameters. This also implies
278-
// that the struct has a tail field.
279-
if unsizing_params.is_empty() {
280-
bug!("expected rematch to succeed")
281-
}
282-
283-
let tail_field = a_def
284-
.non_enum_variant()
285-
.fields
286-
.raw
287-
.last()
288-
.expect("expected unsized ADT to have a tail field");
289-
let tail_field_ty = tcx.type_of(tail_field.did);
290-
291-
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
292-
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
293-
294-
// Instantiate just the unsizing params from B into A. The type after
295-
// this instantiation must be equal to B. This is so we don't unsize
296-
// unrelated type parameters.
297-
let new_a_args = tcx.mk_args_from_iter(
298-
a_args
299-
.iter()
300-
.enumerate()
301-
.map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
302-
);
303-
let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
304-
305-
nested.extend(
306-
infcx
307-
.at(&ObligationCause::dummy(), goal.param_env)
308-
// New solver ignores DefineOpaqueTypes, so choose Yes for consistency
309-
.eq(DefineOpaqueTypes::Yes, unsized_a_ty, b_ty)
310-
.expect("expected rematch to succeed")
311-
.into_obligations(),
312-
);
313-
314-
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
315-
// types.
316-
nested.push(Obligation::new(
317-
tcx,
318-
ObligationCause::dummy(),
319-
goal.param_env,
320-
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
321-
));
322-
323-
Ok(Some(ImplSource::Builtin(source, nested)))
324-
}
325-
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
326-
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
327-
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
328-
{
329-
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
330-
let b_last_ty = b_tys.last().unwrap();
331-
332-
// Instantiate just the tail field of B., and require that they're equal.
333-
let unsized_a_ty =
334-
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
335-
nested.extend(
336-
infcx
337-
.at(&ObligationCause::dummy(), goal.param_env)
338-
// New solver ignores DefineOpaqueTypes, so choose Yes for consistency
339-
.eq(DefineOpaqueTypes::Yes, unsized_a_ty, b_ty)
340-
.expect("expected rematch to succeed")
341-
.into_obligations(),
342-
);
343-
344-
// Similar to ADTs, require that we can unsize the tail.
345-
nested.push(Obligation::new(
346-
tcx,
347-
ObligationCause::dummy(),
348-
goal.param_env,
349-
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
350-
));
351-
352-
// We need to be able to detect tuple unsizing to require its feature gate.
353-
assert_eq!(
354-
source,
355-
BuiltinImplSource::TupleUnsizing,
356-
"compiler-errors wants to know if this can ever be triggered..."
357-
);
358-
Ok(Some(ImplSource::Builtin(source, nested)))
359-
}
360-
_ => {
361-
assert_ne!(certainty, Certainty::Yes);
362-
Ok(None)
363-
}
364-
}
365-
}
207+
},
208+
),
209+
);
366210

367-
fn structurally_normalize<'tcx>(
368-
ty: Ty<'tcx>,
369-
infcx: &InferCtxt<'tcx>,
370-
param_env: ty::ParamEnv<'tcx>,
371-
nested: &mut Vec<PredicateObligation<'tcx>>,
372-
) -> Ty<'tcx> {
373-
if matches!(ty.kind(), ty::Alias(..)) {
374-
let mut engine = <dyn TraitEngine<'tcx>>::new(infcx);
375-
let normalized_ty = infcx
376-
.at(&ObligationCause::dummy(), param_env)
377-
.structurally_normalize(ty, &mut *engine)
378-
.expect("normalization shouldn't fail if we got to here");
379-
nested.extend(engine.pending_obligations());
380-
normalized_ty
381-
} else {
382-
ty
383-
}
211+
ImplSourceUserDefinedData { impl_def_id, nested, args }
384212
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ run-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
fn main() {
7+
let x: &dyn std::any::Any = &1i32;
8+
assert_eq!(x.type_id(), std::any::TypeId::of::<i32>());
9+
}

0 commit comments

Comments
 (0)
Please sign in to comment.