]> git.lizzy.rs Git - rust.git/commitdiff
Diagnose incorrect usages of the question mark operator
authorLukas Wirth <lukastw97@gmail.com>
Wed, 5 Oct 2022 17:15:07 +0000 (19:15 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Sun, 16 Oct 2022 10:58:24 +0000 (12:58 +0200)
13 files changed:
crates/hir-expand/src/mod_path.rs
crates/hir-expand/src/name.rs
crates/hir-ty/src/infer.rs
crates/hir-ty/src/infer/expr.rs
crates/hir-ty/src/method_resolution.rs
crates/hir-ty/src/tests/traits.rs
crates/hir/src/diagnostics.rs
crates/hir/src/lib.rs
crates/ide-diagnostics/src/handlers/incorrect_try_expr.rs [new file with mode: 0644]
crates/ide-diagnostics/src/handlers/not_implemented.rs [new file with mode: 0644]
crates/ide-diagnostics/src/lib.rs
crates/ide/src/hover/tests.rs
crates/test-utils/src/minicore.rs

index d7586d129b768b1e639b915df81f51aa4a5c89b5..68413df420c7d7053c1f707f31fb5cc21d7affa0 100644 (file)
@@ -259,6 +259,7 @@ 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")
     };
index 2679a1c3602671c35fcae81ba8f9fdef4eb90042..8a735b965ab8460a49003399a12b25e13cb9830b 100644 (file)
@@ -279,6 +279,8 @@ macro_rules! known_names {
         RangeToInclusive,
         RangeTo,
         Range,
+        Residual,
+        FromResidual,
         Neg,
         Not,
         None,
index 31e56dec62593770bb938e2ddaa7dc7f6b39731c..4315ccab6c1112f4ff620f227f3bd7ada39151b2 100644 (file)
@@ -190,7 +190,9 @@ fn map<U>(self, f: impl FnOnce(T) -> U) -> InferOk<U> {
 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.
@@ -905,17 +907,6 @@ fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
         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])
index f56108b26c45bdaea3096f4616828c28ba260d12..59ab50d0717ba6d5050e28c533f3c3543a881740 100644 (file)
     resolver::resolver_for_expr,
     ConstParamId, FieldId, ItemContainerId, Lookup,
 };
-use hir_expand::name::Name;
+use hir_expand::{name, name::Name};
 use stdx::always;
 use syntax::ast::RangeOp;
 
 use crate::{
     autoderef::{self, Autoderef},
     consteval,
-    infer::{coerce::CoerceMany, find_continuable, BreakableKind},
+    infer::{coerce::CoerceMany, find_continuable, path, 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_chalk_trait_id,
+    static_lifetime, to_assoc_type_id, to_chalk_trait_id,
     utils::{generics, Generics},
-    AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
-    Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    AdtId, AliasEq, AliasTy, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner,
+    ProjectionTy, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 use super::{
@@ -564,9 +564,29 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                 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());
-                self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
+            &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::Cast { expr, type_ref } => {
                 // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
@@ -1530,4 +1550,67 @@ fn with_breakable_ctx<T>(
         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)),
+        ))
+    }
 }
index 5998680dcd395390dcee0197baf6c8b6052a8acb..224dc21e94e58b2de31529ffa44d712003e542e5 100644 (file)
@@ -1111,6 +1111,24 @@ 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) => {
index 555b6972fb71eecb775979f640b33baa2e3a712e..b91172e33422d67ad40c74ad62f4167399240754 100644 (file)
@@ -162,98 +162,16 @@ 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#"
-//- /main.rs crate:main deps:core
-fn test() {
-    let r: Result<i32, u64> = Result::Ok(1);
+//- minicore: try
+fn test() -> core::ops::ControlFlow<u32, f32> {
+    let r: core::ops::ControlFlow<u32, f32> = core::ops::ControlFlow::Continue(1.0);
     let v = 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::*;
-    }
+      //^ f32
+    r
 }
 "#,
     );
index c5dc60f1ec5f962e319af3c48191fe2769984243..6c8b3088adc0f1c56779c651136d18d06fea5712 100644 (file)
@@ -6,7 +6,7 @@
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
-use hir_def::path::ModPath;
+use hir_def::{path::ModPath, TraitId};
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
 
@@ -33,6 +33,7 @@ fn from(d: $diag) -> AnyDiagnostic {
     BreakOutsideOfLoop,
     InactiveCode,
     IncorrectCase,
+    IncorrectTryExpr,
     InvalidDeriveTarget,
     MacroError,
     MalformedDerive,
@@ -40,6 +41,7 @@ fn from(d: $diag) -> AnyDiagnostic {
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
+    NotImplemented,
     NoSuchField,
     ReplaceFilterMapNextWithFindMap,
     TypeMismatch,
@@ -153,6 +155,16 @@ 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 {
index f5324208c9a4ef12cf6704f1a9066b11c7378b27..e6c5c6b5833980eaf9f8679fc5945c9ee3ec18dd 100644 (file)
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
-        MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
-        UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
-        UnresolvedModule, UnresolvedProcMacro,
+        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, IncorrectTryExpr,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingUnsafe, NoSuchField, NotImplemented,
+        ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
+        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
+        UnresolvedProcMacro,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -1282,30 +1283,45 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
         let infer = db.infer(self.into());
         let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
         for d in &infer.diagnostics {
-            match d {
+            match *d {
                 hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
-                    let field = source_map.field_syntax(*expr);
+                    let field = source_map.field_syntax(expr);
                     acc.push(NoSuchField { field }.into())
                 }
-                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
+                hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
                     let expr = source_map
                         .expr_syntax(expr)
                         .expect("break outside of loop in synthetic syntax");
                     acc.push(BreakOutsideOfLoop { expr, is_break }.into())
                 }
                 hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
-                    match source_map.expr_syntax(*call_expr) {
+                    match source_map.expr_syntax(call_expr) {
                         Ok(source_ptr) => acc.push(
                             MismatchedArgCount {
                                 call_expr: source_ptr,
-                                expected: *expected,
-                                found: *found,
+                                expected: expected,
+                                found: found,
                             }
                             .into(),
                         ),
                         Err(SyntheticSyntax) => (),
                     }
                 }
+                hir_ty::InferenceDiagnostic::IncorrectTryTarget { expr } => {
+                    let expr = source_map.expr_syntax(expr).expect("try in synthetic syntax");
+                    acc.push(IncorrectTryExpr { expr }.into())
+                }
+                hir_ty::InferenceDiagnostic::DoesNotImplement { expr, trait_, ref ty } => {
+                    let expr = source_map.expr_syntax(expr).expect("try in synthetic syntax");
+                    acc.push(
+                        NotImplemented {
+                            expr,
+                            trait_,
+                            ty: Type::new(db, DefWithBodyId::from(self), ty.clone()),
+                        }
+                        .into(),
+                    )
+                }
             }
         }
         for (expr, mismatch) in infer.expr_type_mismatches() {
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_try_expr.rs b/crates/ide-diagnostics/src/handlers/incorrect_try_expr.rs
new file mode 100644 (file)
index 0000000..085d8d3
--- /dev/null
@@ -0,0 +1,37 @@
+use hir::InFile;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: incorrect-try-target
+//
+// This diagnostic is triggered if a question mark operator was used in a context where it is not applicable.
+pub(crate) fn incorrect_try_expr(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::IncorrectTryExpr,
+) -> Diagnostic {
+    Diagnostic::new(
+        "incorrect-try-target",
+        format!("the return type of the containing function does not implement `FromResidual`"),
+        ctx.sema
+            .diagnostics_display_range(InFile::new(d.expr.file_id, d.expr.value.clone().into()))
+            .range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn try_ops_diag() {
+        check_diagnostics(
+            r#"
+//- minicore: try
+fn test() {
+    core::ops::ControlFlow::<u32, f32>::Continue(1.0)?;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the return type of the containing function does not implement `FromResidual`
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/not_implemented.rs b/crates/ide-diagnostics/src/handlers/not_implemented.rs
new file mode 100644 (file)
index 0000000..3bf6a42
--- /dev/null
@@ -0,0 +1,35 @@
+use hir::{db::DefDatabase, HirDisplay};
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: not-implemented
+//
+// This diagnostic is triggered if a type doesn't implement a necessary trait.
+pub(crate) fn not_implemented(ctx: &DiagnosticsContext<'_>, d: &hir::NotImplemented) -> Diagnostic {
+    Diagnostic::new(
+        "not-implemented",
+        format!(
+            "the trait `{}` is not implemented for `{}`",
+            ctx.sema.db.trait_data(d.trait_).name,
+            d.ty.display(ctx.sema.db)
+        ),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn missing_try_impl() {
+        check_diagnostics(
+            r#"
+//- minicore: try
+fn main() {
+    ()?;
+} //^^ error: the trait `Try` is not implemented for `()`
+"#,
+        )
+    }
+}
index ae299f0584148937c64b897e7cf658359a5c7069..4577072149a72d7b6d3a0d64f391f1bc5ef6404d 100644 (file)
@@ -29,6 +29,7 @@ mod handlers {
     pub(crate) mod break_outside_of_loop;
     pub(crate) mod inactive_code;
     pub(crate) mod incorrect_case;
+    pub(crate) mod incorrect_try_expr;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
     pub(crate) mod malformed_derive;
@@ -36,6 +37,7 @@ mod handlers {
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
     pub(crate) mod missing_unsafe;
+    pub(crate) mod not_implemented;
     pub(crate) mod no_such_field;
     pub(crate) mod replace_filter_map_next_with_find_map;
     pub(crate) mod type_mismatch;
@@ -225,12 +227,14 @@ pub fn diagnostics(
         let d = match diag {
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+            AnyDiagnostic::IncorrectTryExpr(d) => handlers::incorrect_try_expr::incorrect_try_expr(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
             AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
             AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
             AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
             AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
+            AnyDiagnostic::NotImplemented(d) => handlers::not_implemented::not_implemented(&ctx, &d),
             AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
             AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
             AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
index eb997e6fef83022f726dec8a2ff0e5cbcd6fdc86..5cab017a58dbdba582340ca11d9c682bfd58294c 100644 (file)
@@ -4913,6 +4913,22 @@ fn foo() -> NotResult<(), Short> {
                 ```
             "#]],
     );
+    check_hover_range(
+        r#"
+//- minicore: try
+use core::ops::ControlFlow;
+fn foo() -> ControlFlow<()> {
+    $0ControlFlow::Break(())?$0;
+    ControlFlow::Continue(())
+}
+"#,
+        expect![[r#"
+            ```text
+            Try Target Type: ControlFlow<(), {unknown}>
+            Propagated as:          ControlFlow<(), ()>
+            ```
+        "#]],
+    );
 }
 
 #[test]
@@ -4928,9 +4944,9 @@ fn foo() -> Option<()> {
 }
 "#,
         expect![[r#"
-                ```rust
-                <Option<i32> as Try>::Output
-                ```"#]],
+            ```rust
+            i32
+            ```"#]],
     );
 }
 
index 69d2e62b2567357541b81ab43bb83ea2ff1ac7f3..59b1c147d7f174e08339de3225c5e3541b2af1c9 100644 (file)
@@ -27,6 +27,7 @@
 //!     generator: pin
 //!     hash:
 //!     index: sized
+//!     infallible:
 //!     iterator: option
 //!     iterators: iterator, fn
 //!     option:
@@ -36,7 +37,7 @@
 //!     result:
 //!     sized:
 //!     slice:
-//!     try:
+//!     try: infallible
 //!     unsize: sized
 
 pub mod marker {
@@ -150,6 +151,9 @@ pub trait AsRef<T: ?Sized> {
         fn as_ref(&self) -> &T;
     }
     // endregion:as_ref
+    // region:infallible
+    pub enum Infallible {}
+    // endregion:infallible
 }
 
 pub mod ops {
@@ -326,7 +330,7 @@ pub enum ControlFlow<B, C = ()> {
             Continue(C),
             Break(B),
         }
-        pub trait FromResidual<R = Self::Residual> {
+        pub trait FromResidual<R = <Self as Try>::Residual> {
             #[lang = "from_residual"]
             fn from_residual(residual: R) -> Self;
         }
@@ -342,13 +346,13 @@ pub trait Try: FromResidual<Self::Residual> {
 
         impl<B, C> Try for ControlFlow<B, C> {
             type Output = C;
-            type Residual = ControlFlow<B, convert::Infallible>;
+            type Residual = ControlFlow<B, crate::convert::Infallible>;
             fn from_output(output: Self::Output) -> Self {}
             fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
         }
 
         impl<B, C> FromResidual for ControlFlow<B, C> {
-            fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
+            fn from_residual(residual: ControlFlow<B, crate::convert::Infallible>) -> Self {}
         }
     }
     pub use self::try_::{ControlFlow, FromResidual, Try};
@@ -469,6 +473,33 @@ pub const fn unwrap(self) -> T {
             }
         }
     }
+    // region:try
+    impl<T> crate::ops::Try for Option<T> {
+        type Output = T;
+        type Residual = Option<crate::convert::Infallible>;
+
+        #[inline]
+        fn from_output(output: Self::Output) -> Self {
+            Some(output)
+        }
+
+        #[inline]
+        fn branch(self) -> crate::ops::ControlFlow<Self::Residual, Self::Output> {
+            match self {
+                Some(v) => crate::ops::ControlFlow::Continue(v),
+                None => crate::ops::ControlFlow::Break(None),
+            }
+        }
+    }
+    impl<T> crate::ops::FromResidual for Option<T> {
+        #[inline]
+        fn from_residual(residual: Option<crate::convert::Infallible>) -> Self {
+            match residual {
+                None => None,
+            }
+        }
+    }
+    // endregion:try
 }
 // endregion:option