⬆️ rust-analyzer

This commit is contained in:
Laurențiu Nicola 2023-03-20 08:31:01 +02:00
parent 544b4cfe4d
commit dbf04a5ee2
106 changed files with 2219 additions and 609 deletions

View file

@ -12,7 +12,7 @@ use hir_def::{
use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
};
@ -378,6 +378,19 @@ impl ProjectionTyExt for ProjectionTy {
}
}
pub trait DynTyExt {
fn principal(&self) -> Option<&TraitRef>;
}
impl DynTyExt for DynTy {
fn principal(&self) -> Option<&TraitRef> {
self.bounds.skip_binders().interned().get(0).and_then(|b| match b.skip_binders() {
crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
_ => None,
})
}
}
pub trait TraitRefExt {
fn hir_trait_id(&self) -> TraitId;
}

View file

@ -11,3 +11,9 @@ pub use crate::diagnostics::{
},
unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr},
};
#[derive(Debug, PartialEq, Eq)]
pub struct IncoherentImpl {
pub file_id: hir_expand::HirFileId,
pub impl_: syntax::AstPtr<syntax::ast::Impl>,
}

View file

@ -275,7 +275,23 @@ impl<'a> InferenceContext<'a> {
Some(type_ref) => self.make_ty(type_ref),
None => self.table.new_type_var(),
};
sig_tys.push(ret_ty.clone());
if let ClosureKind::Async = closure_kind {
// Use the first type parameter as the output type of future.
// existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
let impl_trait_id =
crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
sig_tys.push(
TyKind::OpaqueType(
opaque_ty_id,
Substitution::from1(Interner, ret_ty.clone()),
)
.intern(Interner),
);
} else {
sig_tys.push(ret_ty.clone());
}
let sig_ty = TyKind::Function(FnPointer {
num_binders: 0,
sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
@ -286,33 +302,38 @@ impl<'a> InferenceContext<'a> {
})
.intern(Interner);
let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
// When `sig_tys.len() == 1` the first type is the return type, not the
// first parameter type.
Some(ty) if sig_tys.len() > 1 => ty.clone(),
_ => self.result.standard_types.unit.clone(),
};
let yield_ty = self.table.new_type_var();
let (ty, resume_yield_tys) = match closure_kind {
ClosureKind::Generator(_) => {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
// When `sig_tys.len() == 1` the first type is the return type, not the
// first parameter type.
Some(ty) if sig_tys.len() > 1 => ty.clone(),
_ => self.result.standard_types.unit.clone(),
};
let yield_ty = self.table.new_type_var();
let subst = TyBuilder::subst_for_generator(self.db, self.owner)
.push(resume_ty.clone())
.push(yield_ty.clone())
.push(ret_ty.clone())
.build();
let subst = TyBuilder::subst_for_generator(self.db, self.owner)
.push(resume_ty.clone())
.push(yield_ty.clone())
.push(ret_ty.clone())
.build();
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
(generator_ty, Some((resume_ty, yield_ty)))
} else {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty =
TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
.intern(Interner);
(generator_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure(
closure_id,
Substitution::from1(Interner, sig_ty.clone()),
)
.intern(Interner);
(closure_ty, None)
(closure_ty, None)
}
};
// Eagerly try to relate the closure type with the expected
@ -321,7 +342,7 @@ impl<'a> InferenceContext<'a> {
self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) {
self.infer_top_pat(*arg_pat, &arg_ty);
}

View file

@ -5,10 +5,7 @@ use std::iter::repeat_with;
use chalk_ir::Mutability;
use hir_def::{
body::Body,
expr::{
Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
RecordFieldPat,
},
expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
path::Path,
};
use hir_expand::name::Name;
@ -439,38 +436,8 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
let mut res = false;
walk_pats(body, pat_id, &mut |pat| {
body.walk_pats(pat_id, &mut |pat| {
res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
});
res
}
fn walk_pats(body: &Body, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
let pat = &body[pat_id];
f(pat);
match pat {
Pat::Range { .. }
| Pat::Lit(..)
| Pat::Path(..)
| Pat::ConstBlock(..)
| Pat::Wild
| Pat::Missing => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
walk_pats(body, subpat, f);
}
}
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
args.iter().copied().for_each(|p| walk_pats(body, p, f));
}
Pat::Ref { pat, .. } => walk_pats(body, *pat, f),
Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
total_iter.copied().for_each(|p| walk_pats(body, p, f));
}
Pat::Record { args, .. } => {
args.iter().for_each(|RecordFieldPat { pat, .. }| walk_pats(body, *pat, f));
}
Pat::Box { inner } => walk_pats(body, *inner, f),
}
}

View file

@ -19,13 +19,13 @@ use stdx::never;
use crate::{
autoderef::{self, AutoderefKind},
db::HirDatabase,
from_foreign_def_id,
from_chalk_trait_id, from_foreign_def_id,
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment,
Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
};
/// This is used as a key for indexing impls.
@ -266,11 +266,12 @@ impl TraitImpls {
#[derive(Debug, Eq, PartialEq)]
pub struct InherentImpls {
map: FxHashMap<TyFingerprint, Vec<ImplId>>,
invalid_impls: Vec<ImplId>,
}
impl InherentImpls {
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
let mut impls = Self { map: FxHashMap::default() };
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
let crate_def_map = db.crate_def_map(krate);
impls.collect_def_map(db, &crate_def_map);
@ -283,7 +284,7 @@ impl InherentImpls {
db: &dyn HirDatabase,
block: BlockId,
) -> Option<Arc<Self>> {
let mut impls = Self { map: FxHashMap::default() };
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
if let Some(block_def_map) = db.block_def_map(block) {
impls.collect_def_map(db, &block_def_map);
impls.shrink_to_fit();
@ -306,11 +307,17 @@ impl InherentImpls {
}
let self_ty = db.impl_self_ty(impl_id);
let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
if let Some(fp) = fp {
self.map.entry(fp).or_default().push(impl_id);
let self_ty = self_ty.skip_binders();
match is_inherent_impl_coherent(db, def_map, &data, self_ty) {
true => {
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) {
self.map.entry(fp).or_default().push(impl_id);
}
}
false => self.invalid_impls.push(impl_id),
}
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
}
// To better support custom derives, collect impls in all unnamed const items.
@ -334,6 +341,10 @@ impl InherentImpls {
pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
self.map.values().flat_map(|v| v.iter().copied())
}
pub fn invalid_impls(&self) -> &[ImplId] {
&self.invalid_impls
}
}
pub(crate) fn incoherent_inherent_impl_crates(
@ -775,6 +786,69 @@ fn find_matching_impl(
}
}
fn is_inherent_impl_coherent(
db: &dyn HirDatabase,
def_map: &DefMap,
impl_data: &ImplData,
self_ty: &Ty,
) -> bool {
let self_ty = self_ty.kind(Interner);
let impl_allowed = match self_ty {
TyKind::Tuple(_, _)
| TyKind::FnDef(_, _)
| TyKind::Array(_, _)
| TyKind::Never
| TyKind::Raw(_, _)
| TyKind::Ref(_, _, _)
| TyKind::Slice(_)
| TyKind::Str
| TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(),
&TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(),
TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate() == def_map.krate()
}),
_ => true,
};
impl_allowed || {
let rustc_has_incoherent_inherent_impls = match self_ty {
TyKind::Tuple(_, _)
| TyKind::FnDef(_, _)
| TyKind::Array(_, _)
| TyKind::Never
| TyKind::Raw(_, _)
| TyKind::Ref(_, _, _)
| TyKind::Slice(_)
| TyKind::Str
| TyKind::Scalar(_) => true,
&TyKind::Adt(AdtId(adt), _) => match adt {
hir_def::AdtId::StructId(it) => {
db.struct_data(it).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::UnionId(it) => {
db.union_data(it).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
},
TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
db.trait_data(from_chalk_trait_id(trait_ref.trait_id))
.rustc_has_incoherent_inherent_impls
}),
_ => false,
};
rustc_has_incoherent_inherent_impls
&& !impl_data.items.is_empty()
&& impl_data.items.iter().copied().all(|assoc| match assoc {
AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
})
}
}
pub fn iterate_path_candidates(
ty: &Canonical<Ty>,
db: &dyn HirDatabase,

View file

@ -1113,7 +1113,7 @@ impl MirLowerCtx<'_> {
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
binding_mode = mode;
}
self.push_storage_live(*id, current)?;
self.push_storage_live(*id, current);
self.push_assignment(
current,
target_place.into(),
@ -1327,8 +1327,9 @@ impl MirLowerCtx<'_> {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
}
/// This function push `StorageLive` statements for each binding in the pattern.
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
/// the appropriated places.
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
@ -1356,7 +1357,6 @@ impl MirLowerCtx<'_> {
let l = self.result.binding_locals[b];
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
Ok(())
}
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@ -1381,10 +1381,10 @@ impl MirLowerCtx<'_> {
if let Some(expr_id) = initializer {
let else_block;
let Some((init_place, c)) =
self.lower_expr_as_place(current, *expr_id, true)?
else {
return Ok(None);
};
self.lower_expr_as_place(current, *expr_id, true)?
else {
return Ok(None);
};
current = c;
(current, else_block) = self.pattern_match(
current,
@ -1407,6 +1407,10 @@ impl MirLowerCtx<'_> {
}
}
}
} else {
self.body.walk_bindings_in_pat(*pat, |b| {
self.push_storage_live(b, current);
});
}
}
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {

View file

@ -9,7 +9,7 @@ use base_db::{
salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
};
use hir_def::{db::DefDatabase, ModuleId};
use hir_expand::db::AstDatabase;
use hir_expand::db::ExpandDatabase;
use stdx::hash::{NoHashHashMap, NoHashHashSet};
use syntax::TextRange;
use test_utils::extract_annotations;
@ -17,7 +17,7 @@ use test_utils::extract_annotations;
#[salsa::database(
base_db::SourceDatabaseExtStorage,
base_db::SourceDatabaseStorage,
hir_expand::db::AstDatabaseStorage,
hir_expand::db::ExpandDatabaseStorage,
hir_def::db::InternDatabaseStorage,
hir_def::db::DefDatabaseStorage,
crate::db::HirDatabaseStorage
@ -41,8 +41,8 @@ impl fmt::Debug for TestDB {
}
}
impl Upcast<dyn AstDatabase> for TestDB {
fn upcast(&self) -> &(dyn AstDatabase + 'static) {
impl Upcast<dyn ExpandDatabase> for TestDB {
fn upcast(&self) -> &(dyn ExpandDatabase + 'static) {
&*self
}
}

View file

@ -23,7 +23,7 @@ use hir_def::{
src::HasSource,
AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId,
};
use hir_expand::{db::AstDatabase, InFile};
use hir_expand::{db::ExpandDatabase, InFile};
use once_cell::race::OnceBool;
use stdx::format_to;
use syntax::{

View file

@ -9,6 +9,7 @@ fn infer_slice_method() {
check_types(
r#"
impl<T> [T] {
#[rustc_allow_incoherent_impl]
fn foo(&self) -> T {
loop {}
}
@ -35,6 +36,7 @@ fn test() {
//- /lib.rs crate:other_crate
mod foo {
impl f32 {
#[rustc_allow_incoherent_impl]
pub fn foo(self) -> f32 { 0. }
}
}
@ -47,6 +49,7 @@ fn infer_array_inherent_impl() {
check_types(
r#"
impl<T, const N: usize> [T; N] {
#[rustc_allow_incoherent_impl]
fn foo(&self) -> T {
loop {}
}
@ -1437,6 +1440,7 @@ fn resolve_const_generic_array_methods() {
r#"
#[lang = "array"]
impl<T, const N: usize> [T; N] {
#[rustc_allow_incoherent_impl]
pub fn map<F, U>(self, f: F) -> [U; N]
where
F: FnMut(T) -> U,
@ -1445,6 +1449,7 @@ impl<T, const N: usize> [T; N] {
#[lang = "slice"]
impl<T> [T] {
#[rustc_allow_incoherent_impl]
pub fn map<F, U>(self, f: F) -> &[U]
where
F: FnMut(T) -> U,
@ -1468,6 +1473,7 @@ struct Const<const N: usize>;
#[lang = "array"]
impl<T, const N: usize> [T; N] {
#[rustc_allow_incoherent_impl]
pub fn my_map<F, U, const X: usize>(self, f: F, c: Const<X>) -> [U; X]
where
F: FnMut(T) -> U,
@ -1476,6 +1482,7 @@ impl<T, const N: usize> [T; N] {
#[lang = "slice"]
impl<T> [T] {
#[rustc_allow_incoherent_impl]
pub fn my_map<F, const X: usize, U>(self, f: F, c: Const<X>) -> &[U]
where
F: FnMut(T) -> U,
@ -1874,14 +1881,14 @@ fn incoherent_impls() {
pub struct Box<T>(T);
use core::error::Error;
#[rustc_allow_incoherent_impl]
impl dyn Error {
#[rustc_allow_incoherent_impl]
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
loop {}
}
}
#[rustc_allow_incoherent_impl]
impl dyn Error + Send {
#[rustc_allow_incoherent_impl]
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;

View file

@ -1756,3 +1756,35 @@ const C: usize = 2 + 2;
"#,
);
}
#[test]
fn regression_14164() {
check_types(
r#"
trait Rec {
type K;
type Rebind<Tok>: Rec<K = Tok>;
}
trait Expr<K> {
type Part: Rec<K = K>;
fn foo(_: <Self::Part as Rec>::Rebind<i32>) {}
}
struct Head<K>(K);
impl<K> Rec for Head<K> {
type K = K;
type Rebind<Tok> = Head<Tok>;
}
fn test<E>()
where
E: Expr<usize, Part = Head<usize>>,
{
let head;
//^^^^ Head<i32>
E::foo(head);
}
"#,
);
}

View file

@ -1116,21 +1116,22 @@ fn infer_inherent_method() {
fn infer_inherent_method_str() {
check_infer(
r#"
#[lang = "str"]
impl str {
fn foo(&self) -> i32 {}
}
#![rustc_coherence_is_core]
#[lang = "str"]
impl str {
fn foo(&self) -> i32 {}
}
fn test() {
"foo".foo();
}
"#,
fn test() {
"foo".foo();
}
"#,
expect![[r#"
39..43 'self': &str
52..54 '{}': i32
68..88 '{ ...o(); }': ()
74..79 '"foo"': &str
74..85 '"foo".foo()': i32
67..71 'self': &str
80..82 '{}': i32
96..116 '{ ...o(); }': ()
102..107 '"foo"': &str
102..113 '"foo".foo()': i32
"#]],
);
}
@ -2640,6 +2641,7 @@ impl<T> [T] {}
#[lang = "slice_alloc"]
impl<T> [T] {
#[rustc_allow_incoherent_impl]
pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {
unimplemented!()
}
@ -2655,22 +2657,22 @@ struct Astruct;
impl B for Astruct {}
"#,
expect![[r#"
569..573 'self': Box<[T], A>
602..634 '{ ... }': Vec<T, A>
648..761 '{ ...t]); }': ()
658..661 'vec': Vec<i32, Global>
664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
664..691 '<[_]>:...1i32])': Vec<i32, Global>
680..690 'box [1i32]': Box<[i32; 1], Global>
684..690 '[1i32]': [i32; 1]
685..689 '1i32': i32
701..702 'v': Vec<Box<dyn B, Global>, Global>
722..739 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
722..758 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
740..757 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
744..757 '[box Astruct]': [Box<dyn B, Global>; 1]
745..756 'box Astruct': Box<Astruct, Global>
749..756 'Astruct': Astruct
604..608 'self': Box<[T], A>
637..669 '{ ... }': Vec<T, A>
683..796 '{ ...t]); }': ()
693..696 'vec': Vec<i32, Global>
699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
699..726 '<[_]>:...1i32])': Vec<i32, Global>
715..725 'box [1i32]': Box<[i32; 1], Global>
719..725 '[1i32]': [i32; 1]
720..724 '1i32': i32
736..737 'v': Vec<Box<dyn B, Global>, Global>
757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
779..792 '[box Astruct]': [Box<dyn B, Global>; 1]
780..791 'box Astruct': Box<Astruct, Global>
784..791 'Astruct': Astruct
"#]],
)
}

View file

@ -82,6 +82,46 @@ async fn test() {
);
}
#[test]
fn infer_async_closure() {
check_types(
r#"
//- minicore: future, option
async fn test() {
let f = async move |x: i32| x + 42;
f;
// ^ |i32| -> impl Future<Output = i32>
let a = f(4);
a;
// ^ impl Future<Output = i32>
let x = a.await;
x;
// ^ i32
let f = async move || 42;
f;
// ^ || -> impl Future<Output = i32>
let a = f();
a;
// ^ impl Future<Output = i32>
let x = a.await;
x;
// ^ i32
let b = ((async move || {})()).await;
b;
// ^ ()
let c = async move || {
let y = None;
y
// ^ Option<u64>
};
let _: Option<u64> = c().await;
c;
// ^ || -> impl Future<Output = Option<u64>>
}
"#,
);
}
#[test]
fn auto_sized_async_block() {
check_no_mismatches(
@ -493,29 +533,30 @@ fn tuple_struct_with_fn() {
r#"
struct S(fn(u32) -> u64);
fn test() -> u64 {
let a = S(|i| 2*i);
let a = S(|i| 2*i as u64);
let b = a.0(4);
a.0(2)
}"#,
expect![[r#"
43..101 '{ ...0(2) }': u64
43..108 '{ ...0(2) }': u64
53..54 'a': S
57..58 'S': S(fn(u32) -> u64) -> S
57..67 'S(|i| 2*i)': S
59..66 '|i| 2*i': |u32| -> u64
57..74 'S(|i| ...s u64)': S
59..73 '|i| 2*i as u64': |u32| -> u64
60..61 'i': u32
63..64 '2': u32
63..66 '2*i': u32
63..64 '2': u64
63..73 '2*i as u64': u64
65..66 'i': u32
77..78 'b': u64
81..82 'a': S
81..84 'a.0': fn(u32) -> u64
81..87 'a.0(4)': u64
85..86 '4': u32
93..94 'a': S
93..96 'a.0': fn(u32) -> u64
93..99 'a.0(2)': u64
97..98 '2': u32
65..73 'i as u64': u64
84..85 'b': u64
88..89 'a': S
88..91 'a.0': fn(u32) -> u64
88..94 'a.0(4)': u64
92..93 '4': u32
100..101 'a': S
100..103 'a.0': fn(u32) -> u64
100..106 'a.0(2)': u64
104..105 '2': u32
"#]],
);
}