Give `?` inside a closure the closure's return type
`?` returns from the closure it is written in, not from the function around it -- but `self.ret` was left holding the enclosing function's type, so the early exit would have been built to the wrong shape wherever a closure's type differed. Scoped to the closure, and cleared when the closure's return type is not known, which turns a silently wrong lowering into the refusal it should have been. The four winnow cases reported against it stay refused: those closures are arguments to a generic parameter, so nothing says what they return, and `?` cannot be lowered without that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7010b5d parent: 9a32146 modified
src/lower.rs +14 -1 | @@ -6007,7 +6007,14 @@ impl Lowerer { | ||
| 6007 | 6007 | let outer_out = std::mem::take(&mut self.out); |
| 6008 | 6008 | let outer_indent = std::mem::replace(&mut self.indent, base + 2); |
| 6009 | 6009 | let outer_target = self.target.take(); |
| 6010 | + // `?` in a closure returns from the *closure*, so its return type | |
| 6011 | + // is what the early exit has to build. | |
| 6012 | + let outer_ret = match &ret_ann { | |
| 6013 | + Some(t) => self.ret.replace(t.clone()), | |
| 6014 | + None => self.ret.take(), | |
| 6015 | + }; | |
| 6010 | 6016 | let r = self.block_body_at(&block, ret_ann.as_ref()); |
| 6017 | + self.ret = outer_ret; | |
| 6011 | 6018 | self.target = outer_target; |
| 6012 | 6019 | let tail = match r { |
| 6013 | 6020 | Ok(t) => t, |
| @@ -6049,7 +6056,13 @@ impl Lowerer { | ||
| 6049 | 6056 | .ok_or("a closure body with statements is not implemented yet")?, |
| 6050 | 6057 | other => other, |
| 6051 | 6058 | }; |
| 6052 | - let v = self.expr_at(body, ret_ann.as_ref())?; | |
| 6059 | + let outer_ret = match &ret_ann { | |
| 6060 | + Some(t) => self.ret.replace(t.clone()), | |
| 6061 | + None => self.ret.take(), | |
| 6062 | + }; | |
| 6063 | + let v = self.expr_at(body, ret_ann.as_ref()); | |
| 6064 | + self.ret = outer_ret; | |
| 6065 | + let v = v?; | |
| 6053 | 6066 | self.pop_scope(); |
| 6054 | 6067 | |
| 6055 | 6068 | let ret = ret_ann |
| @@ -6007,7 +6007,14 @@ impl Lowerer { | |||
| 6007 | let outer_out = std::mem::take(&mut self.out); | 6007 | let outer_out = std::mem::take(&mut self.out); |
| 6008 | let outer_indent = std::mem::replace(&mut self.indent, base + 2); | 6008 | let outer_indent = std::mem::replace(&mut self.indent, base + 2); |
| 6009 | let outer_target = self.target.take(); | 6009 | let outer_target = self.target.take(); |
| 6010 | + // `?` in a closure returns from the *closure*, so its return type | ||
| 6011 | + // is what the early exit has to build. | ||
| 6012 | + let outer_ret = match &ret_ann { | ||
| 6013 | + Some(t) => self.ret.replace(t.clone()), | ||
| 6014 | + None => self.ret.take(), | ||
| 6015 | + }; | ||
| 6010 | let r = self.block_body_at(&block, ret_ann.as_ref()); | 6016 | let r = self.block_body_at(&block, ret_ann.as_ref()); |
| 6017 | + self.ret = outer_ret; | ||
| 6011 | self.target = outer_target; | 6018 | self.target = outer_target; |
| 6012 | let tail = match r { | 6019 | let tail = match r { |
| 6013 | Ok(t) => t, | 6020 | Ok(t) => t, |
| @@ -6049,7 +6056,13 @@ impl Lowerer { | |||
| 6049 | .ok_or("a closure body with statements is not implemented yet")?, | 6056 | .ok_or("a closure body with statements is not implemented yet")?, |
| 6050 | other => other, | 6057 | other => other, |
| 6051 | }; | 6058 | }; |
| 6052 | - let v = self.expr_at(body, ret_ann.as_ref())?; | 6059 | + let outer_ret = match &ret_ann { |
| 6060 | + Some(t) => self.ret.replace(t.clone()), | ||
| 6061 | + None => self.ret.take(), | ||
| 6062 | + }; | ||
| 6063 | + let v = self.expr_at(body, ret_ann.as_ref()); | ||
| 6064 | + self.ret = outer_ret; | ||
| 6065 | + let v = v?; | ||
| 6053 | self.pop_scope(); | 6066 | self.pop_scope(); |
| 6054 | 6067 | ||
| 6055 | let ret = ret_ann | 6068 | let ret = ret_ann |