Skip to content

Commit b63aea5

Browse files
committed
demand_base_struct
1 parent 9b735a7 commit b63aea5

File tree

12 files changed

+479
-18
lines changed

12 files changed

+479
-18
lines changed

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,260 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
113113
tcx.calculate_dtor(def_id, dropck::check_drop_impl)
114114
}
115115

116+
/// If this `DefId` is a "primary tables entry", returns
117+
/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
118+
///
119+
/// If this function returns `Some`, then `typeck_results(def_id)` will
120+
/// succeed; if it returns `None`, then `typeck_results(def_id)` may or
121+
/// may not succeed. In some cases where this function returns `None`
122+
/// (notably closures), `typeck_results(def_id)` would wind up
123+
/// redirecting to the owning function.
124+
fn primary_body_of(
125+
tcx: TyCtxt<'_>,
126+
id: hir::HirId,
127+
) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
128+
match tcx.hir().get(id) {
129+
Node::Item(item) => match item.kind {
130+
hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
131+
Some((body, Some(ty), None))
132+
}
133+
hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
134+
_ => None,
135+
},
136+
Node::TraitItem(item) => match item.kind {
137+
hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)),
138+
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
139+
Some((body, None, Some(sig)))
140+
}
141+
_ => None,
142+
},
143+
Node::ImplItem(item) => match item.kind {
144+
hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)),
145+
hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))),
146+
_ => None,
147+
},
148+
Node::AnonConst(constant) => Some((constant.body, None, None)),
149+
_ => None,
150+
}
151+
}
152+
153+
fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
154+
// Closures' typeck results come from their outermost function,
155+
// as they are part of the same "inference environment".
156+
let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
157+
if typeck_root_def_id != def_id {
158+
return tcx.has_typeck_results(typeck_root_def_id);
159+
}
160+
161+
if let Some(def_id) = def_id.as_local() {
162+
let id = tcx.hir().local_def_id_to_hir_id(def_id);
163+
primary_body_of(tcx, id).is_some()
164+
} else {
165+
false
166+
}
167+
}
168+
169+
fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> {
170+
&*tcx.typeck(def_id).used_trait_imports
171+
}
172+
173+
fn typeck_const_arg<'tcx>(
174+
tcx: TyCtxt<'tcx>,
175+
(did, param_did): (LocalDefId, DefId),
176+
) -> &ty::TypeckResults<'tcx> {
177+
let fallback = move || tcx.type_of(param_did);
178+
typeck_with_fallback(tcx, did, fallback)
179+
}
180+
181+
fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
182+
if let Some(param_did) = tcx.opt_const_param_of(def_id) {
183+
tcx.typeck_const_arg((def_id, param_did))
184+
} else {
185+
let fallback = move || tcx.type_of(def_id.to_def_id());
186+
typeck_with_fallback(tcx, def_id, fallback)
187+
}
188+
}
189+
190+
/// Used only to get `TypeckResults` for type inference during error recovery.
191+
/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
192+
fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
193+
let fallback = move || {
194+
let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
195+
tcx.ty_error_with_message(span, "diagnostic only typeck table used")
196+
};
197+
typeck_with_fallback(tcx, def_id, fallback)
198+
}
199+
200+
fn typeck_with_fallback<'tcx>(
201+
tcx: TyCtxt<'tcx>,
202+
def_id: LocalDefId,
203+
fallback: impl Fn() -> Ty<'tcx> + 'tcx,
204+
) -> &'tcx ty::TypeckResults<'tcx> {
205+
// Closures' typeck results come from their outermost function,
206+
// as they are part of the same "inference environment".
207+
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
208+
if typeck_root_def_id != def_id {
209+
return tcx.typeck(typeck_root_def_id);
210+
}
211+
212+
let id = tcx.hir().local_def_id_to_hir_id(def_id);
213+
let span = tcx.hir().span(id);
214+
215+
// Figure out what primary body this item has.
216+
let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
217+
span_bug!(span, "can't type-check body of {:?}", def_id);
218+
});
219+
let body = tcx.hir().body(body_id);
220+
221+
let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
222+
let param_env = tcx.param_env(def_id);
223+
let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
224+
let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
225+
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
226+
<dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
227+
} else {
228+
tcx.fn_sig(def_id)
229+
};
230+
231+
check_abi(tcx, id, span, fn_sig.abi());
232+
233+
// Compute the function signature from point of view of inside the fn.
234+
let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
235+
let fn_sig = inh.normalize_associated_types_in(
236+
body.value.span,
237+
body_id.hir_id,
238+
param_env,
239+
fn_sig,
240+
);
241+
check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0
242+
} else {
243+
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
244+
let expected_type = body_ty
245+
.and_then(|ty| match ty.kind {
246+
hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
247+
_ => None,
248+
})
249+
.unwrap_or_else(|| match tcx.hir().get(id) {
250+
Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) {
251+
Node::Expr(&hir::Expr {
252+
kind: hir::ExprKind::ConstBlock(ref anon_const),
253+
..
254+
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
255+
kind: TypeVariableOriginKind::TypeInference,
256+
span,
257+
}),
258+
Node::Ty(&hir::Ty {
259+
kind: hir::TyKind::Typeof(ref anon_const), ..
260+
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
261+
kind: TypeVariableOriginKind::TypeInference,
262+
span,
263+
}),
264+
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
265+
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
266+
let operand_ty = asm
267+
.operands
268+
.iter()
269+
.filter_map(|(op, _op_sp)| match op {
270+
hir::InlineAsmOperand::Const { anon_const }
271+
if anon_const.hir_id == id =>
272+
{
273+
// Inline assembly constants must be integers.
274+
Some(fcx.next_int_var())
275+
}
276+
hir::InlineAsmOperand::SymFn { anon_const }
277+
if anon_const.hir_id == id =>
278+
{
279+
Some(fcx.next_ty_var(TypeVariableOrigin {
280+
kind: TypeVariableOriginKind::MiscVariable,
281+
span,
282+
}))
283+
}
284+
_ => None,
285+
})
286+
.next();
287+
operand_ty.unwrap_or_else(fallback)
288+
}
289+
_ => fallback(),
290+
},
291+
_ => fallback(),
292+
});
293+
294+
let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type);
295+
fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
296+
297+
// Gather locals in statics (because of block expressions).
298+
GatherLocalsVisitor::new(&fcx).visit_body(body);
299+
300+
fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
301+
302+
fcx.write_ty(id, expected_type);
303+
304+
fcx
305+
};
306+
307+
let fallback_has_occurred = fcx.type_inference_fallback();
308+
309+
// Even though coercion casts provide type hints, we check casts after fallback for
310+
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
311+
fcx.check_casts();
312+
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
313+
314+
// Closure and generator analysis may run after fallback
315+
// because they don't constrain other type variables.
316+
// Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
317+
let prev_constness = fcx.param_env.constness();
318+
fcx.param_env = fcx.param_env.without_const();
319+
fcx.closure_analyze(body);
320+
fcx.param_env = fcx.param_env.with_constness(prev_constness);
321+
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
322+
// Before the generator analysis, temporary scopes shall be marked to provide more
323+
// precise information on types to be captured.
324+
fcx.resolve_rvalue_scopes(def_id.to_def_id());
325+
fcx.resolve_generator_interiors(def_id.to_def_id());
326+
327+
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
328+
let ty = fcx.normalize_ty(span, ty);
329+
fcx.require_type_is_sized(ty, span, code);
330+
}
331+
332+
fcx.resolve_base_expr();
333+
334+
fcx.select_all_obligations_or_error();
335+
336+
if !fcx.infcx.is_tainted_by_errors() {
337+
fcx.check_transmutes();
338+
}
339+
340+
fcx.check_asms();
341+
342+
fcx.infcx.skip_region_resolution();
343+
344+
fcx.resolve_type_vars_in_body(body)
345+
});
346+
347+
// Consistency check our TypeckResults instance can hold all ItemLocalIds
348+
// it will need to hold.
349+
assert_eq!(typeck_results.hir_owner, id.owner);
350+
351+
typeck_results
352+
}
353+
354+
/// When `check_fn` is invoked on a generator (i.e., a body that
355+
/// includes yield), it returns back some information about the yield
356+
/// points.
357+
struct GeneratorTypes<'tcx> {
358+
/// Type of generator argument / values returned by `yield`.
359+
resume_ty: Ty<'tcx>,
360+
361+
/// Type of value that is yielded.
362+
yield_ty: Ty<'tcx>,
363+
364+
/// Types that are captured (see `GeneratorInterior` for more).
365+
interior: Ty<'tcx>,
366+
367+
/// Indicates if the generator is movable or static (immovable).
368+
movability: hir::Movability,
369+
}
116370
/// Given a `DefId` for an opaque type in return position, find its parent item's return
117371
/// expressions.
118372
fn get_owner_return_paths<'tcx>(

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
174174
(expected, Some(err))
175175
}
176176

177+
#[instrument(skip(self), level = "debug")]
178+
pub fn demand_base_struct(
179+
&self,
180+
cause: &ObligationCause<'tcx>,
181+
expected: Ty<'tcx>,
182+
actual: Ty<'tcx>,
183+
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
184+
match self.at(cause, self.param_env).base_struct(expected, actual) {
185+
Ok(InferOk { obligations, value: () }) => {
186+
self.register_predicates(obligations);
187+
None
188+
}
189+
Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
190+
}
191+
}
192+
177193
fn annotate_expected_due_to_let_ty(
178194
&self,
179195
err: &mut Diagnostic,

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use rustc_infer::infer::InferOk;
3939
use rustc_infer::traits::ObligationCause;
4040
use rustc_middle::middle::stability;
4141
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
42-
use rustc_middle::ty::error::TypeError::FieldMisMatch;
4342
use rustc_middle::ty::subst::SubstsRef;
4443
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
4544
use rustc_session::errors::ExprParenthesesNeeded;
@@ -1677,8 +1676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16771676
fru_ty,
16781677
FieldMisMatch(variant.name, ident.name),
16791678
)
1680-
.emit();
1681-
}
1679+
.emit(); }
16821680
}
16831681
}
16841682
self.resolve_vars_if_possible(fru_ty)
@@ -1705,9 +1703,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17051703
let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
17061704
self.check_expr_has_type_or_error(
17071705
base_expr,
1708-
self.resolve_vars_if_possible(fresh_base_ty),
1706+
fresh_base_ty,
17091707
|_| {},
17101708
);
1709+
self.typeck_results.borrow_mut().base_expr_backup_mut().insert(base_expr.hir_id, (fresh_base_ty, adt_ty));
17111710
fru_tys
17121711
} else {
17131712
// Check the base_expr, regardless of a bad expected adt_ty, so we can get

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
605605
}
606606
}
607607

608+
pub(in super::super) fn resolve_base_expr(&self) {
609+
for (_, (base_ty, adt_ty)) in self.typeck_results.borrow().base_expr_backup().iter() {
610+
let base_ty = self.resolve_vars_if_possible(*base_ty);
611+
if base_ty.has_infer_types() {
612+
if let Some(mut err) =
613+
self.demand_base_struct(&self.misc(DUMMY_SP), *adt_ty, base_ty)
614+
{
615+
err.emit();
616+
}
617+
}
618+
}
619+
}
620+
608621
#[instrument(skip(self), level = "debug")]
609622
pub(in super::super) fn select_all_obligations_or_error(&self) {
610623
let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);

compiler/rustc_infer/src/infer/at.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,17 @@ impl<'a, 'tcx> At<'a, 'tcx> {
218218
self.trace(expected, actual).glb(expected, actual)
219219
}
220220

221+
pub fn base_struct<T>(
222+
self,
223+
expected: T,
224+
actual: T,
225+
) -> InferResult<'tcx, ()>
226+
where
227+
T: ToTrace<'tcx>,
228+
{
229+
self.trace(expected, actual).base_struct(expected, actual)
230+
}
231+
221232
/// Sets the "trace" values that will be used for
222233
/// error-reporting, but doesn't actually perform any operation
223234
/// yet (this is useful when you want to set the trace using
@@ -259,6 +270,21 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
259270
})
260271
}
261272

273+
#[instrument(skip(self), level = "debug")]
274+
pub fn base_struct<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
275+
where
276+
T: Relate<'tcx>,
277+
{
278+
let Trace { at, trace, a_is_expected } = self;
279+
at.infcx.commit_if_ok(|_| {
280+
let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types);
281+
fields
282+
.base_struct(a_is_expected)
283+
.relate(a, b)
284+
.map(move |_| InferOk { value: (), obligations: fields.obligations })
285+
})
286+
}
287+
262288
/// Makes `a == b`; the expectation is set by the call to
263289
/// `trace()`.
264290
#[instrument(skip(self), level = "debug")]

0 commit comments

Comments
 (0)