Make everything builtin!

This commit is contained in:
Michael Goulet
2023-07-24 22:02:52 +00:00
parent de81007d13
commit a7ed9c1da7
23 changed files with 345 additions and 444 deletions

View File

@@ -1,25 +1,20 @@
use std::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
use rustc_infer::traits::util::supertraits;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt};
use rustc_infer::traits::{
Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine,
};
use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
use rustc_middle::traits::{
ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
ObligationCause, SelectionError,
BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
use crate::solve::assembly::{Candidate, CandidateSource};
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
use crate::solve::inspect::ProofTreeBuilder;
use crate::solve::search_graph::OverflowHandler;
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
use crate::traits::StructurallyNormalizeExt;
use crate::traits::TraitEngineExt;
@@ -105,47 +100,26 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
rematch_impl(self, goal, def_id, nested_obligations)
}
// Rematching the dyn upcast or object goal will instantiate the same nested
// goals that would have caused the ambiguity, so we can still make progress here
// regardless.
// FIXME: This doesn't actually check the object bounds hold here.
(
_,
CandidateSource::BuiltinImpl(
BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
),
) => rematch_object(self, goal, nested_obligations),
(
Certainty::Maybe(_),
CandidateSource::BuiltinImpl(
BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize,
),
) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => {
rematch_unsize(self, goal, nested_obligations)
}
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize))
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
{
Ok(Some(ImplSource::TupleUnsizing(nested_obligations)))
rematch_unsize(self, goal, nested_obligations, src)
}
// Technically some builtin impls have nested obligations, but if
// `Certainty::Yes`, then they should've all been verified and don't
// need re-checking.
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
Ok(Some(ImplSource::Builtin(nested_obligations)))
(Certainty::Yes, CandidateSource::BuiltinImpl(src)) => {
Ok(Some(ImplSource::Builtin(src, nested_obligations)))
}
// It's fine not to do anything to rematch these, since there are no
// nested obligations.
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations)))
}
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
| (Certainty::Maybe(_), _) => Ok(None),
(Certainty::Maybe(_), _) => Ok(None),
}
}
}
@@ -192,11 +166,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
}
(_, CandidateSource::ParamEnv(_)) => true,
// FIXME: we could prefer earlier vtable bases perhaps...
(
CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
) => false,
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
(_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true,
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
tcx.specializes((other_def_id, victim_def_id))
@@ -234,108 +209,6 @@ fn rematch_impl<'tcx>(
Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested })))
}
fn rematch_object<'tcx>(
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() };
let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty);
let (is_upcasting, target_trait_ref_unnormalized) =
if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() {
assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
let b_ty = structurally_normalize(
goal.predicate.trait_ref.args.type_at(1),
infcx,
goal.param_env,
&mut nested,
);
if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() {
// FIXME: We also need to ensure that the source lifetime outlives the
// target lifetime. This doesn't matter for codegen, though, and only
// *really* matters if the goal's certainty is ambiguous.
(true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty))
} else {
bug!()
}
} else {
(false, ty::Binder::dummy(goal.predicate.trait_ref))
};
let mut target_trait_ref = None;
for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
let result = infcx.commit_if_ok(|_| {
infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
DefineOpaqueTypes::No,
target_trait_ref_unnormalized,
candidate_trait_ref,
)
// FIXME: We probably should at least shallowly verify these...
});
match result {
Ok(InferOk { value: (), obligations }) => {
target_trait_ref = Some(candidate_trait_ref);
nested.extend(obligations);
break;
}
Err(_) => continue,
}
}
let target_trait_ref = target_trait_ref.unwrap();
let mut offset = 0;
let Some((vtable_base, vtable_vptr_slot)) =
prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
match segment {
VtblSegment::MetadataDSA => {
offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
if trait_ref == target_trait_ref {
if emit_vptr {
return ControlFlow::Break((
offset,
Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
));
} else {
return ControlFlow::Break((offset, None));
}
}
offset += own_vtable_entries;
if emit_vptr {
offset += 1;
}
}
}
ControlFlow::Continue(())
})
else {
bug!();
};
// If we're upcasting, get the offset of the vtable pointer, otherwise get
// the base of the vtable.
Ok(Some(if is_upcasting {
// If source and target trait def ids are identical,
// then we are simply removing auto traits.
if source_trait_ref.def_id() == target_trait_ref.def_id() {
ImplSource::Builtin(nested)
} else {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
}
} else {
ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
}))
}
/// The `Unsize` trait is particularly important to coercion, so we try rematch it.
/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
/// goal assembly in the solver, both for soundness and in order to avoid ICEs.
@@ -343,6 +216,7 @@ fn rematch_unsize<'tcx>(
infcx: &InferCtxt<'tcx>,
goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
mut nested: Vec<PredicateObligation<'tcx>>,
source: BuiltinImplSource,
) -> SelectionResult<'tcx, Selection<'tcx>> {
let tcx = infcx.tcx;
let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
@@ -379,6 +253,8 @@ fn rematch_unsize<'tcx>(
goal.param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
));
Ok(Some(ImplSource::Builtin(source, nested)))
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
@@ -389,6 +265,8 @@ fn rematch_unsize<'tcx>(
.expect("expected rematch to succeed")
.into_obligations(),
);
Ok(Some(ImplSource::Builtin(source, nested)))
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
@@ -439,6 +317,8 @@ fn rematch_unsize<'tcx>(
goal.param_env,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
));
Ok(Some(ImplSource::Builtin(source, nested)))
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
@@ -467,15 +347,18 @@ fn rematch_unsize<'tcx>(
));
// We need to be able to detect tuple unsizing to require its feature gate.
return Ok(Some(ImplSource::TupleUnsizing(nested)));
assert_eq!(
source,
BuiltinImplSource::TupleUnsizing,
"compiler-errors wants to know if this can ever be triggered..."
);
Ok(Some(ImplSource::Builtin(source, nested)))
}
// FIXME: We *could* ICE here if either:
// 1. the certainty is `Certainty::Yes`,
// 2. we're in codegen (which should mean `Certainty::Yes`).
_ => return Ok(None),
_ => Ok(None),
}
Ok(Some(ImplSource::Builtin(nested)))
}
fn structurally_normalize<'tcx>(