Skip to content

Commit

Permalink
Auto merge of #76906 - Nicholas-Baron:shorten_typeck_check, r=oli-obk
Browse files Browse the repository at this point in the history
Split rustc_typeck::check into separate files

Contributing to #60302.
  • Loading branch information
bors committed Sep 22, 2020
2 parents 44ae0b8 + ccd218d commit c113030
Show file tree
Hide file tree
Showing 7 changed files with 5,124 additions and 5,006 deletions.
1,344 changes: 1,344 additions & 0 deletions compiler/rustc_typeck/src/check/check.rs

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions compiler/rustc_typeck/src/check/diverges.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use rustc_span::source_map::DUMMY_SP;
use rustc_span::{self, Span};
use std::{cmp, ops};

/// Tracks whether executing a node may exit normally (versus
/// return/break/panic, which "diverge", leaving dead code in their
/// wake). Tracked semi-automatically (through type variables marked
/// as diverging), with some manual adjustments for control-flow
/// primitives (approximating a CFG).
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Diverges {
/// Potentially unknown, some cases converge,
/// others require a CFG to determine them.
Maybe,

/// Definitely known to diverge and therefore
/// not reach the next sibling or its parent.
Always {
/// The `Span` points to the expression
/// that caused us to diverge
/// (e.g. `return`, `break`, etc).
span: Span,
/// In some cases (e.g. a `match` expression
/// where all arms diverge), we may be
/// able to provide a more informative
/// message to the user.
/// If this is `None`, a default message
/// will be generated, which is suitable
/// for most cases.
custom_note: Option<&'static str>,
},

/// Same as `Always` but with a reachability
/// warning already emitted.
WarnedAlways,
}

// Convenience impls for combining `Diverges`.

impl ops::BitAnd for Diverges {
type Output = Self;
fn bitand(self, other: Self) -> Self {
cmp::min(self, other)
}
}

impl ops::BitOr for Diverges {
type Output = Self;
fn bitor(self, other: Self) -> Self {
cmp::max(self, other)
}
}

impl ops::BitAndAssign for Diverges {
fn bitand_assign(&mut self, other: Self) {
*self = *self & other;
}
}

impl ops::BitOrAssign for Diverges {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}

impl Diverges {
/// Creates a `Diverges::Always` with the provided `span` and the default note message.
pub(super) fn always(span: Span) -> Diverges {
Diverges::Always { span, custom_note: None }
}

pub(super) fn is_always(self) -> bool {
// Enum comparison ignores the
// contents of fields, so we just
// fill them in with garbage here.
self >= Diverges::Always { span: DUMMY_SP, custom_note: None }
}
}
117 changes: 117 additions & 0 deletions compiler/rustc_typeck/src/check/expectation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::{self, Ty};
use rustc_span::{self, Span};

use super::Expectation::*;
use super::FnCtxt;

/// When type-checking an expression, we propagate downward
/// whatever type hint we are able in the form of an `Expectation`.
#[derive(Copy, Clone, Debug)]
pub enum Expectation<'tcx> {
/// We know nothing about what type this expression should have.
NoExpectation,

/// This expression should have the type given (or some subtype).
ExpectHasType(Ty<'tcx>),

/// This expression will be cast to the `Ty`.
ExpectCastableToType(Ty<'tcx>),

/// This rvalue expression will be wrapped in `&` or `Box` and coerced
/// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
ExpectRvalueLikeUnsized(Ty<'tcx>),
}

impl<'a, 'tcx> Expectation<'tcx> {
// Disregard "castable to" expectations because they
// can lead us astray. Consider for example `if cond
// {22} else {c} as u8` -- if we propagate the
// "castable to u8" constraint to 22, it will pick the
// type 22u8, which is overly constrained (c might not
// be a u8). In effect, the problem is that the
// "castable to" expectation is not the tightest thing
// we can say, so we want to drop it in this case.
// The tightest thing we can say is "must unify with
// else branch". Note that in the case of a "has type"
// constraint, this limitation does not hold.

// If the expected type is just a type variable, then don't use
// an expected type. Otherwise, we might write parts of the type
// when checking the 'then' block which are incompatible with the
// 'else' branch.
pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
match *self {
ExpectHasType(ety) => {
let ety = fcx.shallow_resolve(ety);
if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation }
}
ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety),
_ => NoExpectation,
}
}

/// Provides an expectation for an rvalue expression given an *optional*
/// hint, which is not required for type safety (the resulting type might
/// be checked higher up, as is the case with `&expr` and `box expr`), but
/// is useful in determining the concrete type.
///
/// The primary use case is where the expected type is a fat pointer,
/// like `&[isize]`. For example, consider the following statement:
///
/// let x: &[isize] = &[1, 2, 3];
///
/// In this case, the expected type for the `&[1, 2, 3]` expression is
/// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
/// expectation `ExpectHasType([isize])`, that would be too strong --
/// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
/// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
/// to the type `&[isize]`. Therefore, we propagate this more limited hint,
/// which still is useful, because it informs integer literals and the like.
/// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
match fcx.tcx.struct_tail_without_normalization(ty).kind() {
ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
_ => ExpectHasType(ty),
}
}

// Resolves `expected` by a single level if it is a variable. If
// there is no expected type or resolution is not possible (e.g.,
// no constraints yet present), just returns `None`.
fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
match self {
NoExpectation => NoExpectation,
ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)),
ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)),
ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)),
}
}

pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
match self.resolve(fcx) {
NoExpectation => None,
ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty),
}
}

/// It sometimes happens that we want to turn an expectation into
/// a **hard constraint** (i.e., something that must be satisfied
/// for the program to type-check). `only_has_type` will return
/// such a constraint, if it exists.
pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
match self.resolve(fcx) {
ExpectHasType(ty) => Some(ty),
NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
}
}

/// Like `only_has_type`, but instead of returning `None` if no
/// hard constraint exists, creates a fresh type variable.
pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
self.only_has_type(fcx).unwrap_or_else(|| {
fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span })
})
}
}
Loading

0 comments on commit c113030

Please sign in to comment.