Skip to content

Revert "feat: Diagnose some incorrect usages of the question mark operator" #13433

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

Merged
merged 1 commit into from
Oct 18, 2022
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
1 change: 0 additions & 1 deletion crates/hir-expand/src/mod_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ macro_rules! __known_path {
(core::future::Future) => {};
(core::future::IntoFuture) => {};
(core::ops::Try) => {};
(core::ops::FromResidual) => {};
($path:path) => {
compile_error!("Please register your known path in the path module")
};
Expand Down
2 changes: 0 additions & 2 deletions crates/hir-expand/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,6 @@ pub mod known {
RangeToInclusive,
RangeTo,
Range,
Residual,
FromResidual,
Neg,
Not,
None,
Expand Down
13 changes: 11 additions & 2 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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])
Expand Down
99 changes: 8 additions & 91 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)),
))
}
}
18 changes: 0 additions & 18 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
92 changes: 87 additions & 5 deletions crates/hir-ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
}
}
"#,
);
Expand Down
14 changes: 1 addition & 13 deletions crates/hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::{path::ModPath, TraitId};
use hir_def::path::ModPath;
use hir_expand::{name::Name, HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};

Expand All @@ -33,15 +33,13 @@ diagnostics![
BreakOutsideOfLoop,
InactiveCode,
IncorrectCase,
IncorrectTryExpr,
InvalidDeriveTarget,
MacroError,
MalformedDerive,
MismatchedArgCount,
MissingFields,
MissingMatchArms,
MissingUnsafe,
NotImplemented,
NoSuchField,
ReplaceFilterMapNextWithFindMap,
TypeMismatch,
Expand Down Expand Up @@ -155,16 +153,6 @@ pub struct MismatchedArgCount {
pub expected: usize,
pub found: usize,
}
#[derive(Debug)]
pub struct IncorrectTryExpr {
pub expr: InFile<AstPtr<ast::Expr>>,
}
#[derive(Debug)]
pub struct NotImplemented {
pub expr: InFile<AstPtr<ast::Expr>>,
pub trait_: TraitId,
pub ty: Type,
}

#[derive(Debug)]
pub struct MissingMatchArms {
Expand Down
Loading