Skip to content

Commit

Permalink
Rollup merge of rust-lang#89561 - nbdd0121:const_typeck, r=nikomatsakis
Browse files Browse the repository at this point in the history
Type inference for inline consts

Fixes rust-lang#78132
Fixes rust-lang#78174
Fixes rust-lang#81857
Fixes rust-lang#89964

Perform type checking/inference of inline consts in the same context as the outer def, similar to what is currently done to closure.

Doing so would require `closure_base_def_id` of the inline const to return the outer def, and since `closure_base_def_id` can be called on non-local crate (and thus have no HIR available), a new `DefKind` is created for inline consts.

The type of the generated anon const can capture lifetime of outer def, so we couldn't just use the typeck result as the type of the inline const's def. Closure has a similar issue, and it uses extra type params `CK, CS, U` to capture closure kind, input/output signature and upvars. I use a similar approach for inline consts, letting it have an extra type param `R`, and then `typeof(InlineConst<[paremt generics], R>)` would just be `R`. In borrowck region requirements are also propagated to the outer MIR body just like it's currently done for closure.

With this PR, inline consts in expression position are quitely usable now; however the usage in pattern position is still incomplete -- since those does not remain in the MIR borrowck couldn't verify the lifetime there. I have left an ignored test as a FIXME.

Some disucssions can be found on [this Zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/260443-project-const-generics/topic/inline.20consts.20typeck).
cc ```@spastorino``` ```@lcnr```
r? ```@nikomatsakis```

```@rustbot``` label A-inference F-inline_const T-compiler
  • Loading branch information
matthiaskrgr authored Nov 8, 2021
2 parents efc320c c4103d4 commit e4ce370
Show file tree
Hide file tree
Showing 49 changed files with 656 additions and 119 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 408,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let param = generics.type_param(&param_ty, tcx);
if let Some(generics) = tcx
.hir()
.get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id()))
.get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id()))
{
suggest_constraining_type_param(
tcx,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 376,7 @@ pub(super) fn dump_annotation<'a, 'tcx>(
errors_buffer: &mut Vec<Diagnostic>,
) {
let tcx = infcx.tcx;
let base_def_id = tcx.closure_base_def_id(body.source.def_id());
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
if !tcx.has_attr(base_def_id, sym::rustc_regions) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 569,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// to store those. Otherwise, we'll pass in `None` to the
// functions below, which will trigger them to report errors
// eagerly.
let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new);
let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new);

self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);

Expand Down Expand Up @@ -2229,7 2229,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
tcx,
closure_substs,
self.num_external_vids,
tcx.closure_base_def_id(closure_def_id),
tcx.typeck_root_def_id(closure_def_id),
);
debug!("apply_requirements: closure_mapping={:?}", closure_mapping);

Expand Down
101 changes: 88 additions & 13 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::vec_map::VecMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::{Idx, IndexVec};
Expand Down Expand Up @@ -1343,13 1344,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// though.
let category = match place.as_local() {
Some(RETURN_PLACE) => {
if let BorrowCheckContext {
universal_regions:
UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
..
} = self.borrowck_context
{
if tcx.is_static(*def_id) {
let defining_ty = &self.borrowck_context.universal_regions.defining_ty;
if defining_ty.is_const() {
if tcx.is_static(defining_ty.def_id()) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
Expand Down Expand Up @@ -1527,6 1524,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
self.check_operand(discr, term_location);

let discr_ty = discr.ty(body, tcx);
if let Err(terr) = self.sub_types(
discr_ty,
Expand All @@ -1549,6 1548,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// FIXME: check the values
}
TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => {
self.check_operand(func, term_location);
for arg in args {
self.check_operand(arg, term_location);
}

let func_ty = func.ty(body, tcx);
debug!("check_terminator: call, func_ty={:?}", func_ty);
let sig = match func_ty.kind() {
Expand Down Expand Up @@ -1593,6 1597,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call);
}
TerminatorKind::Assert { ref cond, ref msg, .. } => {
self.check_operand(cond, term_location);

let cond_ty = cond.ty(body, tcx);
if cond_ty != tcx.types.bool {
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
Expand All @@ -1608,6 1614,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::Yield { ref value, .. } => {
self.check_operand(value, term_location);

let value_ty = value.ty(body, tcx);
match body.yield_ty() {
None => span_mirbug!(self, term, "yield in non-generator"),
Expand Down Expand Up @@ -1650,7 1658,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Some(RETURN_PLACE) => {
if let BorrowCheckContext {
universal_regions:
UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
UniversalRegions {
defining_ty:
DefiningTy::Const(def_id, _)
| DefiningTy::InlineConst(def_id, _),
..
},
..
} = self.borrowck_context
{
Expand Down Expand Up @@ -1931,15 1944,51 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}

fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
if let Operand::Constant(constant) = op {
let maybe_uneval = match constant.literal {
ConstantKind::Ty(ct) => match ct.val {
ty::ConstKind::Unevaluated(uv) => Some(uv),
_ => None,
},
_ => None,
};
if let Some(uv) = maybe_uneval {
if uv.promoted.is_none() {
let tcx = self.tcx();
let def_id = uv.def.def_id_for_type_of();
if tcx.def_kind(def_id) == DefKind::InlineConst {
let predicates = self.prove_closure_bounds(
tcx,
def_id.expect_local(),
uv.substs(tcx),
location,
);
self.normalize_and_prove_instantiated_predicates(
def_id,
predicates,
location.to_locations(),
);
}
}
}
}
}

fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
let tcx = self.tcx();

match rvalue {
Rvalue::Aggregate(ak, ops) => {
for op in ops {
self.check_operand(op, location);
}
self.check_aggregate_rvalue(&body, rvalue, ak, ops, location)
}

Rvalue::Repeat(operand, len) => {
self.check_operand(operand, location);

// If the length cannot be evaluated we must assume that the length can be larger
// than 1.
// If the length is larger than 1, the repeat expression will need to copy the
Expand Down Expand Up @@ -1990,7 2039,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}

Rvalue::NullaryOp(_, ty) | Rvalue::ShallowInitBox(_, ty) => {
Rvalue::NullaryOp(_, ty) => {
let trait_ref = ty::TraitRef {
def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
substs: tcx.mk_substs_trait(ty, &[]),
};

self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::SizedBound,
);
}

Rvalue::ShallowInitBox(operand, ty) => {
self.check_operand(operand, location);

let trait_ref = ty::TraitRef {
def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
substs: tcx.mk_substs_trait(ty, &[]),
Expand All @@ -2004,6 2068,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}

Rvalue::Cast(cast_kind, op, ty) => {
self.check_operand(op, location);

match cast_kind {
CastKind::Pointer(PointerCast::ReifyFnPointer) => {
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
Expand Down Expand Up @@ -2250,6 2316,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
box (left, right),
) => {
self.check_operand(left, location);
self.check_operand(right, location);

let ty_left = left.ty(body, tcx);
match ty_left.kind() {
// Types with regions are comparable if they have a common super-type.
Expand Down Expand Up @@ -2300,13 2369,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}

Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => {
self.check_operand(operand, location);
}

Rvalue::BinaryOp(_, box (left, right))
| Rvalue::CheckedBinaryOp(_, box (left, right)) => {
self.check_operand(left, location);
self.check_operand(right, location);
}

Rvalue::AddressOf(..)
| Rvalue::ThreadLocalRef(..)
| Rvalue::Use(..)
| Rvalue::Len(..)
| Rvalue::BinaryOp(..)
| Rvalue::CheckedBinaryOp(..)
| Rvalue::UnaryOp(..)
| Rvalue::Discriminant(..) => {}
}
}
Expand Down
Loading

0 comments on commit e4ce370

Please sign in to comment.