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

Don't display default generic parameters in diagnostics that compare types #52244

Merged
merged 2 commits into from
Jul 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
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
70 changes: 63 additions & 7 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa
use super::region_constraints::GenericKind;
use super::lexical_region_resolve::RegionResolutionError;

use std::fmt;
use std::{cmp, fmt};
use hir;
use hir::map as hir_map;
use hir::def_id::DefId;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::error::TypeError;
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
Expand Down Expand Up @@ -672,6 +672,43 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

/// For generic types with parameters with defaults, remove the parameters corresponding to
/// the defaults. This repeats a lot of the logic found in `PrintContext::parameterized`.
fn strip_generic_default_params(
&self,
def_id: DefId,
substs: &ty::subst::Substs<'tcx>
) -> &'tcx ty::subst::Substs<'tcx> {
let generics = self.tcx.generics_of(def_id);
let mut num_supplied_defaults = 0;
let mut type_params = generics.params.iter().rev().filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
}).peekable();
let has_default = {
let has_default = type_params.peek().map(|(_, has_default)| has_default);
*has_default.unwrap_or(&false)
};
if has_default {
let types = substs.types().rev();
for ((def_id, has_default), actual) in type_params.zip(types) {
if !has_default {
break;
}
if self.tcx.type_of(def_id).subst(self.tcx, substs) != actual {
break;
}
num_supplied_defaults += 1;
}
}
let len = generics.params.len();
let mut generics = generics.clone();
generics.params.truncate(len - num_supplied_defaults);
substs.truncate_to(self.tcx, &generics)
}

/// Compare two given types, eliding parts that are the same between them and highlighting
/// relevant differences, and return two representation of those types for highlighted printing.
fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
Expand Down Expand Up @@ -713,6 +750,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

match (&t1.sty, &t2.sty) {
(&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => {
let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1);
let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2);
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
let path1 = self.tcx.item_path_str(def1.did.clone());
let path2 = self.tcx.item_path_str(def2.did.clone());
Expand All @@ -728,8 +767,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
values.0.push_normal(path1);
values.1.push_normal(path2);

// Avoid printing out default generic parameters that are common to both
// types.
let len1 = sub_no_defaults_1.len();
let len2 = sub_no_defaults_2.len();
let common_len = cmp::min(len1, len2);
let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
let common_default_params =
remainder1.iter().rev().zip(remainder2.iter().rev())
.filter(|(a, b)| a == b).count();
let len = sub1.len() - common_default_params;

// Only draw `<...>` if there're lifetime/type arguments.
let len = sub1.len();
if len > 0 {
values.0.push_normal("<");
values.1.push_normal("<");
Expand Down Expand Up @@ -774,7 +824,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// ^ elided type as this type argument was the same in both sides
let type_arguments = sub1.types().zip(sub2.types());
let regions_len = sub1.regions().collect::<Vec<_>>().len();
for (i, (ta1, ta2)) in type_arguments.enumerate() {
for (i, (ta1, ta2)) in type_arguments.take(len).enumerate() {
let i = i + regions_len;
if ta1 == ta2 {
values.0.push_normal("_");
Expand Down Expand Up @@ -804,7 +854,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&mut values.0,
&mut values.1,
path1.clone(),
sub1,
sub_no_defaults_1,
path2.clone(),
&t2,
).is_some()
Expand All @@ -816,8 +866,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// Bar<Qux>
// Foo<Bar<Qux>>
// ------- this type argument is exactly the same as the other type
if self.cmp_type_arg(&mut values.1, &mut values.0, path2, sub2, path1, &t1)
.is_some()
if self.cmp_type_arg(
&mut values.1,
&mut values.0,
path2,
sub_no_defaults_2,
path1,
&t1,
).is_some()
{
return values;
}
Expand Down
86 changes: 86 additions & 0 deletions src/test/ui/type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

trait Qux {}
struct A;
struct B;
impl Qux for A {}
impl Qux for B {}

struct Foo<T, U: Qux = A, V: Qux = B>(T, U, V);

struct foo;
struct bar;

fn want<T>(t: T) {}

fn have_usize(f: usize) {
want::<foo>(f); //~ ERROR mismatched types
want::<bar>(f); //~ ERROR mismatched types
want::<Foo<usize>>(f); //~ ERROR mismatched types
want::<Foo<usize, B>>(f); //~ ERROR mismatched types
want::<Foo<foo>>(f); //~ ERROR mismatched types
want::<Foo<foo, B>>(f); //~ ERROR mismatched types
want::<Foo<bar>>(f); //~ ERROR mismatched types
want::<Foo<bar, B>>(f); //~ ERROR mismatched types
}

fn have_foo(f: foo) {
want::<usize>(f); //~ ERROR mismatched types
want::<bar>(f); //~ ERROR mismatched types
want::<Foo<usize>>(f); //~ ERROR mismatched types
want::<Foo<usize, B>>(f); //~ ERROR mismatched types
want::<Foo<foo>>(f); //~ ERROR mismatched types
want::<Foo<foo, B>>(f); //~ ERROR mismatched types
want::<Foo<bar>>(f); //~ ERROR mismatched types
want::<Foo<bar, B>>(f); //~ ERROR mismatched types
}

fn have_foo_foo(f: Foo<foo>) {
want::<usize>(f); //~ ERROR mismatched types
want::<foo>(f); //~ ERROR mismatched types
want::<bar>(f); //~ ERROR mismatched types
want::<Foo<usize>>(f); //~ ERROR mismatched types
want::<Foo<usize, B>>(f); //~ ERROR mismatched types
want::<Foo<foo, B>>(f); //~ ERROR mismatched types
want::<Foo<bar>>(f); //~ ERROR mismatched types
want::<Foo<bar, B>>(f); //~ ERROR mismatched types
want::<&Foo<foo>>(f); //~ ERROR mismatched types
want::<&Foo<foo, B>>(f); //~ ERROR mismatched types
}

fn have_foo_foo_b(f: Foo<foo, B>) {
want::<usize>(f); //~ ERROR mismatched types
want::<foo>(f); //~ ERROR mismatched types
want::<bar>(f); //~ ERROR mismatched types
want::<Foo<usize>>(f); //~ ERROR mismatched types
want::<Foo<usize, B>>(f); //~ ERROR mismatched types
want::<Foo<foo>>(f); //~ ERROR mismatched types
want::<Foo<bar>>(f); //~ ERROR mismatched types
want::<Foo<bar, B>>(f); //~ ERROR mismatched types
want::<&Foo<foo>>(f); //~ ERROR mismatched types
want::<&Foo<foo, B>>(f); //~ ERROR mismatched types
}

fn have_foo_foo_b_a(f: Foo<foo, B, A>) {
want::<usize>(f); //~ ERROR mismatched types
want::<foo>(f); //~ ERROR mismatched types
want::<bar>(f); //~ ERROR mismatched types
want::<Foo<usize>>(f); //~ ERROR mismatched types
want::<Foo<usize, B>>(f); //~ ERROR mismatched types
want::<Foo<foo>>(f); //~ ERROR mismatched types
want::<Foo<foo, B>>(f); //~ ERROR mismatched types
want::<Foo<bar>>(f); //~ ERROR mismatched types
want::<Foo<bar, B>>(f); //~ ERROR mismatched types
want::<&Foo<foo>>(f); //~ ERROR mismatched types
want::<&Foo<foo, B>>(f); //~ ERROR mismatched types
}

fn main() {}
Loading