Fix type parameter defaults

They should not be applied in expression or pattern contexts, unless there are
other explicitly given type args.
This commit is contained in:
Florian Diebold 2020-05-29 19:14:04 +02:00
parent 02f7b5d7ab
commit a4a4a1854e
10 changed files with 192 additions and 115 deletions

View file

@ -195,7 +195,7 @@ struct Test<K, T = u8> {
}
fn main() {
let test<|> = Test { t: 23, k: 33 };
let test<|> = Test { t: 23u8, k: 33 };
}"#,
r#"
struct Test<K, T = u8> {
@ -204,7 +204,7 @@ struct Test<K, T = u8> {
}
fn main() {
let test: Test<i32> = Test { t: 23, k: 33 };
let test: Test<i32> = Test { t: 23u8, k: 33 };
}"#,
);
}

View file

@ -439,13 +439,13 @@ impl<'a> InferenceContext<'a> {
};
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = Ty::substs_from_path(&ctx, path, strukt.into());
let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.subst(&substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
let substs = Ty::substs_from_path(&ctx, path, var.into());
let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
let ty = self.db.ty(var.parent.into());
let ty = self.insert_type_vars(ty.subst(&substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved)

View file

@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> {
// self_subst is just for the parent
let parent_substs = self_subst.unwrap_or_else(Substs::empty);
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
let substs = Ty::substs_from_path(&ctx, path, typable);
let substs = Ty::substs_from_path(&ctx, path, typable, true);
let full_substs = Substs::builder(substs.len())
.use_parent_substs(&parent_substs)
.fill(substs.0[parent_substs.len()..].iter().cloned())
@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> {
def,
resolved_segment,
remaining_segments_for_ty,
true,
);
if let Ty::Unknown = ty {
return None;

View file

@ -323,6 +323,7 @@ impl Ty {
resolution: TypeNs,
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
infer_args: bool,
) -> (Ty, Option<TypeNs>) {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
@ -400,9 +401,15 @@ impl Ty {
ctx.db.ty(adt.into()).subst(&substs)
}
TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()),
TypeNs::AdtId(it) => {
Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
}
TypeNs::BuiltinType(it) => {
Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
}
TypeNs::TypeAliasId(it) => {
Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
}
// FIXME: report error
TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
};
@ -428,7 +435,13 @@ impl Ty {
),
Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
};
Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments)
Ty::from_partly_resolved_hir_path(
ctx,
resolution,
resolved_segment,
remaining_segments,
false,
)
}
fn select_associated_type(
@ -474,13 +487,14 @@ impl Ty {
ctx: &TyLoweringContext<'_>,
segment: PathSegment<'_>,
typable: TyDefId,
infer_args: bool,
) -> Ty {
let generic_def = match typable {
TyDefId::BuiltinType(_) => None,
TyDefId::AdtId(it) => Some(it.into()),
TyDefId::TypeAliasId(it) => Some(it.into()),
};
let substs = substs_from_path_segment(ctx, segment, generic_def, false);
let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
ctx.db.ty(typable).subst(&substs)
}
@ -493,6 +507,7 @@ impl Ty {
// `ValueTyDefId` is just a convenient way to pass generics and
// special-case enum variants
resolved: ValueTyDefId,
infer_args: bool,
) -> Substs {
let last = path.segments().last().expect("path should have at least one segment");
let (segment, generic_def) = match resolved {
@ -515,22 +530,27 @@ impl Ty {
(segment, Some(var.parent.into()))
}
};
substs_from_path_segment(ctx, segment, generic_def, false)
substs_from_path_segment(ctx, segment, generic_def, infer_args)
}
}
pub(super) fn substs_from_path_segment(
fn substs_from_path_segment(
ctx: &TyLoweringContext<'_>,
segment: PathSegment<'_>,
def_generic: Option<GenericDefId>,
_add_self_param: bool,
infer_args: bool,
) -> Substs {
let mut substs = Vec::new();
let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def));
let (parent_params, self_params, type_params, impl_trait_params) =
def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
let total_len = parent_params + self_params + type_params + impl_trait_params;
substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
let mut had_explicit_args = false;
if let Some(generic_args) = &segment.args_and_bindings {
if !generic_args.has_self_type {
substs.extend(iter::repeat(Ty::Unknown).take(self_params));
@ -542,31 +562,35 @@ pub(super) fn substs_from_path_segment(
for arg in generic_args.args.iter().skip(skip).take(expected_num) {
match arg {
GenericArg::Type(type_ref) => {
had_explicit_args = true;
let ty = Ty::from_hir(ctx, type_ref);
substs.push(ty);
}
}
}
}
let total_len = parent_params + self_params + type_params + impl_trait_params;
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
if !infer_args || had_explicit_args {
if let Some(def_generic) = def_generic {
let default_substs = ctx.db.generic_defaults(def_generic);
assert_eq!(total_len, default_substs.len());
for default_ty in default_substs.iter().skip(substs.len()) {
substs.push(default_ty.clone());
}
}
}
// add placeholders for args that were not provided
// FIXME: emit diagnostics in contexts where this is not allowed
for _ in substs.len()..total_len {
substs.push(Ty::Unknown);
}
assert_eq!(substs.len(), total_len);
// handle defaults
if let Some(def_generic) = def_generic {
let default_substs = ctx.db.generic_defaults(def_generic);
assert_eq!(substs.len(), default_substs.len());
for (i, default_ty) in default_substs.iter().enumerate() {
if substs[i] == Ty::Unknown {
substs[i] = default_ty.clone();
}
}
}
Substs(substs.into())
}
@ -615,9 +639,7 @@ impl TraitRef {
segment: PathSegment<'_>,
resolved: TraitId,
) -> Substs {
let has_self_param =
segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param)
substs_from_path_segment(ctx, segment, Some(resolved.into()), false)
}
pub(crate) fn from_type_bound(

View file

@ -29,7 +29,7 @@ fn omit_default_type_parameters() {
//- /main.rs
struct Foo<T = u8> { t: T }
fn main() {
let foo = Foo { t: 5 };
let foo = Foo { t: 5u8 };
foo<|>;
}
",
@ -41,7 +41,7 @@ fn omit_default_type_parameters() {
//- /main.rs
struct Foo<K, T = u8> { k: K, t: T }
fn main() {
let foo = Foo { k: 400, t: 5 };
let foo = Foo { k: 400, t: 5u8 };
foo<|>;
}
",

View file

@ -183,60 +183,6 @@ fn test() {
);
}
#[test]
fn infer_associated_method_generics_with_default_param() {
assert_snapshot!(
infer(r#"
struct Gen<T=u32> {
val: T
}
impl<T> Gen<T> {
pub fn make() -> Gen<T> {
loop { }
}
}
fn test() {
let a = Gen::make();
}
"#),
@r###"
80..104 '{ ... }': Gen<T>
90..98 'loop { }': !
95..98 '{ }': ()
118..146 '{ ...e(); }': ()
128..129 'a': Gen<u32>
132..141 'Gen::make': fn make<u32>() -> Gen<u32>
132..143 'Gen::make()': Gen<u32>
"###
);
}
#[test]
fn infer_associated_method_generics_with_default_tuple_param() {
let t = type_at(
r#"
//- /main.rs
struct Gen<T=()> {
val: T
}
impl<T> Gen<T> {
pub fn make() -> Gen<T> {
loop { }
}
}
fn test() {
let a = Gen::make();
a.val<|>;
}
"#,
);
assert_eq!(t, "()");
}
#[test]
fn infer_associated_method_generics_without_args() {
assert_snapshot!(

View file

@ -1997,3 +1997,111 @@ fn foo() {
"###
);
}
#[test]
fn generic_default() {
assert_snapshot!(
infer(r#"
struct Thing<T = ()> { t: T }
enum OtherThing<T = ()> {
One { t: T },
Two(T),
}
fn test(t1: Thing, t2: OtherThing, t3: Thing<i32>, t4: OtherThing<i32>) {
t1.t;
t3.t;
match t2 {
OtherThing::One { t } => { t; },
OtherThing::Two(t) => { t; },
}
match t4 {
OtherThing::One { t } => { t; },
OtherThing::Two(t) => { t; },
}
}
"#),
@r###"
98..100 't1': Thing<()>
109..111 't2': OtherThing<()>
125..127 't3': Thing<i32>
141..143 't4': OtherThing<i32>
162..385 '{ ... } }': ()
168..170 't1': Thing<()>
168..172 't1.t': ()
178..180 't3': Thing<i32>
178..182 't3.t': i32
188..283 'match ... }': ()
194..196 't2': OtherThing<()>
207..228 'OtherT... { t }': OtherThing<()>
225..226 't': ()
232..238 '{ t; }': ()
234..235 't': ()
248..266 'OtherT...Two(t)': OtherThing<()>
264..265 't': ()
270..276 '{ t; }': ()
272..273 't': ()
288..383 'match ... }': ()
294..296 't4': OtherThing<i32>
307..328 'OtherT... { t }': OtherThing<i32>
325..326 't': i32
332..338 '{ t; }': ()
334..335 't': i32
348..366 'OtherT...Two(t)': OtherThing<i32>
364..365 't': i32
370..376 '{ t; }': ()
372..373 't': i32
"###
);
}
#[test]
fn generic_default_in_struct_literal() {
assert_snapshot!(
infer(r#"
struct Thing<T = ()> { t: T }
enum OtherThing<T = ()> {
One { t: T },
Two(T),
}
fn test() {
let x = Thing { t: loop {} };
let y = Thing { t: () };
let z = Thing { t: 1i32 };
if let Thing { t } = z {
t;
}
let a = OtherThing::One { t: 1i32 };
let b = OtherThing::Two(1i32);
}
"#),
@r###"
100..320 '{ ...32); }': ()
110..111 'x': Thing<!>
114..134 'Thing ...p {} }': Thing<!>
125..132 'loop {}': !
130..132 '{}': ()
144..145 'y': Thing<()>
148..163 'Thing { t: () }': Thing<()>
159..161 '()': ()
173..174 'z': Thing<i32>
177..194 'Thing ...1i32 }': Thing<i32>
188..192 '1i32': i32
200..241 'if let... }': ()
207..218 'Thing { t }': Thing<i32>
215..216 't': i32
221..222 'z': Thing<i32>
223..241 '{ ... }': ()
233..234 't': i32
251..252 'a': OtherThing<i32>
255..282 'OtherT...1i32 }': OtherThing<i32>
276..280 '1i32': i32
292..293 'b': OtherThing<i32>
296..311 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
296..317 'OtherT...(1i32)': OtherThing<i32>
312..316 '1i32': i32
"###
);
}

View file

@ -1806,33 +1806,33 @@ fn test() {
}
"#),
@r###"
65..69 'self': &Self
166..170 'self': Self
172..176 'args': Args
240..244 'self': &Foo
255..257 '{}': ()
335..336 'f': F
355..357 '{}': ()
444..690 '{ ...o(); }': ()
454..459 'lazy1': Lazy<Foo, fn() -> T>
476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T>
476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T>
486..492 '|| Foo': || -> T
489..492 'Foo': Foo
503..505 'r1': {unknown}
508..513 'lazy1': Lazy<Foo, fn() -> T>
508..519 'lazy1.foo()': {unknown}
561..576 'make_foo_fn_ptr': fn() -> Foo
592..603 'make_foo_fn': fn make_foo_fn() -> Foo
613..618 'lazy2': Lazy<Foo, fn() -> T>
635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T>
635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T>
645..660 'make_foo_fn_ptr': fn() -> Foo
671..673 'r2': {unknown}
676..681 'lazy2': Lazy<Foo, fn() -> T>
676..687 'lazy2.foo()': {unknown}
550..552 '{}': ()
"###
65..69 'self': &Self
166..170 'self': Self
172..176 'args': Args
240..244 'self': &Foo
255..257 '{}': ()
335..336 'f': F
355..357 '{}': ()
444..690 '{ ...o(); }': ()
454..459 'lazy1': Lazy<Foo, || -> Foo>
476..485 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
476..493 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
486..492 '|| Foo': || -> Foo
489..492 'Foo': Foo
503..505 'r1': usize
508..513 'lazy1': Lazy<Foo, || -> Foo>
508..519 'lazy1.foo()': usize
561..576 'make_foo_fn_ptr': fn() -> Foo
592..603 'make_foo_fn': fn make_foo_fn() -> Foo
613..618 'lazy2': Lazy<Foo, fn() -> Foo>
635..644 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
645..660 'make_foo_fn_ptr': fn() -> Foo
671..673 'r2': {unknown}
676..681 'lazy2': Lazy<Foo, fn() -> Foo>
676..687 'lazy2.foo()': {unknown}
550..552 '{}': ()
"###
);
}

View file

@ -529,7 +529,7 @@ struct Test<K, T = u8> {
}
fn main() {
let zz<|> = Test { t: 23, k: 33 };
let zz<|> = Test { t: 23u8, k: 33 };
}"#,
&["Test<i32, u8>"],
);

View file

@ -415,7 +415,7 @@ struct Test<K, T = u8> {
}
fn main() {
let zz = Test { t: 23, k: 33 };
let zz = Test { t: 23u8, k: 33 };
let zz_ref = &zz;
}"#,
);
@ -428,7 +428,7 @@ fn main() {
label: "Test<i32>",
},
InlayHint {
range: 105..111,
range: 107..113,
kind: TypeHint,
label: "&Test<i32>",
},