Skip to content

Commit 52767cc

Browse files
esdrubaltritao
andauthored
Fixes issue of missing type annotations on explicit returns. (#5575)
## Description Explicit returns did not have any type annotation causing the method disambiguation to fail. The solution was to add a new field to the `TypeCheckContext` called `function_type_annotation`. This field is initialized only in the type_check of function declarations. The field is used to set the type_annotation one while doing the type check of explicit return expressions. With this change `TypeCheckUnification` was also simplified as the unification is now done with `function_type_annotation`. The result was a few functions that are no longer required to be removed. Closes #5518 ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: João Matos <joao@tritao.eu>
1 parent 046d292 commit 52767cc

File tree

13 files changed

+115
-276
lines changed

13 files changed

+115
-276
lines changed

sway-core/src/language/ty/ast_node.rs

-16
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,6 @@ impl GetDeclIdent for TyAstNode {
154154
}
155155

156156
impl TyAstNode {
157-
/// recurse into `self` and get any return statements -- used to validate that all returns
158-
/// do indeed return the correct type
159-
/// This does _not_ extract implicit return statements as those are not control flow! This is
160-
/// _only_ for explicit returns.
161-
pub(crate) fn gather_return_statements(&self) -> Vec<&TyExpression> {
162-
match &self.content {
163-
// assignments and reassignments can happen during control flow and can abort
164-
TyAstNodeContent::Declaration(TyDecl::VariableDecl(decl)) => {
165-
decl.body.gather_return_statements()
166-
}
167-
TyAstNodeContent::Expression(exp) => exp.gather_return_statements(),
168-
TyAstNodeContent::Error(_, _) => vec![],
169-
TyAstNodeContent::SideEffect(_) | TyAstNodeContent::Declaration(_) => vec![],
170-
}
171-
}
172-
173157
/// Returns `true` if this AST node will be exported in a library, i.e. it is a public declaration.
174158
pub(crate) fn is_public(&self, decl_engine: &DeclEngine) -> bool {
175159
match &self.content {

sway-core/src/language/ty/expression/expression.rs

-8
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,6 @@ impl TyExpression {
424424
}
425425
}
426426

427-
/// recurse into `self` and get any return statements -- used to validate that all returns
428-
/// do indeed return the correct type
429-
/// This does _not_ extract implicit return statements as those are not control flow! This is
430-
/// _only_ for explicit returns.
431-
pub(crate) fn gather_return_statements(&self) -> Vec<&TyExpression> {
432-
self.expression.gather_return_statements()
433-
}
434-
435427
/// gathers the mutability of the expressions within
436428
pub(crate) fn gather_mutability(&self) -> VariableMutability {
437429
match &self.expression {

sway-core/src/language/ty/expression/expression_variant.rs

-117
Original file line numberDiff line numberDiff line change
@@ -1456,121 +1456,4 @@ impl TyExpressionVariant {
14561456
_ => None,
14571457
}
14581458
}
1459-
1460-
/// Recurse into `self` and get any return statements -- used to validate that all returns
1461-
/// do indeed return the correct type.
1462-
/// This does _not_ extract implicit return statements as those are not control flow! This is
1463-
/// _only_ for explicit returns.
1464-
pub(crate) fn gather_return_statements(&self) -> Vec<&TyExpression> {
1465-
match self {
1466-
TyExpressionVariant::MatchExp { desugared, .. } => {
1467-
desugared.expression.gather_return_statements()
1468-
}
1469-
TyExpressionVariant::IfExp {
1470-
condition,
1471-
then,
1472-
r#else,
1473-
} => {
1474-
let mut buf = condition.gather_return_statements();
1475-
buf.append(&mut then.gather_return_statements());
1476-
if let Some(ref r#else) = r#else {
1477-
buf.append(&mut r#else.gather_return_statements());
1478-
}
1479-
buf
1480-
}
1481-
TyExpressionVariant::CodeBlock(TyCodeBlock { contents, .. }) => {
1482-
let mut buf = vec![];
1483-
for node in contents {
1484-
buf.append(&mut node.gather_return_statements())
1485-
}
1486-
buf
1487-
}
1488-
TyExpressionVariant::WhileLoop { condition, body } => {
1489-
let mut buf = condition.gather_return_statements();
1490-
for node in &body.contents {
1491-
buf.append(&mut node.gather_return_statements())
1492-
}
1493-
buf
1494-
}
1495-
TyExpressionVariant::Reassignment(reassignment) => {
1496-
reassignment.rhs.gather_return_statements()
1497-
}
1498-
TyExpressionVariant::LazyOperator { lhs, rhs, .. } => [lhs, rhs]
1499-
.into_iter()
1500-
.flat_map(|expr| expr.gather_return_statements())
1501-
.collect(),
1502-
TyExpressionVariant::Tuple { fields } => fields
1503-
.iter()
1504-
.flat_map(|expr| expr.gather_return_statements())
1505-
.collect(),
1506-
TyExpressionVariant::Array {
1507-
elem_type: _,
1508-
contents,
1509-
} => contents
1510-
.iter()
1511-
.flat_map(|expr| expr.gather_return_statements())
1512-
.collect(),
1513-
TyExpressionVariant::ArrayIndex { prefix, index } => [prefix, index]
1514-
.into_iter()
1515-
.flat_map(|expr| expr.gather_return_statements())
1516-
.collect(),
1517-
TyExpressionVariant::StructFieldAccess { prefix, .. } => {
1518-
prefix.gather_return_statements()
1519-
}
1520-
TyExpressionVariant::TupleElemAccess { prefix, .. } => {
1521-
prefix.gather_return_statements()
1522-
}
1523-
TyExpressionVariant::EnumInstantiation { contents, .. } => contents
1524-
.iter()
1525-
.flat_map(|expr| expr.gather_return_statements())
1526-
.collect(),
1527-
TyExpressionVariant::AbiCast { address, .. } => address.gather_return_statements(),
1528-
TyExpressionVariant::IntrinsicFunction(intrinsic_function_kind) => {
1529-
intrinsic_function_kind
1530-
.arguments
1531-
.iter()
1532-
.flat_map(|expr| expr.gather_return_statements())
1533-
.collect()
1534-
}
1535-
TyExpressionVariant::StructExpression { fields, .. } => fields
1536-
.iter()
1537-
.flat_map(|field| field.value.gather_return_statements())
1538-
.collect(),
1539-
TyExpressionVariant::FunctionApplication {
1540-
contract_call_params,
1541-
arguments,
1542-
selector,
1543-
..
1544-
} => contract_call_params
1545-
.values()
1546-
.chain(arguments.iter().map(|(_name, expr)| expr))
1547-
.chain(
1548-
selector
1549-
.iter()
1550-
.map(|contract_call_params| &*contract_call_params.contract_address),
1551-
)
1552-
.flat_map(|expr| expr.gather_return_statements())
1553-
.collect(),
1554-
TyExpressionVariant::EnumTag { exp } => exp.gather_return_statements(),
1555-
TyExpressionVariant::UnsafeDowncast { exp, .. } => exp.gather_return_statements(),
1556-
TyExpressionVariant::ImplicitReturn(exp) => exp.gather_return_statements(),
1557-
TyExpressionVariant::Return(exp) => {
1558-
vec![exp]
1559-
}
1560-
TyExpressionVariant::Ref(exp) | TyExpressionVariant::Deref(exp) => {
1561-
exp.gather_return_statements()
1562-
}
1563-
// if it is impossible for an expression to contain a return _statement_ (not an
1564-
// implicit return!), put it in the pattern below.
1565-
TyExpressionVariant::Literal(_)
1566-
| TyExpressionVariant::FunctionParameter { .. }
1567-
| TyExpressionVariant::AsmExpression { .. }
1568-
| TyExpressionVariant::ConstantExpression { .. }
1569-
| TyExpressionVariant::VariableExpression { .. }
1570-
| TyExpressionVariant::AbiName(_)
1571-
| TyExpressionVariant::StorageAccess { .. }
1572-
| TyExpressionVariant::Break
1573-
| TyExpressionVariant::Continue => vec![],
1574-
}
1575-
}
15761459
}

sway-core/src/semantic_analysis/ast_node/declaration/function.rs

+2-42
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ impl ty::TyFunctionDecl {
209209
.with_help_text(
210210
"Function body's return type does not match up with its return type annotation.",
211211
)
212-
.with_type_annotation(return_type.type_id);
212+
.with_type_annotation(return_type.type_id)
213+
.with_function_type_annotation(return_type.type_id);
213214

214215
let body = ty::TyCodeBlock::type_check(handler, ctx.by_ref(), body)
215216
.unwrap_or_else(|_err| ty::TyCodeBlock::default());
@@ -223,32 +224,6 @@ impl ty::TyFunctionDecl {
223224
}
224225
}
225226

226-
/// Unifies the types of the return statements and the return type of the
227-
/// function declaration.
228-
fn unify_return_statements(
229-
handler: &Handler,
230-
ctx: TypeCheckContext,
231-
return_statements: &[&ty::TyExpression],
232-
return_type: TypeId,
233-
) -> Result<(), ErrorEmitted> {
234-
let type_engine = ctx.engines.te();
235-
236-
handler.scope(|handler| {
237-
for stmt in return_statements.iter() {
238-
type_engine.unify(
239-
handler,
240-
ctx.engines(),
241-
stmt.return_type,
242-
return_type,
243-
&stmt.span,
244-
"Return statement must return the declared function return type.",
245-
None,
246-
);
247-
}
248-
Ok(())
249-
})
250-
}
251-
252227
impl TypeCheckAnalysis for DeclId<TyFunctionDecl> {
253228
fn type_check_analyze(
254229
&self,
@@ -314,21 +289,6 @@ impl TypeCheckUnification for ty::TyFunctionDecl {
314289

315290
let return_type = &self.return_type;
316291

317-
// gather the return statements
318-
let return_statements: Vec<&ty::TyExpression> = self
319-
.body
320-
.contents
321-
.iter()
322-
.flat_map(|node| node.gather_return_statements())
323-
.collect();
324-
325-
unify_return_statements(
326-
handler,
327-
type_check_ctx.by_ref(),
328-
&return_statements,
329-
return_type.type_id,
330-
)?;
331-
332292
return_type.type_id.check_type_parameter_bounds(
333293
handler,
334294
type_check_ctx.by_ref(),

sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -393,20 +393,12 @@ impl ty::TyExpression {
393393
Ok(typed_expr)
394394
}
395395
ExpressionKind::Return(expr) => {
396+
let function_type_annotation = ctx.function_type_annotation();
396397
let ctx = ctx
397-
// we use "unknown" here because return statements do not
398-
// necessarily follow the type annotation of their immediate
399-
// surrounding context. Because a return statement is control flow
400-
// that breaks out to the nearest function, we need to type check
401-
// it against the surrounding function.
402-
// That is impossible here, as we don't have that information. It
403-
// is the responsibility of the function declaration to type check
404-
// all return statements contained within it.
405398
.by_ref()
406-
.with_type_annotation(type_engine.insert(engines, TypeInfo::Unknown, None))
399+
.with_type_annotation(function_type_annotation)
407400
.with_help_text(
408-
"Returned value must match up with the function return type \
409-
annotation.",
401+
"Return statement must return the declared function return type.",
410402
);
411403
let expr_span = expr.span();
412404
let expr = ty::TyExpression::type_check(handler, ctx, *expr)

sway-core/src/semantic_analysis/type_check_context.rs

+17
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub struct TypeCheckContext<'a> {
4848
///
4949
/// Assists type inference.
5050
type_annotation: TypeId,
51+
/// Assists type inference.
52+
function_type_annotation: TypeId,
5153
/// When true unify_with_type_annotation will use unify_with_generic instead of the default unify.
5254
/// This ensures that expected generic types are unified to more specific received types.
5355
unify_generic: bool,
@@ -118,6 +120,7 @@ impl<'a> TypeCheckContext<'a> {
118120
namespace,
119121
engines,
120122
type_annotation: engines.te().insert(engines, TypeInfo::Unknown, None),
123+
function_type_annotation: engines.te().insert(engines, TypeInfo::Unknown, None),
121124
unify_generic: false,
122125
self_type: None,
123126
type_subst: TypeSubstMap::new(),
@@ -146,6 +149,7 @@ impl<'a> TypeCheckContext<'a> {
146149
TypeCheckContext {
147150
namespace: self.namespace,
148151
type_annotation: self.type_annotation,
152+
function_type_annotation: self.function_type_annotation,
149153
unify_generic: self.unify_generic,
150154
self_type: self.self_type,
151155
type_subst: self.type_subst.clone(),
@@ -168,6 +172,7 @@ impl<'a> TypeCheckContext<'a> {
168172
TypeCheckContext {
169173
namespace,
170174
type_annotation: self.type_annotation,
175+
function_type_annotation: self.function_type_annotation,
171176
unify_generic: self.unify_generic,
172177
self_type: self.self_type,
173178
type_subst: self.type_subst,
@@ -217,6 +222,14 @@ impl<'a> TypeCheckContext<'a> {
217222
}
218223
}
219224

225+
/// Map this `TypeCheckContext` instance to a new one with the given type annotation.
226+
pub(crate) fn with_function_type_annotation(self, function_type_annotation: TypeId) -> Self {
227+
Self {
228+
function_type_annotation,
229+
..self
230+
}
231+
}
232+
220233
/// Map this `TypeCheckContext` instance to a new one with the given type annotation.
221234
pub(crate) fn with_unify_generic(self, unify_generic: bool) -> Self {
222235
Self {
@@ -322,6 +335,10 @@ impl<'a> TypeCheckContext<'a> {
322335
self.type_annotation
323336
}
324337

338+
pub(crate) fn function_type_annotation(&self) -> TypeId {
339+
self.function_type_annotation
340+
}
341+
325342
pub(crate) fn unify_generic(&self) -> bool {
326343
self.unify_generic
327344
}

0 commit comments

Comments
 (0)