Skip to content

Commit

Permalink
optimize and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
SparrowLii committed Nov 11, 2022
1 parent a04088b commit 3bea6bc
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 310 deletions.
254 changes: 0 additions & 254 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,260 +113,6 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
tcx.calculate_dtor(def_id, dropck::check_drop_impl)
}

/// If this `DefId` is a "primary tables entry", returns
/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
///
/// If this function returns `Some`, then `typeck_results(def_id)` will
/// succeed; if it returns `None`, then `typeck_results(def_id)` may or
/// may not succeed. In some cases where this function returns `None`
/// (notably closures), `typeck_results(def_id)` would wind up
/// redirecting to the owning function.
fn primary_body_of(
tcx: TyCtxt<'_>,
id: hir::HirId,
) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
match tcx.hir().get(id) {
Node::Item(item) => match item.kind {
hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
Some((body, Some(ty), None))
}
hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
_ => None,
},
Node::TraitItem(item) => match item.kind {
hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)),
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
Some((body, None, Some(sig)))
}
_ => None,
},
Node::ImplItem(item) => match item.kind {
hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)),
hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))),
_ => None,
},
Node::AnonConst(constant) => Some((constant.body, None, None)),
_ => None,
}
}

fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
// Closures' typeck results come from their outermost function,
// as they are part of the same "inference environment".
let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
if typeck_root_def_id != def_id {
return tcx.has_typeck_results(typeck_root_def_id);
}

if let Some(def_id) = def_id.as_local() {
let id = tcx.hir().local_def_id_to_hir_id(def_id);
primary_body_of(tcx, id).is_some()
} else {
false
}
}

fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> {
&*tcx.typeck(def_id).used_trait_imports
}

fn typeck_const_arg<'tcx>(
tcx: TyCtxt<'tcx>,
(did, param_did): (LocalDefId, DefId),
) -> &ty::TypeckResults<'tcx> {
let fallback = move || tcx.type_of(param_did);
typeck_with_fallback(tcx, did, fallback)
}

fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
if let Some(param_did) = tcx.opt_const_param_of(def_id) {
tcx.typeck_const_arg((def_id, param_did))
} else {
let fallback = move || tcx.type_of(def_id.to_def_id());
typeck_with_fallback(tcx, def_id, fallback)
}
}

/// Used only to get `TypeckResults` for type inference during error recovery.
/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
let fallback = move || {
let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
tcx.ty_error_with_message(span, "diagnostic only typeck table used")
};
typeck_with_fallback(tcx, def_id, fallback)
}

fn typeck_with_fallback<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
fallback: impl Fn() -> Ty<'tcx> + 'tcx,
) -> &'tcx ty::TypeckResults<'tcx> {
// Closures' typeck results come from their outermost function,
// as they are part of the same "inference environment".
let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
if typeck_root_def_id != def_id {
return tcx.typeck(typeck_root_def_id);
}

let id = tcx.hir().local_def_id_to_hir_id(def_id);
let span = tcx.hir().span(id);

// Figure out what primary body this item has.
let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
span_bug!(span, "can't type-check body of {:?}", def_id);
});
let body = tcx.hir().body(body_id);

let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
let param_env = tcx.param_env(def_id);
let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
<dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id)
};

check_abi(tcx, id, span, fn_sig.abi());

// Compute the function signature from point of view of inside the fn.
let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
let fn_sig = inh.normalize_associated_types_in(
body.value.span,
body_id.hir_id,
param_env,
fn_sig,
);
check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0
} else {
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
let expected_type = body_ty
.and_then(|ty| match ty.kind {
hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
_ => None,
})
.unwrap_or_else(|| match tcx.hir().get(id) {
Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) {
Node::Expr(&hir::Expr {
kind: hir::ExprKind::ConstBlock(ref anon_const),
..
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span,
}),
Node::Ty(&hir::Ty {
kind: hir::TyKind::Typeof(ref anon_const), ..
}) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span,
}),
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
let operand_ty = asm
.operands
.iter()
.filter_map(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const }
if anon_const.hir_id == id =>
{
// Inline assembly constants must be integers.
Some(fcx.next_int_var())
}
hir::InlineAsmOperand::SymFn { anon_const }
if anon_const.hir_id == id =>
{
Some(fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span,
}))
}
_ => None,
})
.next();
operand_ty.unwrap_or_else(fallback)
}
_ => fallback(),
},
_ => fallback(),
});

let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type);
fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);

// Gather locals in statics (because of block expressions).
GatherLocalsVisitor::new(&fcx).visit_body(body);

fcx.check_expr_coercable_to_type(&body.value, expected_type, None);

fcx.write_ty(id, expected_type);

fcx
};

let fallback_has_occurred = fcx.type_inference_fallback();

// Even though coercion casts provide type hints, we check casts after fallback for
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
fcx.check_casts();
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});

// Closure and generator analysis may run after fallback
// because they don't constrain other type variables.
// Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
let prev_constness = fcx.param_env.constness();
fcx.param_env = fcx.param_env.without_const();
fcx.closure_analyze(body);
fcx.param_env = fcx.param_env.with_constness(prev_constness);
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
// Before the generator analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured.
fcx.resolve_rvalue_scopes(def_id.to_def_id());
fcx.resolve_generator_interiors(def_id.to_def_id());

for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize_ty(span, ty);
fcx.require_type_is_sized(ty, span, code);
}

fcx.resolve_base_expr();

fcx.select_all_obligations_or_error();

if !fcx.infcx.is_tainted_by_errors() {
fcx.check_transmutes();
}

fcx.check_asms();

fcx.infcx.skip_region_resolution();

fcx.resolve_type_vars_in_body(body)
});

// Consistency check our TypeckResults instance can hold all ItemLocalIds
// it will need to hold.
assert_eq!(typeck_results.hir_owner, id.owner);

typeck_results
}

/// When `check_fn` is invoked on a generator (i.e., a body that
/// includes yield), it returns back some information about the yield
/// points.
struct GeneratorTypes<'tcx> {
/// Type of generator argument / values returned by `yield`.
resume_ty: Ty<'tcx>,

/// Type of value that is yielded.
yield_ty: Ty<'tcx>,

/// Types that are captured (see `GeneratorInterior` for more).
interior: Ty<'tcx>,

/// Indicates if the generator is movable or static (immovable).
movability: hir::Movability,
}
/// Given a `DefId` for an opaque type in return position, find its parent item's return
/// expressions.
fn get_owner_return_paths<'tcx>(
Expand Down
14 changes: 5 additions & 9 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
use crate::FnCtxt;
use rustc_infer::infer::base_struct::base_struct;
use rustc_infer::infer::InferOk;
use rustc_middle::middle::stability::EvalResult;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::ObligationCause;
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{is_range_literal, Node};
use rustc_infer::infer::base_struct::base_struct;
use rustc_infer::infer::InferOk;
use rustc_middle::lint::in_external_macro;
use rustc_middle::middle::stability::EvalResult;
Expand Down Expand Up @@ -183,16 +179,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn demand_base_struct(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
adt_ty: Ty<'tcx>,
base_ty: Ty<'tcx>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
let at = self.at(cause, self.param_env);
match base_struct(at, expected, actual) {
match base_struct(at, adt_ty, base_ty) {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
None
}
Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, adt_ty, base_ty, e)),
}
}

Expand Down
27 changes: 11 additions & 16 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1667,16 +1667,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.register_predicates(obligations)
}
Err(_) => {
// This should never happen, since we're just subtyping the
// remaining_fields, but it's fine to emit this, I guess.
self.err_ctxt()
.report_mismatched_types(
&cause,
target_ty,
fru_ty,
FieldMisMatch(variant.name, ident.name),
)
.emit(); }
bug!("subtype fresh substs failed")
}
}
}
self.resolve_vars_if_possible(fru_ty)
Expand All @@ -1701,12 +1693,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This is important to allow coercions to happen in
// `other_struct` itself. See `coerce-in-base-expr.rs`.
let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
self.check_expr_has_type_or_error(
base_expr,
fresh_base_ty,
|_| {},
);
self.typeck_results.borrow_mut().base_expr_backup_mut().insert(base_expr.hir_id, (fresh_base_ty, adt_ty));
self.check_expr_has_type_or_error(base_expr, fresh_base_ty, |_| {});
let fresh_base_ty = self.resolve_vars_if_possible(fresh_base_ty);
if fresh_base_ty.needs_infer() {
self.typeck_results.borrow_mut().base_expr_backup.push((
base_expr.span,
adt_ty,
fresh_base_ty,
));
}
fru_tys
} else {
// Check the base_expr, regardless of a bad expected adt_ty, so we can get
Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

pub(in super::super) fn resolve_base_expr(&self) {
for (_, (base_ty, adt_ty)) in self.typeck_results.borrow().base_expr_backup().iter() {
for (span, adt_ty, base_ty) in self.typeck_results.borrow().base_expr_backup.iter() {
let base_ty = self.resolve_vars_if_possible(*base_ty);
if base_ty.has_infer_types() {
if let Some(mut err) =
self.demand_base_struct(&self.misc(DUMMY_SP), *adt_ty, base_ty)
if base_ty.needs_infer() {
if let Some(mut err) = self.demand_base_struct(&self.misc(*span), *adt_ty, base_ty)
{
err.emit();
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ fn typeck_with_fallback<'tcx>(
fcx.require_type_is_sized(ty, span, code);
}

// If the base structs used to creat structs still remain infer generics,
// unify it with the created ones.
fcx.resolve_base_expr();

fcx.select_all_obligations_or_error();

if !fcx.infcx.is_tainted_by_errors() {
Expand Down
Loading

0 comments on commit 3bea6bc

Please sign in to comment.