mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
26a413e015
commit
8807fc4cc3
64 changed files with 2244 additions and 1607 deletions
|
@ -190,9 +190,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
|
|||
pub enum InferenceDiagnostic {
|
||||
NoSuchField { expr: ExprId },
|
||||
BreakOutsideOfLoop { expr: ExprId, is_break: bool },
|
||||
IncorrectTryTarget { expr: ExprId },
|
||||
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
||||
DoesNotImplement { expr: ExprId, trait_: TraitId, ty: Ty },
|
||||
}
|
||||
|
||||
/// A mismatch between an expected and an inferred type.
|
||||
|
@ -907,6 +905,17 @@ impl<'a> InferenceContext<'a> {
|
|||
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
|
||||
}
|
||||
|
||||
fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
|
||||
// FIXME resolve via lang_item once try v2 is stable
|
||||
let path = path![core::ops::Try];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||
let trait_data = self.db.trait_data(trait_);
|
||||
trait_data
|
||||
// FIXME remove once try v2 is stable
|
||||
.associated_type_by_name(&name![Ok])
|
||||
.or_else(|| trait_data.associated_type_by_name(&name![Output]))
|
||||
}
|
||||
|
||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||
let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
|
|
|
@ -19,24 +19,24 @@ use hir_def::{
|
|||
resolver::resolver_for_expr,
|
||||
ConstParamId, FieldId, ItemContainerId, Lookup,
|
||||
};
|
||||
use hir_expand::{name, name::Name};
|
||||
use hir_expand::name::Name;
|
||||
use stdx::always;
|
||||
use syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
autoderef::{self, Autoderef},
|
||||
consteval,
|
||||
infer::{coerce::CoerceMany, find_continuable, path, BreakableKind},
|
||||
infer::{coerce::CoerceMany, find_continuable, BreakableKind},
|
||||
lower::{
|
||||
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
||||
},
|
||||
mapping::{from_chalk, ToChalk},
|
||||
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_assoc_type_id, to_chalk_trait_id,
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
utils::{generics, Generics},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner,
|
||||
ProjectionTy, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
|
||||
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -564,29 +564,9 @@ impl<'a> InferenceContext<'a> {
|
|||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
||||
}
|
||||
&Expr::Try { expr } => {
|
||||
let inner_ty = self.infer_expr_inner(expr, &Expectation::none());
|
||||
match self.resolve_try_impl_for(inner_ty.clone()) {
|
||||
Some((_, Some((output, residual)))) => {
|
||||
if let Some((_trait, false)) =
|
||||
self.implements_from_residual(self.return_ty.clone(), residual)
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::IncorrectTryTarget {
|
||||
expr: tgt_expr,
|
||||
});
|
||||
}
|
||||
output
|
||||
}
|
||||
Some((trait_, None)) => {
|
||||
self.push_diagnostic(InferenceDiagnostic::DoesNotImplement {
|
||||
expr,
|
||||
trait_,
|
||||
ty: inner_ty,
|
||||
});
|
||||
self.err_ty()
|
||||
}
|
||||
None => self.err_ty(),
|
||||
}
|
||||
Expr::Try { expr } => {
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
|
||||
|
@ -1550,67 +1530,4 @@ impl<'a> InferenceContext<'a> {
|
|||
let ctx = self.breakables.pop().expect("breakable stack broken");
|
||||
(ctx.may_break.then(|| ctx.coerce.complete()), res)
|
||||
}
|
||||
|
||||
/// Check whether `ty` implements `FromResidual<r>`
|
||||
fn implements_from_residual(&mut self, ty: Ty, r: Ty) -> Option<(hir_def::TraitId, bool)> {
|
||||
let from_residual_trait = self
|
||||
.resolver
|
||||
.resolve_known_trait(self.db.upcast(), &(super::path![core::ops::FromResidual]))?;
|
||||
let r = GenericArgData::Ty(r).intern(Interner);
|
||||
let b = TyBuilder::trait_ref(self.db, from_residual_trait);
|
||||
if b.remaining() != 2 {
|
||||
return Some((from_residual_trait, false));
|
||||
}
|
||||
let trait_ref = b.push(ty).push(r).build();
|
||||
Some((from_residual_trait, self.table.try_obligation(trait_ref.cast(Interner)).is_some()))
|
||||
}
|
||||
|
||||
fn resolve_try_impl_for(&mut self, ty: Ty) -> Option<(hir_def::TraitId, Option<(Ty, Ty)>)> {
|
||||
let path = path![core::ops::Try];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||
|
||||
let trait_ref = TyBuilder::trait_ref(self.db, trait_).push(ty).build();
|
||||
let substitution = trait_ref.substitution.clone();
|
||||
self.push_obligation(trait_ref.clone().cast(Interner));
|
||||
|
||||
let trait_data = self.db.trait_data(trait_);
|
||||
let output = trait_data.associated_type_by_name(&name![Output]);
|
||||
let residual = trait_data.associated_type_by_name(&name![Residual]);
|
||||
|
||||
let output_ty = match output {
|
||||
Some(output) => {
|
||||
let output_ty = self.table.new_type_var();
|
||||
let alias_eq = AliasEq {
|
||||
alias: AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(output),
|
||||
substitution: substitution.clone(),
|
||||
}),
|
||||
ty: output_ty.clone(),
|
||||
};
|
||||
self.push_obligation(alias_eq.cast(Interner));
|
||||
output_ty
|
||||
}
|
||||
None => self.err_ty(),
|
||||
};
|
||||
let residual_ty = match residual {
|
||||
Some(residual) => {
|
||||
let residual_ty = self.table.new_type_var();
|
||||
let alias_eq = AliasEq {
|
||||
alias: AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(residual),
|
||||
substitution,
|
||||
}),
|
||||
ty: residual_ty.clone(),
|
||||
};
|
||||
self.push_obligation(alias_eq.cast(Interner));
|
||||
residual_ty
|
||||
}
|
||||
None => self.err_ty(),
|
||||
};
|
||||
// FIXME: We are doing the work twice here I think?
|
||||
Some((
|
||||
trait_,
|
||||
self.table.try_obligation(trait_ref.cast(Interner)).map(|_| (output_ty, residual_ty)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1111,24 +1111,6 @@ pub fn resolve_indexing_op(
|
|||
}
|
||||
None
|
||||
}
|
||||
/// Returns the receiver type for the try branch trait call.
|
||||
pub fn resolve_branch_op(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
ty: Canonical<Ty>,
|
||||
try_trait: TraitId,
|
||||
) -> Option<ReceiverAdjustments> {
|
||||
let mut table = InferenceTable::new(db, env.clone());
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
|
||||
for (ty, adj) in deref_chain.into_iter().zip(adj) {
|
||||
let goal = generic_implements_goal(db, env.clone(), try_trait, &ty);
|
||||
if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
|
||||
return Some(adj);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
macro_rules! check_that {
|
||||
($cond:expr) => {
|
||||
|
|
|
@ -162,16 +162,98 @@ fn test() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
v;
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod ops {
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod result {
|
||||
pub enum Result<O, E> {
|
||||
Ok(O),
|
||||
Err(E)
|
||||
}
|
||||
|
||||
impl<O, E> crate::ops::Try for Result<O, E> {
|
||||
type Ok = O;
|
||||
type Error = E;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub mod rust_2018 {
|
||||
pub use crate::{result::*, ops::*};
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try_trait_v2() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: try
|
||||
fn test() -> core::ops::ControlFlow<u32, f32> {
|
||||
let r: core::ops::ControlFlow<u32, f32> = core::ops::ControlFlow::Continue(1.0);
|
||||
//- /main.rs crate:main deps:core
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
//^ f32
|
||||
r
|
||||
v;
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
mod ops {
|
||||
mod try_trait {
|
||||
pub trait Try: FromResidual {
|
||||
type Output;
|
||||
type Residual;
|
||||
}
|
||||
pub trait FromResidual<R = <Self as Try>::Residual> {}
|
||||
}
|
||||
|
||||
pub use self::try_trait::FromResidual;
|
||||
pub use self::try_trait::Try;
|
||||
}
|
||||
|
||||
mod convert {
|
||||
pub trait From<T> {}
|
||||
impl<T> From<T> for T {}
|
||||
}
|
||||
|
||||
pub mod result {
|
||||
use crate::convert::From;
|
||||
use crate::ops::{Try, FromResidual};
|
||||
|
||||
pub enum Infallible {}
|
||||
pub enum Result<O, E> {
|
||||
Ok(O),
|
||||
Err(E)
|
||||
}
|
||||
|
||||
impl<O, E> Try for Result<O, E> {
|
||||
type Output = O;
|
||||
type Error = Result<Infallible, E>;
|
||||
}
|
||||
|
||||
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub mod rust_2018 {
|
||||
pub use crate::result::*;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue