about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/lib.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-09-27 10:45:57 +0000
committerbors <bors@rust-lang.org>2022-09-27 10:45:57 +0000
commit57ee5cf5a93923dae9c98bffb11545fc3a31368d (patch)
treeabd98065e805dca388767b651ae60673397fea9d /compiler/rustc_hir_analysis/src/lib.rs
parentd9297d22ad9edc2b56f0dd8734c1187a0c88be69 (diff)
parent1fc86a63f451b81606e4787692517dc613f333db (diff)
downloadrust-57ee5cf5a93923dae9c98bffb11545fc3a31368d.tar.gz
rust-57ee5cf5a93923dae9c98bffb11545fc3a31368d.zip
Auto merge of #102306 - lcnr:rustc_hir_analysis, r=compiler-errors
rename rustc_typeck to rustc_hir_analysis

first part of https://github.com/rust-lang/compiler-team/issues/529

r? `@compiler-errors`
Diffstat (limited to 'compiler/rustc_hir_analysis/src/lib.rs')
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs555
1 files changed, 555 insertions, 0 deletions
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
new file mode 100644
index 00000000000..1859473166a
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -0,0 +1,555 @@
+/*!
+
+# typeck
+
+The type checker is responsible for:
+
+1. Determining the type of each expression.
+2. Resolving methods and traits.
+3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?"
+   Well, dear reader, read on.)
+
+The main entry point is [`check_crate()`]. Type checking operates in
+several major phases:
+
+1. The collect phase first passes over all items and determines their
+   type, without examining their "innards".
+
+2. Variance inference then runs to compute the variance of each parameter.
+
+3. Coherence checks for overlapping or orphaned impls.
+
+4. Finally, the check phase then checks function bodies and so forth.
+   Within the check phase, we check each function body one at a time
+   (bodies of function expressions are checked as part of the
+   containing function).  Inference is used to supply types wherever
+   they are unknown. The actual checking of a function itself has
+   several phases (check, regionck, writeback), as discussed in the
+   documentation for the [`check`] module.
+
+The type checker is defined into various submodules which are documented
+independently:
+
+- astconv: converts the AST representation of types
+  into the `ty` representation.
+
+- collect: computes the types of each top-level item and enters them into
+  the `tcx.types` table for later use.
+
+- coherence: enforces coherence rules, builds some tables.
+
+- variance: variance inference
+
+- outlives: outlives inference
+
+- check: walks over function bodies and type checks them, inferring types for
+  local variables, type parameters, etc as necessary.
+
+- infer: finds the types to use for each type variable such that
+  all subtyping and assignment constraints are met.  In essence, the check
+  module specifies the constraints, and the infer module solves them.
+
+## Note
+
+This API is completely unstable and subject to change.
+
+*/
+
+#![allow(rustc::potential_query_instability)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(drain_filter)]
+#![feature(hash_drain_filter)]
+#![feature(if_let_guard)]
+#![feature(is_sorted)]
+#![feature(iter_intersperse)]
+#![feature(let_chains)]
+#![feature(min_specialization)]
+#![feature(never_type)]
+#![feature(once_cell)]
+#![feature(slice_partition_dedup)]
+#![feature(try_blocks)]
+#![feature(is_some_with)]
+#![feature(type_alias_impl_trait)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate tracing;
+
+#[macro_use]
+extern crate rustc_middle;
+
+// These are used by Clippy.
+pub mod check;
+pub mod expr_use_visitor;
+
+mod astconv;
+mod bounds;
+mod check_unused;
+mod coherence;
+mod collect;
+mod constrained_generic_params;
+mod errors;
+pub mod hir_wf_check;
+mod impl_wf_check;
+mod mem_categorization;
+mod outlives;
+mod structured_errors;
+mod variance;
+
+use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_infer::infer::{InferOk, TyCtxtInferExt};
+use rustc_middle::middle;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::util;
+use rustc_session::config::EntryFnType;
+use rustc_span::{symbol::sym, Span, DUMMY_SP};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
+
+use std::iter;
+
+use astconv::AstConv;
+use bounds::Bounds;
+
+fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
+    match (decl.c_variadic, abi) {
+        // The function has the correct calling convention, or isn't a "C-variadic" function.
+        (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
+        // The function is a "C-variadic" function with an incorrect calling convention.
+        (true, _) => {
+            let mut err = struct_span_err!(
+                tcx.sess,
+                span,
+                E0045,
+                "C-variadic function must have C or cdecl calling convention"
+            );
+            err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
+        }
+    }
+}
+
+fn require_same_types<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    cause: &ObligationCause<'tcx>,
+    expected: Ty<'tcx>,
+    actual: Ty<'tcx>,
+) -> bool {
+    tcx.infer_ctxt().enter(|ref infcx| {
+        let param_env = ty::ParamEnv::empty();
+        let errors = match infcx.at(cause, param_env).eq(expected, actual) {
+            Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations),
+            Err(err) => {
+                infcx.report_mismatched_types(cause, expected, actual, err).emit();
+                return false;
+            }
+        };
+
+        match &errors[..] {
+            [] => true,
+            errors => {
+                infcx.report_fulfillment_errors(errors, None, false);
+                false
+            }
+        }
+    })
+}
+
+fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
+    let main_fnsig = tcx.fn_sig(main_def_id);
+    let main_span = tcx.def_span(main_def_id);
+
+    fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId {
+        if let Some(local_def_id) = def_id.as_local() {
+            let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
+            let hir_type = tcx.type_of(local_def_id);
+            if !matches!(hir_type.kind(), ty::FnDef(..)) {
+                span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
+            }
+            hir_id
+        } else {
+            CRATE_HIR_ID
+        }
+    }
+
+    fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+        if !def_id.is_local() {
+            return None;
+        }
+        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+        match tcx.hir().find(hir_id) {
+            Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
+                if !generics.params.is_empty() {
+                    Some(generics.span)
+                } else {
+                    None
+                }
+            }
+            _ => {
+                span_bug!(tcx.def_span(def_id), "main has a non-function type");
+            }
+        }
+    }
+
+    fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+        if !def_id.is_local() {
+            return None;
+        }
+        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+        match tcx.hir().find(hir_id) {
+            Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
+                Some(generics.where_clause_span)
+            }
+            _ => {
+                span_bug!(tcx.def_span(def_id), "main has a non-function type");
+            }
+        }
+    }
+
+    fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+        if !def_id.is_local() {
+            return None;
+        }
+        Some(tcx.def_span(def_id))
+    }
+
+    fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+        if !def_id.is_local() {
+            return None;
+        }
+        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+        match tcx.hir().find(hir_id) {
+            Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => {
+                Some(fn_sig.decl.output.span())
+            }
+            _ => {
+                span_bug!(tcx.def_span(def_id), "main has a non-function type");
+            }
+        }
+    }
+
+    let mut error = false;
+    let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span);
+    let main_fn_generics = tcx.generics_of(main_def_id);
+    let main_fn_predicates = tcx.predicates_of(main_def_id);
+    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
+        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
+        let msg = "`main` function is not allowed to have generic \
+            parameters";
+        let mut diag =
+            struct_span_err!(tcx.sess, generics_param_span.unwrap_or(main_span), E0131, "{}", msg);
+        if let Some(generics_param_span) = generics_param_span {
+            let label = "`main` cannot have generic parameters";
+            diag.span_label(generics_param_span, label);
+        }
+        diag.emit();
+        error = true;
+    } else if !main_fn_predicates.predicates.is_empty() {
+        // generics may bring in implicit predicates, so we skip this check if generics is present.
+        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
+        let mut diag = struct_span_err!(
+            tcx.sess,
+            generics_where_clauses_span.unwrap_or(main_span),
+            E0646,
+            "`main` function is not allowed to have a `where` clause"
+        );
+        if let Some(generics_where_clauses_span) = generics_where_clauses_span {
+            diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause");
+        }
+        diag.emit();
+        error = true;
+    }
+
+    let main_asyncness = tcx.asyncness(main_def_id);
+    if let hir::IsAsync::Async = main_asyncness {
+        let mut diag = struct_span_err!(
+            tcx.sess,
+            main_span,
+            E0752,
+            "`main` function is not allowed to be `async`"
+        );
+        let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
+        if let Some(asyncness_span) = asyncness_span {
+            diag.span_label(asyncness_span, "`main` function is not allowed to be `async`");
+        }
+        diag.emit();
+        error = true;
+    }
+
+    for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
+        tcx.sess
+            .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`")
+            .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
+            .emit();
+        error = true;
+    }
+
+    if error {
+        return;
+    }
+
+    let expected_return_type;
+    if let Some(term_did) = tcx.lang_items().termination() {
+        let return_ty = main_fnsig.output();
+        let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
+        if !return_ty.bound_vars().is_empty() {
+            let msg = "`main` function return type is not allowed to have generic \
+                    parameters";
+            struct_span_err!(tcx.sess, return_ty_span, E0131, "{}", msg).emit();
+            error = true;
+        }
+        let return_ty = return_ty.skip_binder();
+        tcx.infer_ctxt().enter(|infcx| {
+            // Main should have no WC, so empty param env is OK here.
+            let param_env = ty::ParamEnv::empty();
+            let cause = traits::ObligationCause::new(
+                return_ty_span,
+                main_diagnostics_hir_id,
+                ObligationCauseCode::MainFunctionType,
+            );
+            let ocx = traits::ObligationCtxt::new(&infcx);
+            let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
+            ocx.register_bound(cause, param_env, norm_return_ty, term_did);
+            let errors = ocx.select_all_or_error();
+            if !errors.is_empty() {
+                infcx.report_fulfillment_errors(&errors, None, false);
+                error = true;
+            }
+        });
+        // now we can take the return type of the given main function
+        expected_return_type = main_fnsig.output();
+    } else {
+        // standard () main return type
+        expected_return_type = ty::Binder::dummy(tcx.mk_unit());
+    }
+
+    if error {
+        return;
+    }
+
+    let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
+        tcx.mk_fn_sig(iter::empty(), expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
+    }));
+
+    require_same_types(
+        tcx,
+        &ObligationCause::new(
+            main_span,
+            main_diagnostics_hir_id,
+            ObligationCauseCode::MainFunctionType,
+        ),
+        se_ty,
+        tcx.mk_fn_ptr(main_fnsig),
+    );
+}
+fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
+    let start_def_id = start_def_id.expect_local();
+    let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id);
+    let start_span = tcx.def_span(start_def_id);
+    let start_t = tcx.type_of(start_def_id);
+    match start_t.kind() {
+        ty::FnDef(..) => {
+            if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
+                if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
+                    let mut error = false;
+                    if !generics.params.is_empty() {
+                        struct_span_err!(
+                            tcx.sess,
+                            generics.span,
+                            E0132,
+                            "start function is not allowed to have type parameters"
+                        )
+                        .span_label(generics.span, "start function cannot have type parameters")
+                        .emit();
+                        error = true;
+                    }
+                    if generics.has_where_clause_predicates {
+                        struct_span_err!(
+                            tcx.sess,
+                            generics.where_clause_span,
+                            E0647,
+                            "start function is not allowed to have a `where` clause"
+                        )
+                        .span_label(
+                            generics.where_clause_span,
+                            "start function cannot have a `where` clause",
+                        )
+                        .emit();
+                        error = true;
+                    }
+                    if let hir::IsAsync::Async = sig.header.asyncness {
+                        let span = tcx.def_span(it.def_id);
+                        struct_span_err!(
+                            tcx.sess,
+                            span,
+                            E0752,
+                            "`start` is not allowed to be `async`"
+                        )
+                        .span_label(span, "`start` is not allowed to be `async`")
+                        .emit();
+                        error = true;
+                    }
+
+                    let attrs = tcx.hir().attrs(start_id);
+                    for attr in attrs {
+                        if attr.has_name(sym::track_caller) {
+                            tcx.sess
+                                .struct_span_err(
+                                    attr.span,
+                                    "`start` is not allowed to be `#[track_caller]`",
+                                )
+                                .span_label(
+                                    start_span,
+                                    "`start` is not allowed to be `#[track_caller]`",
+                                )
+                                .emit();
+                            error = true;
+                        }
+                    }
+
+                    if error {
+                        return;
+                    }
+                }
+            }
+
+            let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+                [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(),
+                tcx.types.isize,
+                false,
+                hir::Unsafety::Normal,
+                Abi::Rust,
+            )));
+
+            require_same_types(
+                tcx,
+                &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
+                se_ty,
+                tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
+            );
+        }
+        _ => {
+            span_bug!(start_span, "start has a non-function type: found `{}`", start_t);
+        }
+    }
+}
+
+fn check_for_entry_fn(tcx: TyCtxt<'_>) {
+    match tcx.entry_fn(()) {
+        Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
+        Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
+        _ => {}
+    }
+}
+
+pub fn provide(providers: &mut Providers) {
+    collect::provide(providers);
+    coherence::provide(providers);
+    check::provide(providers);
+    variance::provide(providers);
+    outlives::provide(providers);
+    impl_wf_check::provide(providers);
+    hir_wf_check::provide(providers);
+}
+
+pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
+    let _prof_timer = tcx.sess.timer("type_check_crate");
+
+    // this ensures that later parts of type checking can assume that items
+    // have valid types and not error
+    // FIXME(matthewjasper) We shouldn't need to use `track_errors`.
+    tcx.sess.track_errors(|| {
+        tcx.sess.time("type_collecting", || {
+            tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
+        });
+    })?;
+
+    if tcx.features().rustc_attrs {
+        tcx.sess.track_errors(|| {
+            tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
+        })?;
+    }
+
+    tcx.sess.track_errors(|| {
+        tcx.sess.time("impl_wf_inference", || {
+            tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module))
+        });
+    })?;
+
+    tcx.sess.track_errors(|| {
+        tcx.sess.time("coherence_checking", || {
+            for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
+                tcx.ensure().coherent_trait(trait_def_id);
+            }
+
+            // these queries are executed for side-effects (error reporting):
+            tcx.ensure().crate_inherent_impls(());
+            tcx.ensure().crate_inherent_impls_overlap_check(());
+        });
+    })?;
+
+    if tcx.features().rustc_attrs {
+        tcx.sess.track_errors(|| {
+            tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
+        })?;
+    }
+
+    tcx.sess.track_errors(|| {
+        tcx.sess.time("wf_checking", || {
+            tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
+        });
+    })?;
+
+    // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
+    tcx.sess.time("item_types_checking", || {
+        tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
+    });
+
+    tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(()));
+
+    check_unused::check_crate(tcx);
+    check_for_entry_fn(tcx);
+
+    if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) }
+}
+
+/// A quasi-deprecated helper used in rustdoc and clippy to get
+/// the type from a HIR node.
+pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+    // In case there are any projections, etc., find the "environment"
+    // def-ID that will be used to determine the traits/predicates in
+    // scope.  This is derived from the enclosing item-like thing.
+    let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
+    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
+    <dyn AstConv<'_>>::ast_ty_to_ty(&item_cx, hir_ty)
+}
+
+pub fn hir_trait_to_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    hir_trait: &hir::TraitRef<'_>,
+    self_ty: Ty<'tcx>,
+) -> Bounds<'tcx> {
+    // In case there are any projections, etc., find the "environment"
+    // def-ID that will be used to determine the traits/predicates in
+    // scope.  This is derived from the enclosing item-like thing.
+    let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
+    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
+    let mut bounds = Bounds::default();
+    let _ = <dyn AstConv<'_>>::instantiate_poly_trait_ref(
+        &item_cx,
+        hir_trait,
+        DUMMY_SP,
+        ty::BoundConstness::NotConst,
+        self_ty,
+        &mut bounds,
+        true,
+    );
+
+    bounds
+}