Set expectation for no-semi expression statements to unit
This commit is contained in:
parent
73e2505cfa
commit
24ba1bed04
@ -294,8 +294,8 @@ impl Resolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let res @ Some(_) = self.module_scope.resolve_path_in_value_ns(db, path) {
|
if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
|
||||||
return res;
|
return Some(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
|
// If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
|
||||||
|
@ -130,7 +130,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
);
|
);
|
||||||
let ty = match label {
|
let ty = match label {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
let break_ty = self.table.new_type_var();
|
let break_ty = expected.coercion_target_type(&mut self.table);
|
||||||
let (breaks, ty) = self.with_breakable_ctx(
|
let (breaks, ty) = self.with_breakable_ctx(
|
||||||
BreakableKind::Block,
|
BreakableKind::Block,
|
||||||
Some(break_ty.clone()),
|
Some(break_ty.clone()),
|
||||||
@ -403,37 +403,47 @@ impl<'a> InferenceContext<'a> {
|
|||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
||||||
|
|
||||||
let expected = expected.adjust_for_branches(&mut self.table);
|
if arms.is_empty() {
|
||||||
|
self.diverges = Diverges::Always;
|
||||||
let result_ty = if arms.is_empty() {
|
|
||||||
self.result.standard_types.never.clone()
|
self.result.standard_types.never.clone()
|
||||||
} else {
|
} else {
|
||||||
expected.coercion_target_type(&mut self.table)
|
let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
};
|
let mut all_arms_diverge = Diverges::Always;
|
||||||
let mut coerce = CoerceMany::new(result_ty);
|
for arm in arms.iter() {
|
||||||
|
let input_ty = self.resolve_ty_shallow(&input_ty);
|
||||||
let matchee_diverges = self.diverges;
|
self.infer_top_pat(arm.pat, &input_ty);
|
||||||
let mut all_arms_diverge = Diverges::Always;
|
|
||||||
|
|
||||||
for arm in arms.iter() {
|
|
||||||
self.diverges = Diverges::Maybe;
|
|
||||||
let input_ty = self.resolve_ty_shallow(&input_ty);
|
|
||||||
self.infer_top_pat(arm.pat, &input_ty);
|
|
||||||
if let Some(guard_expr) = arm.guard {
|
|
||||||
self.infer_expr(
|
|
||||||
guard_expr,
|
|
||||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
let expected = expected.adjust_for_branches(&mut self.table);
|
||||||
all_arms_diverge &= self.diverges;
|
let result_ty = match &expected {
|
||||||
coerce.coerce(self, Some(arm.expr), &arm_ty);
|
// We don't coerce to `()` so that if the match expression is a
|
||||||
|
// statement it's branches can have any consistent type.
|
||||||
|
Expectation::HasType(ty) if *ty != self.result.standard_types.unit => {
|
||||||
|
ty.clone()
|
||||||
|
}
|
||||||
|
_ => self.table.new_type_var(),
|
||||||
|
};
|
||||||
|
let mut coerce = CoerceMany::new(result_ty);
|
||||||
|
|
||||||
|
for arm in arms.iter() {
|
||||||
|
if let Some(guard_expr) = arm.guard {
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
|
self.infer_expr(
|
||||||
|
guard_expr,
|
||||||
|
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
|
|
||||||
|
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||||
|
all_arms_diverge &= self.diverges;
|
||||||
|
coerce.coerce(self, Some(arm.expr), &arm_ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.diverges = matchee_diverges | all_arms_diverge;
|
||||||
|
|
||||||
|
coerce.complete(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.diverges = matchee_diverges | all_arms_diverge;
|
|
||||||
|
|
||||||
coerce.complete(self)
|
|
||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
// FIXME this could be more efficient...
|
// FIXME this could be more efficient...
|
||||||
@ -1179,8 +1189,15 @@ impl<'a> InferenceContext<'a> {
|
|||||||
self.diverges = previous_diverges;
|
self.diverges = previous_diverges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::Expr { expr, .. } => {
|
&Statement::Expr { expr, has_semi } => {
|
||||||
self.infer_expr(*expr, &Expectation::none());
|
self.infer_expr(
|
||||||
|
expr,
|
||||||
|
&if has_semi {
|
||||||
|
Expectation::none()
|
||||||
|
} else {
|
||||||
|
Expectation::HasType(self.result.standard_types.unit.clone())
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,20 +40,14 @@ impl<'a> InferenceContext<'a> {
|
|||||||
id: ExprOrPatId,
|
id: ExprOrPatId,
|
||||||
) -> Option<Ty> {
|
) -> Option<Ty> {
|
||||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||||
if path.segments().is_empty() {
|
let Some(last) = path.segments().last() else { return None };
|
||||||
// This can't actually happen syntax-wise
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let ty = self.make_ty(type_ref);
|
let ty = self.make_ty(type_ref);
|
||||||
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
||||||
let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
|
let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
|
||||||
let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
|
let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
|
||||||
self.resolve_ty_assoc_item(
|
self.resolve_ty_assoc_item(ty, last.name, id)?
|
||||||
ty,
|
|
||||||
path.segments().last().expect("path had at least one segment").name,
|
|
||||||
id,
|
|
||||||
)?
|
|
||||||
} else {
|
} else {
|
||||||
|
// FIXME: report error, unresolved first path segment
|
||||||
let value_or_partial =
|
let value_or_partial =
|
||||||
resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
|
resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
|
||||||
|
|
||||||
@ -66,10 +60,13 @@ impl<'a> InferenceContext<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let typable: ValueTyDefId = match value {
|
let typable: ValueTyDefId = match value {
|
||||||
ValueNs::LocalBinding(pat) => {
|
ValueNs::LocalBinding(pat) => match self.result.type_of_pat.get(pat) {
|
||||||
let ty = self.result.type_of_pat.get(pat)?.clone();
|
Some(ty) => return Some(ty.clone()),
|
||||||
return Some(ty);
|
None => {
|
||||||
}
|
never!("uninferred pattern?");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
ValueNs::FunctionId(it) => it.into(),
|
ValueNs::FunctionId(it) => it.into(),
|
||||||
ValueNs::ConstId(it) => it.into(),
|
ValueNs::ConstId(it) => it.into(),
|
||||||
ValueNs::StaticId(it) => it.into(),
|
ValueNs::StaticId(it) => it.into(),
|
||||||
@ -91,7 +88,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
|
let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
|
||||||
return Some(ty);
|
return Some(ty);
|
||||||
} else {
|
} else {
|
||||||
// FIXME: diagnostic, invalid Self reference
|
// FIXME: report error, invalid Self reference
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str {
|
|||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_unit_block_expr_stmt_no_semi() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test(x: bool) {
|
||||||
|
if x {
|
||||||
|
"notok"
|
||||||
|
//^^^^^^^ expected (), got &str
|
||||||
|
} else {
|
||||||
|
"ok"
|
||||||
|
//^^^^ expected (), got &str
|
||||||
|
}
|
||||||
|
match x { true => true, false => 0 }
|
||||||
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool
|
||||||
|
//^ expected bool, got i32
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1015,9 +1015,9 @@ fn cfg_tail() {
|
|||||||
20..31 '{ "first" }': ()
|
20..31 '{ "first" }': ()
|
||||||
22..29 '"first"': &str
|
22..29 '"first"': &str
|
||||||
72..190 '{ ...] 13 }': ()
|
72..190 '{ ...] 13 }': ()
|
||||||
78..88 '{ "fake" }': &str
|
78..88 '{ "fake" }': ()
|
||||||
80..86 '"fake"': &str
|
80..86 '"fake"': &str
|
||||||
93..103 '{ "fake" }': &str
|
93..103 '{ "fake" }': ()
|
||||||
95..101 '"fake"': &str
|
95..101 '"fake"': &str
|
||||||
108..120 '{ "second" }': ()
|
108..120 '{ "second" }': ()
|
||||||
110..118 '"second"': &str
|
110..118 '"second"': &str
|
||||||
|
Loading…
x
Reference in New Issue
Block a user