Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] rustc_typeck: check well-formedness of type aliases. #54033

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 85 additions & 83 deletions src/librustc/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use hir::{self, Local, Pat, Body, HirId};
use hir::intravisit::{self, Visitor, NestedVisitorMap};
use hir::{self, HirId};
use infer::InferCtxt;
use infer::type_variable::TypeVariableOrigin;
use traits;
use ty::{self, Ty, Infer, TyVar};
use syntax::source_map::CompilerDesugaringKind;
use syntax_pos::Span;
use errors::DiagnosticBuilder;

struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
target_ty: &'a Ty<'tcx>,
hir_map: &'a hir::map::Map<'gcx>,
found_local_pattern: Option<&'gcx Pat>,
found_arg_pattern: Option<&'gcx Pat>,
}

impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
fn node_matches_type(&mut self, node_id: HirId) -> bool {
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
tables.borrow().node_id_to_type_opt(node_id)
});
match ty_opt {
Some(ty) => {
let ty = self.infcx.resolve_type_vars_if_possible(&ty);
ty.walk().any(|inner_ty| {
inner_ty == *self.target_ty || match (&inner_ty.sty, &self.target_ty.sty) {
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
self.infcx
.type_variables
.borrow_mut()
.sub_unified(a_vid, b_vid)
}
_ => false,
}
})
}
None => false,
}
}
}

impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
NestedVisitorMap::OnlyBodies(&self.hir_map)
}

fn visit_local(&mut self, local: &'gcx Local) {
if self.found_local_pattern.is_none() && self.node_matches_type(local.hir_id) {
self.found_local_pattern = Some(&*local.pat);
}
intravisit::walk_local(self, local);
}

fn visit_body(&mut self, body: &'gcx Body) {
for argument in &body.arguments {
if self.found_arg_pattern.is_none() && self.node_matches_type(argument.hir_id) {
self.found_arg_pattern = Some(&*argument.pat);
}
}
intravisit::walk_body(self, body);
}
}


impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn extract_type_name(&self, ty: &'a Ty<'tcx>) -> String {
if let ty::Infer(ty::TyVar(ty_vid)) = (*ty).sty {
Expand All @@ -89,38 +32,85 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}

pub fn need_type_info_err(&self,
body_id: Option<hir::BodyId>,
span: Span,
ty: Ty<'tcx>)
-> DiagnosticBuilder<'gcx> {
cause: &traits::ObligationCause<'tcx>,
ty: Ty<'tcx>)
-> DiagnosticBuilder<'gcx> {
let ty = self.resolve_type_vars_if_possible(&ty);
let name = self.extract_type_name(&ty);

let mut err_span = span;
let mut labels = vec![(
span,
cause.span,
if &name == "_" {
"cannot infer type".to_string()
} else {
format!("cannot infer type for `{}`", name)
},
)];
let mut span = cause.span;

let mut local_visitor = FindLocalByTypeVisitor {
infcx: &self,
target_ty: &ty,
hir_map: &self.tcx.hir,
found_local_pattern: None,
found_arg_pattern: None,
};

if let Some(body_id) = body_id {
let expr = self.tcx.hir.expect_expr(body_id.node_id);
local_visitor.visit_expr(expr);
// NB. Lower values are more preferred.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum LocalKind {
ClosureArg,
Let,
}

if let Some(pattern) = local_visitor.found_arg_pattern {
err_span = pattern.span;
let found_local = self.in_progress_tables.and_then(|tables| {
let tables = tables.borrow();
let local_id_root = tables.local_id_root?;
assert!(local_id_root.is_local());

tables.node_types().iter().filter_map(|(&local_id, &node_ty)| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

such ECS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It'd be nice to pull this out into some sort of helper function, I think? I'd also like to see a comment, something like:


Iterate over the all the things that we've assigned types to. Check whether they represent local variables and, if so, if the type of that local variable references the type we are looking for.

let node_id = self.tcx.hir.hir_to_node_id(HirId {
owner: local_id_root.index,
local_id,
});

let (kind, pattern) = match self.tcx.hir.find(node_id) {
Some(hir::Node::Local(local)) => {
(LocalKind::Let, &*local.pat)
}

Some(hir::Node::Binding(pat)) |
Some(hir::Node::Pat(pat)) => {
let parent_id = self.tcx.hir.get_parent_node(node_id);
match self.tcx.hir.find(parent_id) {
Some(hir::Node::Expr(e)) => {
match e.node {
hir::ExprKind::Closure(..) => {}
_ => return None,
}
}
_ => return None,
}

(LocalKind::ClosureArg, pat)
}

_ => return None
};

let node_ty = self.resolve_type_vars_if_possible(&node_ty);
let matches_type = node_ty.walk().any(|inner_ty| {
inner_ty == ty || match (&inner_ty.sty, &ty.sty) {
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
self.type_variables
.borrow_mut()
.sub_unified(a_vid, b_vid)
}
_ => false,
}
});
if !matches_type {
return None;
}

Some((kind, pattern))
}).min_by_key(|&(kind, pattern)| (kind, pattern.hir_id.local_id))
});

if let Some((LocalKind::ClosureArg, pattern)) = found_local {
span = pattern.span;
// We don't want to show the default label for closures.
//
// So, before clearing, the output would look something like this:
Expand All @@ -139,7 +129,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
labels.clear();
labels.push(
(pattern.span, "consider giving this closure parameter a type".to_string()));
} else if let Some(pattern) = local_visitor.found_local_pattern {
} else if let Some((LocalKind::Let, pattern)) = found_local {
if let Some(simple_ident) = pattern.simple_ident() {
match pattern.span.compiler_desugaring_kind() {
None => labels.push((pattern.span,
Expand All @@ -155,10 +145,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

let mut err = struct_span_err!(self.tcx.sess,
err_span,
E0282,
"type annotations needed");
let lint = self.get_lint_from_cause_code(&cause.code);
macro_rules! struct_span_err_or_lint {
($code:ident, $($message:tt)*) => {
match lint {
Some((lint, id)) => {
let message = format!($($message)*);
self.tcx.struct_span_lint_node(lint, id, span, &message)
}
None => {
struct_span_err!(self.tcx.sess, span, $code, $($message)*)
}
}
}
}

let mut err = struct_span_err_or_lint!(E0282, "type annotations needed");

for (target_span, label_message) in labels {
err.span_label(target_span, label_message);
Expand Down
4 changes: 1 addition & 3 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1424,8 +1424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
/// new obligations that must further be processed.
pub fn partially_normalize_associated_types_in<T>(
&self,
span: Span,
body_id: ast::NodeId,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> InferOk<'tcx, T>
Expand All @@ -1434,7 +1433,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
{
debug!("partially_normalize_associated_types_in(value={:?})", value);
let mut selcx = traits::SelectionContext::new(self);
let cause = ObligationCause::misc(span, body_id);
let traits::Normalized { value, obligations } =
traits::normalize(&mut selcx, param_env, cause, value);
debug!(
Expand Down
10 changes: 3 additions & 7 deletions src/librustc/infer/outlives/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@

use infer::{GenericKind, InferCtxt};
use infer::outlives::free_region_map::FreeRegionMap;
use traits::query::outlives_bounds::{self, OutlivesBound};
use traits::{self, query::outlives_bounds::{self, OutlivesBound}};
use ty::{self, Ty};

use syntax::ast;
use syntax_pos::Span;

/// The `OutlivesEnvironment` collects information about what outlives
/// what in a given type-checking setting. For example, if we have a
/// where-clause like `where T: 'a` in scope, then the
Expand Down Expand Up @@ -136,15 +133,14 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
fn_sig_tys: &[Ty<'tcx>],
body_id: ast::NodeId,
span: Span,
cause: &traits::ObligationCause<'tcx>,
) {
debug!("add_implied_bounds()");

for &ty in fn_sig_tys {
let ty = infcx.resolve_type_vars_if_possible(&ty);
debug!("add_implied_bounds: ty = {}", ty);
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, cause, ty);
self.add_outlives_bounds(Some(infcx), implied_bounds)
}
}
Expand Down
18 changes: 14 additions & 4 deletions src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
self.struct_generic(tcx, message, None)
}

pub fn struct_lint(&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str,
lint_root: ast::NodeId,
lint: Option<&'static ::lint::Lint>)
-> Option<DiagnosticBuilder<'tcx>>
{
self.struct_generic(tcx, message, Some((lint_root, lint)))
}

pub fn report_as_error(&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str
Expand All @@ -73,7 +83,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
let lint = self.struct_generic(
tcx,
message,
Some(lint_root),
Some((lint_root, None)),
);
if let Some(mut lint) = lint {
lint.emit();
Expand All @@ -84,7 +94,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str,
lint_root: Option<ast::NodeId>,
lint_root: Option<(ast::NodeId, Option<&'static ::lint::Lint>)>,
) -> Option<DiagnosticBuilder<'tcx>> {
match self.error.kind {
::mir::interpret::EvalErrorKind::TypeckError |
Expand All @@ -97,15 +107,15 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
_ => {},
}
trace!("reporting const eval failure at {:?}", self.span);
let mut err = if let Some(lint_root) = lint_root {
let mut err = if let Some((lint_root, lint)) = lint_root {
let node_id = self.stacktrace
.iter()
.rev()
.filter_map(|frame| frame.lint_root)
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_node(
::rustc::lint::builtin::CONST_ERR,
lint.unwrap_or(::rustc::lint::builtin::CONST_ERR),
node_id,
tcx.span,
message,
Expand Down
Loading