From 0a2dc2fd74371097b756af3c86911f8eb26674ed Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Sat, 1 Feb 2025 00:11:39 +0100 Subject: [PATCH 1/8] Add test --- .../language/unify_never/Forc.lock | 13 +++++++++++++ .../language/unify_never/Forc.toml | 8 ++++++++ .../language/unify_never/src/main.sw | 19 +++++++++++++++++++ .../language/unify_never/test.toml | 2 ++ 4 files changed, 42 insertions(+) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.lock new file mode 100644 index 00000000000..d16c3b3b60f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-DFCFC85C94C0C9BD" + +[[package]] +name = "std" +source = "path+from-root-DFCFC85C94C0C9BD" +dependencies = ["core"] + +[[package]] +name = "unify_never" +source = "member" +dependencies = ["std"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.toml new file mode 100644 index 00000000000..1ef6d57569b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "unify_never" +entry = "main.sw" + +[dependencies] +std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw new file mode 100644 index 00000000000..480fc9cb011 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw @@ -0,0 +1,19 @@ +script; + +// Test of unification of the Never type + +fn test_1(){ + let mut v: u32 = 1; + v = return; +} + +fn test_2(){ + let mut v: u32 = return; + v = 1; +} + + +fn main() { + test_1(); + test_2(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml new file mode 100644 index 00000000000..2c5bf1ae09a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml @@ -0,0 +1,2 @@ +category = "compile" +expected_warnings = 0 From 1c0e2bd87b0dd6a4ac3b260a902b405dcdef9684 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 19:19:27 +0100 Subject: [PATCH 2/8] Fix test case --- .../should_pass/language/unify_never/src/main.sw | 8 ++++---- .../should_pass/language/unify_never/test.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw index 480fc9cb011..0252954aa39 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/src/main.sw @@ -3,13 +3,13 @@ script; // Test of unification of the Never type fn test_1(){ - let mut v: u32 = 1; - v = return; + let mut v1: u32 = 1; + v1 = return; } fn test_2(){ - let mut v: u32 = return; - v = 1; + let mut v2: u32 = return; + v2 = 1; } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml index 2c5bf1ae09a..ef91e8aa1ac 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unify_never/test.toml @@ -1,2 +1,2 @@ category = "compile" -expected_warnings = 0 +expected_warnings = 1 From f74ca11a70164f789fdab35a02636849089f26fd Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 19:21:14 +0100 Subject: [PATCH 3/8] Attempted solution + debugs --- .../language/ty/declaration/declaration.rs | 2 +- .../semantic_analysis/ast_node/code_block.rs | 10 +- .../ast_node/declaration/declaration.rs | 2 + .../ast_node/declaration/variable.rs | 83 +- .../typed/typed_match_expression.rs | 7 + .../ast_node/expression/typed_expression.rs | 31 +- .../namespace/lexical_scope.rs | 8 + .../semantic_analysis/type_check_context.rs | 2 +- .../src/semantic_analysis/type_resolve.rs | 11 + sway-core/src/type_system/engine.rs | 2 + .../language/diverging_exprs/src/main.sw | 718 +++++++++--------- 11 files changed, 495 insertions(+), 381 deletions(-) diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index 5a9ba831149..d5a36054940 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -734,7 +734,7 @@ impl TyDecl { let type_engine = engines.te(); let decl_engine = engines.de(); let type_id = match self { - TyDecl::VariableDecl(decl) => decl.body.return_type, + TyDecl::VariableDecl(decl) => decl./*body.*/return_type, TyDecl::FunctionDecl(FunctionDecl { decl_id, .. }) => { let decl = decl_engine.get_function(decl_id); decl.return_type.type_id diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index dfc37050faa..0d30eb36ec7 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -108,12 +108,12 @@ impl ty::TyCodeBlock { }) .flatten(); let span = implicit_return_span.unwrap_or_else(|| code_block.whole_block_span.clone()); - + let block_type = code_block .contents .iter() .find_map(|node| { - match node { + match node { // If an ast node of the block returns, breaks, or continues then the whole block should have Never as return type. ty::TyAstNode { content: @@ -154,6 +154,12 @@ impl ty::TyCodeBlock { }) .unwrap_or_else(|| ctx.engines.te().id_of_unit()); + let current_mod_path = ctx.namespace.current_mod_path(); + let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + if problem { + dbg!(&ctx.engines.te().get(block_type)); + } + (block_type, span) } } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs index 32f1d1cf63f..c115c9fc5c0 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs @@ -93,6 +93,8 @@ impl TyDecl { Ok(res) => res, Err(err) => return Ok(ty::TyDecl::ErrorRecovery(span, err)), }; +// dbg!(&var_decl.name); +// dbg!(&*type_engine.get(var_decl.return_type)); let typed_var_decl = ty::TyDecl::VariableDecl(Box::new(var_decl)); ctx.insert_symbol(handler, name, typed_var_decl.clone())?; typed_var_decl diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index 5f4e8792683..70d89300684 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -40,6 +40,15 @@ impl ty::TyVariableDecl { let mut type_ascription = var_decl.type_ascription.clone(); + let current_mod_path = ctx.namespace.current_mod_path(); + let problem = var_decl.name.as_str() == "__matched_value_1" && + current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + + if problem { + dbg!(type_ascription.type_id); + dbg!(&*type_engine.get(type_ascription.type_id)); + } + type_ascription.type_id = ctx .resolve_type( handler, @@ -49,27 +58,69 @@ impl ty::TyVariableDecl { None, ) .unwrap_or_else(|err| type_engine.id_of_error_recovery(err)); + + if problem { +// dbg!(&body); +// dbg!(&*type_engine.get(body.return_type)); + dbg!(type_ascription.type_id); + dbg!(&*type_engine.get(type_ascription.type_id)); + } + let mut ctx = ctx .with_type_annotation(type_ascription.type_id) .with_help_text( "Variable declaration's type annotation does not match up \ with the assigned expression's type.", ); - let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); + + if problem { +// dbg!(&body); +// dbg!(&*type_engine.get(body.return_type)); + dbg!(type_ascription.type_id); + dbg!(&*type_engine.get(type_ascription.type_id)); + } + +// if problem { +//// dbg!(std::backtrace::Backtrace::force_capture()); +// dbg!(&var_decl.body); +// } + + let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); let body = result .unwrap_or_else(|err| ty::TyExpression::error(err, var_decl.name.span(), engines)); - // TODO: Integers shouldn't be anything special. RHS expressions should be written in - // a way to always use the context provided from the LHS, and if the LHS is - // an integer, RHS should properly unify or type check should fail. - // Remove this special case as a part of the initiative of improving type inference. - // Integers are special in the sense that we can't only rely on the type of `body` - // to get the type of the variable. The type of the variable *has* to follow - // `type_ascription` if `type_ascription` is a concrete integer type that does not - // conflict with the type of `body` (i.e. passes the type checking above). - let return_type = match &*type_engine.get(type_ascription.type_id) { - TypeInfo::UnsignedInteger(_) => type_ascription.type_id, - _ => match &*type_engine.get(body.return_type) { + typechecking of the body changes what type_ascription.type_id refers to - it changes from Unknown to Boolean. + This is almost certainly to do with the unification stuff that happens at the end of ast_node/code_block.rs::type_check. + An obvious guess is that the type of the code block is determined as the type of the implicit return rather than as (Never if a diverging expression exists, or else the implicit return). See TyCodeBloc::compute_return_type_and_span. + + if problem { +// dbg!(&body); + dbg!(&*type_engine.get(body.return_type)); + dbg!(type_ascription.type_id); + dbg!(&*type_engine.get(type_ascription.type_id)); + } +// dbg!(&var_decl.name); +// dbg!(&type_ascription.type_id); +// dbg!(&*type_engine.get(type_ascription.type_id)); +// dbg!(&body.return_type); +// dbg!(&*type_engine.get(body.return_type)); + // Determine the type of the variable going forward. Typically this is the type of the RHS, + // but in some cases we need to use the type ascription instead. + // TODO: We should not have these special cases. The typecheck expressions should be written + // in a way to always use the context provided by the LHS, and use the unified type of + // LHS and RHS as the return type of the RHS. Remove this special case as a part of + // the initiative of improving type inference. + let return_type = + match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { + // Integers: We can't rely on the type of the RHS to give us the correct integer + // type, so the type of the variable *has* to follow `type_ascription` if + // `type_ascription` is a concrete integer type that does not conflict with the type + // of `body` (i.e. passes the type checking above). + (TypeInfo::UnsignedInteger(_), _) | + // Never: If the RHS resolves to Never, then any code following the declaration is + // unreachable. If the variable is used later on, then it should be treated as + // having the same type as the type ascription. + (_, TypeInfo::Never) | // If RHS type check ends up in an error we want to use the // provided type ascription as the variable type. E.g.: // let v: Struct = Struct { x: 0 }; // `v` should be "Struct". @@ -77,10 +128,12 @@ impl ty::TyVariableDecl { // let v = ; // `v` will remain "{unknown}". // TODO: Refine and improve this further. E.g., // let v: Struct { /* MISSING FIELDS */ }; // Despite the error, `v` should be of type "Struct". - TypeInfo::ErrorRecovery(_) => type_ascription.type_id, + (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, + // In all other cases we use the type of the RHS. _ => body.return_type, - }, - }; + }; + +// dbg!(&*type_engine.get(return_type)); if !ctx.code_block_first_pass() { let previous_symbol = ctx diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs index fdb5edfdd8c..01632a5ef62 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs @@ -88,6 +88,13 @@ impl ty::TyMatchExpression { span, }; + let current_mod_path = ctx.namespace.current_mod_path(); + let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + if problem { + dbg!(&ctx.engines.te().get(typed_exp.value_type_id)); + } + + Ok((typed_exp, typed_scrutinees)) } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 65d645dffed..92df58a2c92 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -787,6 +787,12 @@ impl ty::TyExpression { Err(_err) => (ty::TyCodeBlock::default(), type_engine.id_of_unit()), }; + let current_mod_path = ctx.namespace.current_mod_path(); + let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + if problem { + dbg!(&ctx.engines.te().get(block_return_type)); + } + let exp = ty::TyExpression { expression: ty::TyExpressionVariant::CodeBlock(typed_block), return_type: block_return_type, @@ -880,6 +886,15 @@ impl ty::TyExpression { }; let type_id = typed_value.return_type; + let current_mod_path = ctx.namespace.current_mod_path(); + let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + if problem { + dbg!(&value); + dbg!(&typed_value); + dbg!(&ctx.engines.te().get(type_id)); + } + + // check to make sure that the type of the value is something that can be matched upon type_engine .get(type_id) @@ -2357,6 +2372,7 @@ impl ty::TyExpression { ReassignmentTarget::ElementAccess(path) => { let lhs_span = path.span.clone(); let mut expr = path; +// dbg!(&expr); let mut indices = Vec::new(); // Loop through the LHS "backwards" starting from the outermost expression // (the whole LHS) and moving towards the first identifier that must @@ -2366,7 +2382,9 @@ impl ty::TyExpression { ExpressionKind::Variable(name) => { // check that the reassigned name exists let unknown_decl = ctx.resolve_symbol(handler, &name)?; - +// if name.as_str() == "v2" { +// dbg!(&unknown_decl); +// } match unknown_decl { TyDecl::VariableDecl(variable_decl) => { if !variable_decl.mutability.is_mutable() { @@ -2377,8 +2395,7 @@ impl ty::TyExpression { }, )); } - - break (name, variable_decl.body.return_type); + break (name, variable_decl./*body.*/return_type); } TyDecl::ConstantDecl(constant_decl) => { let constant_decl = @@ -2457,6 +2474,7 @@ impl ty::TyExpression { }; let indices = indices.into_iter().rev().collect::>(); +// dbg!(&base_type); let (ty_of_field, _ty_of_parent) = ctx.namespace().current_module().read(engines, |m| { Self::find_subfield_type( @@ -2468,6 +2486,7 @@ impl ty::TyExpression { &indices, ) })?; +// dbg!(&ty_of_field); ( TyReassignmentTarget::ElementAccess { @@ -2480,6 +2499,9 @@ impl ty::TyExpression { } }; +// dbg!(&lhs); +// dbg!(&rhs); +// dbg!(&*type_engine.get(expected_rhs_type)); let ctx = ctx .with_type_annotation(expected_rhs_type) .with_help_text(""); @@ -2550,6 +2572,9 @@ impl ty::TyExpression { ResolvedDeclaration::Parsed(_) => unreachable!(), ResolvedDeclaration::Typed(ty_decl) => ty_decl.return_type(handler, engines)?, }; +// if base_name.as_str() == "v2" { +// dbg!(&*type_engine.get(symbol)); +// } let mut symbol_span = base_name.span(); let mut parent_rover = symbol; let mut full_span_for_error = base_name.span(); diff --git a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs index f35ee74514b..2840c8f62ac 100644 --- a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs +++ b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs @@ -711,8 +711,16 @@ impl Items { .symbols_unique_while_collecting_unifications .write() .insert(name.clone().into(), item.clone()); +// if name.as_str() == "v2" { +// dbg!(&name); +// dbg!(&item); +// } } +// if name.as_str() == "v2" { +// dbg!(&name); +// dbg!(&item); +// } module.current_items_mut().symbols.insert(name, item); Ok(()) diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index fb68d4e6ca6..b05264f13aa 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -74,7 +74,7 @@ pub struct TypeCheckContext<'a> { self_type: Option, /// While type-checking an expression, this indicates the types to be substituted when a /// type is resolved. This is required is to replace associated types, namely TypeInfo::TraitType. - type_subst: TypeSubstMap, + pub type_subst: TypeSubstMap, /// Whether or not we're within an `abi` implementation. /// /// This is `ImplAbiFn` while checking `abi` implementations whether at their original impl diff --git a/sway-core/src/semantic_analysis/type_resolve.rs b/sway-core/src/semantic_analysis/type_resolve.rs index 1b09dddd4ee..b21ad1f3a1e 100644 --- a/sway-core/src/semantic_analysis/type_resolve.rs +++ b/sway-core/src/semantic_analysis/type_resolve.rs @@ -180,9 +180,20 @@ pub fn resolve_type( _ => type_id, }; +// let current_mod_path = namespace.current_mod_path(); +// let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; + +// if problem { +// dbg!(&*type_engine.get(type_id)); +//// dbg!(&subst_ctx); +// } let mut type_id = type_id; type_id.subst(subst_ctx); +// if problem { +// dbg!(&*type_engine.get(type_id)); +// } + Ok(type_id) } diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 8c97dd475af..267b801711c 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -2089,6 +2089,8 @@ impl TypeEngine { handler.emit_err(err_override); } None => { +// dbg!("unify_helper error"); +// dbg!(std::backtrace::Backtrace::capture()); handler.emit_err(CompileError::TypeError(TypeError::MismatchedType { expected: engines.help_out(expected).to_string(), received: engines.help_out(received).to_string(), diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw index 2f1ff0d15dd..6f8c9c0aa2d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw @@ -1,87 +1,87 @@ script; -fn diverge_in_let_body() -> u64 { - let _x: bool = { - return 5; - }; - 123 -} - -struct Foo { - x: bool, - y: u32, -} - -fn diverge_in_struct_0() -> u64 { - let _foo: Foo = Foo { - x: { - return 5; - true - }, - y: 123, - }; - 123 -} - -fn diverge_in_struct_1() -> u64 { - let _foo: Foo = Foo { - x: true, - y: { - return 5; - 123 - }, - }; - 123 -} - -fn diverge_in_tuple_0() -> u64 { - let _tuple: (bool, u32) = ( - { - return 5; - }, - 123, - ); - 123 -} - -fn diverge_in_tuple_1() -> u64 { - let _tuple: (bool, u32) = ( - true, - { - return 5; - }, - ); - 123 -} - -fn diverge_in_array() -> u64 { - let _arr: [bool; 2] = [ { - return 5; - }; 2 ]; - 123 -} - -fn diverge_in_return() -> u64 { - return { - return 5; - 6 - }; -} - -fn diverge_in_if_condition() -> u64 { - let _b: bool = if { return 5; } { true } else { false }; - 123 -} - -fn diverge_in_if_then() -> u64 { - let _b: bool = if true { return 5; } else { false }; - 123 -} - -fn diverge_in_if_else() -> u64 { - let _b: bool = if false { true } else { return 5; }; - 123 -} +//fn diverge_in_let_body() -> u64 { +// let _x: bool = { +// return 5; +// }; +// 123 +//} +// +//struct Foo { +// x: bool, +// y: u32, +//} +// +//fn diverge_in_struct_0() -> u64 { +// let _foo: Foo = Foo { +// x: { +// return 5; +// true +// }, +// y: 123, +// }; +// 123 +//} +// +//fn diverge_in_struct_1() -> u64 { +// let _foo: Foo = Foo { +// x: true, +// y: { +// return 5; +// 123 +// }, +// }; +// 123 +//} +// +//fn diverge_in_tuple_0() -> u64 { +// let _tuple: (bool, u32) = ( +// { +// return 5; +// }, +// 123, +// ); +// 123 +//} +// +//fn diverge_in_tuple_1() -> u64 { +// let _tuple: (bool, u32) = ( +// true, +// { +// return 5; +// }, +// ); +// 123 +//} +// +//fn diverge_in_array() -> u64 { +// let _arr: [bool; 2] = [ { +// return 5; +// }; 2 ]; +// 123 +//} +// +//fn diverge_in_return() -> u64 { +// return { +// return 5; +// 6 +// }; +//} +// +//fn diverge_in_if_condition() -> u64 { +// let _b: bool = if { return 5; } { true } else { false }; +// 123 +//} +// +//fn diverge_in_if_then() -> u64 { +// let _b: bool = if true { return 5; } else { false }; +// 123 +//} +// +//fn diverge_in_if_else() -> u64 { +// let _b: bool = if false { true } else { return 5; }; +// 123 +//} fn diverge_in_match_condition() -> u64 { match { @@ -91,286 +91,286 @@ fn diverge_in_match_condition() -> u64 { } } -fn diverge_in_match_branch_0() -> u64 { - match true { - true => { - return 5; - }, - false => (), - }; - 123 -} - -fn diverge_in_match_branch_1() -> u64 { - match false { - true => (), - false => { - return 5; - }, - }; - 123 -} - -fn diverge_in_match_branch_2() -> u64 { - let _m:! = match false { - true => { - return 5; - }, - false => { - return 5; - }, - }; - 123 -} - - -fn diverge_in_while_condition() -> u64 { - while { - return 5; - } { } - 123 -} - -fn diverge_in_while_body() -> u64 { - while true { - return 5; - } - 123 -} - -fn func(arg: bool) -> u64 { - if arg { 123 } else { 456 } -} - -fn diverge_in_func_arg() -> u64 { - func( { - return 5; - }) -} - -fn diverge_in_array_index_index() -> u64 { - let arr: [bool; 2] = [true, false]; - let _b: bool = arr[ { - return 5; - }]; - 123 -} - -fn diverge_in_op_not() -> u64 { - let _b: bool = ! { - return 5; - }; - 123 -} - -fn diverge_in_op_add_rhs() -> u64 { - let _x: u32 = 1u32 + ( { - return 5; - 1u32 - }); - 123 -} - -fn diverge_in_logical_and_lhs() -> u64 { - let _b: bool = ( { - return 5; - true - }) && true; - 123 -} - -fn diverge_in_logical_and_rhs() -> u64 { - let _b: bool = true && ( { - return 5; - true - }); - 123 -} - -fn diverge_in_logical_or_lhs() -> u64 { - let _b: bool = ( { - return 5; - true - }) || true; - 123 -} - -fn diverge_in_logical_or_rhs() -> u64 { - let _b: bool = false || ( { - return 5; - true - }); - 123 -} - -fn diverge_in_reassignment() -> u64 { - let mut _b: bool = true; - _b = { - return 5; - }; - 123 -} - -fn diverge_with_if_else(b: bool) -> u64 { - let x: u64 = if b { - return 5; - } else { - return 1; - }; - - return x; -} - -fn diverge_in_eq() -> u64 { - let mut _b: bool = true; - _b = { - return 5; - } == { - return 6; - }; - 123 -} - -fn diverge_in_lt() -> u64 { - let mut _b: bool = true; - _b = { - return 5; - } < { - return 6; - }; - 123 -} - -fn diverge_in_gt() -> u64 { - let mut _b: bool = true; - _b = { - return 5; - } > { - return 6; - }; - 123 -} - -#[inline(never)] -fn diverge_in_if_with_std_revert(cond: bool) -> (u64, u64) { - let result1 = if cond == true { - revert(0) - } else { - 5 - }; - - let result2 = if cond == false { - 5 - } else { - revert(0) - }; - - (result1, result2) -} - -#[inline(never)] -fn diverge_in_if_with_revert_intrinsic(cond: bool) -> (u64, u64) { - let result1 = if cond == true { - __revert(0) - } else { - 5 - }; - - let result2 = if cond == false { - 5 - } else { - __revert(0) - }; - - (result1, result2) -} - -#[inline(never)] -fn diverge_in_match_with_std_revert(cond: bool) -> (u64, u64) { - let result1 = match cond { - true => revert(0), - false => 5, - }; - - let result2 = match cond { - false => 5, - true => revert(0), - }; - - (result1, result2) -} - -#[inline(never)] -fn diverge_in_match_with_revert_intrinsic(cond: bool) -> (u64, u64) { - let result1 = match cond { - true => __revert(0), - false => 5, - }; - - let result2 = match cond { - false => 5, - true => __revert(0), - }; - - (result1, result2) -} +//fn diverge_in_match_branch_0() -> u64 { +// match true { +// true => { +// return 5; +// }, +// false => (), +// }; +// 123 +//} +// +//fn diverge_in_match_branch_1() -> u64 { +// match false { +// true => (), +// false => { +// return 5; +// }, +// }; +// 123 +//} +// +//fn diverge_in_match_branch_2() -> u64 { +// let _m:! = match false { +// true => { +// return 5; +// }, +// false => { +// return 5; +// }, +// }; +// 123 +//} +// +// +//fn diverge_in_while_condition() -> u64 { +// while { +// return 5; +// } { } +// 123 +//} +// +//fn diverge_in_while_body() -> u64 { +// while true { +// return 5; +// } +// 123 +//} +// +//fn func(arg: bool) -> u64 { +// if arg { 123 } else { 456 } +//} +// +//fn diverge_in_func_arg() -> u64 { +// func( { +// return 5; +// }) +//} +// +//fn diverge_in_array_index_index() -> u64 { +// let arr: [bool; 2] = [true, false]; +// let _b: bool = arr[ { +// return 5; +// }]; +// 123 +//} +// +//fn diverge_in_op_not() -> u64 { +// let _b: bool = ! { +// return 5; +// }; +// 123 +//} +// +//fn diverge_in_op_add_rhs() -> u64 { +// let _x: u32 = 1u32 + ( { +// return 5; +// 1u32 +// }); +// 123 +//} +// +//fn diverge_in_logical_and_lhs() -> u64 { +// let _b: bool = ( { +// return 5; +// true +// }) && true; +// 123 +//} +// +//fn diverge_in_logical_and_rhs() -> u64 { +// let _b: bool = true && ( { +// return 5; +// true +// }); +// 123 +//} +// +//fn diverge_in_logical_or_lhs() -> u64 { +// let _b: bool = ( { +// return 5; +// true +// }) || true; +// 123 +//} +// +//fn diverge_in_logical_or_rhs() -> u64 { +// let _b: bool = false || ( { +// return 5; +// true +// }); +// 123 +//} +// +//fn diverge_in_reassignment() -> u64 { +// let mut _b: bool = true; +// _b = { +// return 5; +// }; +// 123 +//} +// +//fn diverge_with_if_else(b: bool) -> u64 { +// let x: u64 = if b { +// return 5; +// } else { +// return 1; +// }; +// +// return x; +//} +// +//fn diverge_in_eq() -> u64 { +// let mut _b: bool = true; +// _b = { +// return 5; +// } == { +// return 6; +// }; +// 123 +//} +// +//fn diverge_in_lt() -> u64 { +// let mut _b: bool = true; +// _b = { +// return 5; +// } < { +// return 6; +// }; +// 123 +//} +// +//fn diverge_in_gt() -> u64 { +// let mut _b: bool = true; +// _b = { +// return 5; +// } > { +// return 6; +// }; +// 123 +//} +// +//#[inline(never)] +//fn diverge_in_if_with_std_revert(cond: bool) -> (u64, u64) { +// let result1 = if cond == true { +// revert(0) +// } else { +// 5 +// }; +// +// let result2 = if cond == false { +// 5 +// } else { +// revert(0) +// }; +// +// (result1, result2) +//} +// +//#[inline(never)] +//fn diverge_in_if_with_revert_intrinsic(cond: bool) -> (u64, u64) { +// let result1 = if cond == true { +// __revert(0) +// } else { +// 5 +// }; +// +// let result2 = if cond == false { +// 5 +// } else { +// __revert(0) +// }; +// +// (result1, result2) +//} +// +//#[inline(never)] +//fn diverge_in_match_with_std_revert(cond: bool) -> (u64, u64) { +// let result1 = match cond { +// true => revert(0), +// false => 5, +// }; +// +// let result2 = match cond { +// false => 5, +// true => revert(0), +// }; +// +// (result1, result2) +//} +// +//#[inline(never)] +//fn diverge_in_match_with_revert_intrinsic(cond: bool) -> (u64, u64) { +// let result1 = match cond { +// true => __revert(0), +// false => 5, +// }; +// +// let result2 = match cond { +// false => 5, +// true => __revert(0), +// }; +// +// (result1, result2) +//} fn main() -> u64 { - assert(5 == diverge_in_let_body()); - assert(5 == diverge_in_struct_0()); - assert(5 == diverge_in_struct_1()); - assert(5 == diverge_in_tuple_0()); - assert(5 == diverge_in_tuple_1()); - assert(5 == diverge_in_array()); - assert(5 == diverge_in_return()); - assert(5 == diverge_in_if_condition()); - assert(5 == diverge_in_if_then()); - assert(5 == diverge_in_if_else()); - assert(5 == diverge_in_match_condition()); - assert(5 == diverge_in_match_branch_0()); - assert(5 == diverge_in_match_branch_1()); - assert(5 == diverge_in_match_branch_2()); - assert(5 == diverge_in_while_condition()); - assert(5 == diverge_in_while_body()); - assert(5 == diverge_in_func_arg()); - assert(5 == diverge_in_array_index_index()); - assert(5 == diverge_with_if_else(true)); - assert(1 == diverge_with_if_else(false)); - assert(5 == diverge_in_op_not()); - assert(5 == diverge_in_op_add_rhs()); - assert(5 == diverge_in_logical_and_lhs()); - assert(5 == diverge_in_logical_and_rhs()); - assert(5 == diverge_in_logical_or_lhs()); - assert(5 == diverge_in_logical_or_rhs()); - assert(5 == diverge_in_reassignment()); - assert(5 == diverge_in_eq()); - assert(5 == diverge_in_lt()); - assert(5 == diverge_in_gt()); - - let result = diverge_in_if_with_std_revert(false); - assert(result.0 == 5); - assert(result.1 == 5); - - let result = diverge_in_if_with_revert_intrinsic(false); - assert(result.0 == 5); - assert(result.1 == 5); - - let result = diverge_in_match_with_std_revert(false); - assert(result.0 == 5); - assert(result.1 == 5); - - let result = diverge_in_match_with_revert_intrinsic(false); - assert(result.0 == 5); - assert(result.1 == 5); - - // Test type coercion - if false { - let _: u8 = __revert(1); // Ok. Never -> u8. - let _: u8 = { return 123 }; // Ok. Never -> u8. - let _: ! = __revert(1); // Ok. Never -> Never. - let _: ! = { return 123 }; // Ok. Never -> Never. - } +// assert(5 == diverge_in_let_body()); +// assert(5 == diverge_in_struct_0()); +// assert(5 == diverge_in_struct_1()); +// assert(5 == diverge_in_tuple_0()); +// assert(5 == diverge_in_tuple_1()); +// assert(5 == diverge_in_array()); +// assert(5 == diverge_in_return()); +// assert(5 == diverge_in_if_condition()); +// assert(5 == diverge_in_if_then()); +// assert(5 == diverge_in_if_else()); +// assert(5 == diverge_in_match_condition()); +// assert(5 == diverge_in_match_branch_0()); +// assert(5 == diverge_in_match_branch_1()); +// assert(5 == diverge_in_match_branch_2()); +// assert(5 == diverge_in_while_condition()); +// assert(5 == diverge_in_while_body()); +// assert(5 == diverge_in_func_arg()); +// assert(5 == diverge_in_array_index_index()); +// assert(5 == diverge_with_if_else(true)); +// assert(1 == diverge_with_if_else(false)); +// assert(5 == diverge_in_op_not()); +// assert(5 == diverge_in_op_add_rhs()); +// assert(5 == diverge_in_logical_and_lhs()); +// assert(5 == diverge_in_logical_and_rhs()); +// assert(5 == diverge_in_logical_or_lhs()); +// assert(5 == diverge_in_logical_or_rhs()); +// assert(5 == diverge_in_reassignment()); +// assert(5 == diverge_in_eq()); +// assert(5 == diverge_in_lt()); +// assert(5 == diverge_in_gt()); +// +// let result = diverge_in_if_with_std_revert(false); +// assert(result.0 == 5); +// assert(result.1 == 5); +// +// let result = diverge_in_if_with_revert_intrinsic(false); +// assert(result.0 == 5); +// assert(result.1 == 5); +// +// let result = diverge_in_match_with_std_revert(false); +// assert(result.0 == 5); +// assert(result.1 == 5); +// +// let result = diverge_in_match_with_revert_intrinsic(false); +// assert(result.0 == 5); +// assert(result.1 == 5); +// +// // Test type coercion +// if false { +// let _: u8 = __revert(1); // Ok. Never -> u8. +// let _: u8 = { return 123 }; // Ok. Never -> u8. +// let _: ! = __revert(1); // Ok. Never -> Never. +// let _: ! = { return 123 }; // Ok. Never -> Never. +// } 42 } From 554c20ce36b051d3d327850b44f33344eba9b647 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 23:11:39 +0100 Subject: [PATCH 4/8] Code cleanup --- .../language/ty/declaration/declaration.rs | 2 +- .../semantic_analysis/ast_node/code_block.rs | 10 +- .../ast_node/declaration/declaration.rs | 2 - .../ast_node/declaration/variable.rs | 83 +- .../typed/typed_match_expression.rs | 7 - .../ast_node/expression/typed_expression.rs | 29 +- .../namespace/lexical_scope.rs | 8 - .../semantic_analysis/type_check_context.rs | 2 +- .../src/semantic_analysis/type_resolve.rs | 11 - sway-core/src/type_system/engine.rs | 2 - .../language/diverging_exprs/src/main.sw | 719 +++++++++--------- 11 files changed, 384 insertions(+), 491 deletions(-) diff --git a/sway-core/src/language/ty/declaration/declaration.rs b/sway-core/src/language/ty/declaration/declaration.rs index d5a36054940..8d7fd2f7438 100644 --- a/sway-core/src/language/ty/declaration/declaration.rs +++ b/sway-core/src/language/ty/declaration/declaration.rs @@ -734,7 +734,7 @@ impl TyDecl { let type_engine = engines.te(); let decl_engine = engines.de(); let type_id = match self { - TyDecl::VariableDecl(decl) => decl./*body.*/return_type, + TyDecl::VariableDecl(decl) => decl.return_type, TyDecl::FunctionDecl(FunctionDecl { decl_id, .. }) => { let decl = decl_engine.get_function(decl_id); decl.return_type.type_id diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index 0d30eb36ec7..76c24661452 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -108,12 +108,12 @@ impl ty::TyCodeBlock { }) .flatten(); let span = implicit_return_span.unwrap_or_else(|| code_block.whole_block_span.clone()); - + let block_type = code_block .contents .iter() .find_map(|node| { - match node { + match node { // If an ast node of the block returns, breaks, or continues then the whole block should have Never as return type. ty::TyAstNode { content: @@ -154,12 +154,6 @@ impl ty::TyCodeBlock { }) .unwrap_or_else(|| ctx.engines.te().id_of_unit()); - let current_mod_path = ctx.namespace.current_mod_path(); - let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - if problem { - dbg!(&ctx.engines.te().get(block_type)); - } - (block_type, span) } } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs index c115c9fc5c0..32f1d1cf63f 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/declaration.rs @@ -93,8 +93,6 @@ impl TyDecl { Ok(res) => res, Err(err) => return Ok(ty::TyDecl::ErrorRecovery(span, err)), }; -// dbg!(&var_decl.name); -// dbg!(&*type_engine.get(var_decl.return_type)); let typed_var_decl = ty::TyDecl::VariableDecl(Box::new(var_decl)); ctx.insert_symbol(handler, name, typed_var_decl.clone())?; typed_var_decl diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index 70d89300684..43b50ae6692 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -40,15 +40,6 @@ impl ty::TyVariableDecl { let mut type_ascription = var_decl.type_ascription.clone(); - let current_mod_path = ctx.namespace.current_mod_path(); - let problem = var_decl.name.as_str() == "__matched_value_1" && - current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - - if problem { - dbg!(type_ascription.type_id); - dbg!(&*type_engine.get(type_ascription.type_id)); - } - type_ascription.type_id = ctx .resolve_type( handler, @@ -59,13 +50,6 @@ impl ty::TyVariableDecl { ) .unwrap_or_else(|err| type_engine.id_of_error_recovery(err)); - if problem { -// dbg!(&body); -// dbg!(&*type_engine.get(body.return_type)); - dbg!(type_ascription.type_id); - dbg!(&*type_engine.get(type_ascription.type_id)); - } - let mut ctx = ctx .with_type_annotation(type_ascription.type_id) .with_help_text( @@ -73,54 +57,27 @@ impl ty::TyVariableDecl { with the assigned expression's type.", ); - if problem { -// dbg!(&body); -// dbg!(&*type_engine.get(body.return_type)); - dbg!(type_ascription.type_id); - dbg!(&*type_engine.get(type_ascription.type_id)); - } - -// if problem { -//// dbg!(std::backtrace::Backtrace::force_capture()); -// dbg!(&var_decl.body); -// } - - let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); + let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); let body = result .unwrap_or_else(|err| ty::TyExpression::error(err, var_decl.name.span(), engines)); - typechecking of the body changes what type_ascription.type_id refers to - it changes from Unknown to Boolean. - This is almost certainly to do with the unification stuff that happens at the end of ast_node/code_block.rs::type_check. - An obvious guess is that the type of the code block is determined as the type of the implicit return rather than as (Never if a diverging expression exists, or else the implicit return). See TyCodeBloc::compute_return_type_and_span. - - if problem { -// dbg!(&body); - dbg!(&*type_engine.get(body.return_type)); - dbg!(type_ascription.type_id); - dbg!(&*type_engine.get(type_ascription.type_id)); - } -// dbg!(&var_decl.name); -// dbg!(&type_ascription.type_id); -// dbg!(&*type_engine.get(type_ascription.type_id)); -// dbg!(&body.return_type); -// dbg!(&*type_engine.get(body.return_type)); - // Determine the type of the variable going forward. Typically this is the type of the RHS, - // but in some cases we need to use the type ascription instead. - // TODO: We should not have these special cases. The typecheck expressions should be written - // in a way to always use the context provided by the LHS, and use the unified type of - // LHS and RHS as the return type of the RHS. Remove this special case as a part of - // the initiative of improving type inference. + // Determine the type of the variable going forward. Typically this is the type of the RHS, + // but in some cases we need to use the type ascription instead. + // TODO: We should not have these special cases. The typecheck expressions should be written + // in a way to always use the context provided by the LHS, and use the unified type of LHS + // and RHS as the return type of the RHS. Remove this special case as a part of the + // initiative of improving type inference. let return_type = - match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { - // Integers: We can't rely on the type of the RHS to give us the correct integer - // type, so the type of the variable *has* to follow `type_ascription` if - // `type_ascription` is a concrete integer type that does not conflict with the type - // of `body` (i.e. passes the type checking above). - (TypeInfo::UnsignedInteger(_), _) | - // Never: If the RHS resolves to Never, then any code following the declaration is - // unreachable. If the variable is used later on, then it should be treated as - // having the same type as the type ascription. - (_, TypeInfo::Never) | + match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { + // Integers: We can't rely on the type of the RHS to give us the correct integer + // type, so the type of the variable *has* to follow `type_ascription` if + // `type_ascription` is a concrete integer type that does not conflict with the type + // of `body` (i.e. passes the type checking above). + (TypeInfo::UnsignedInteger(_), _) | + // Never: If the RHS resolves to Never, then any code following the declaration is + // unreachable. If the variable is used later on, then it should be treated as + // having the same type as the type ascription. + (_, TypeInfo::Never) | // If RHS type check ends up in an error we want to use the // provided type ascription as the variable type. E.g.: // let v: Struct = Struct { x: 0 }; // `v` should be "Struct". @@ -128,13 +85,11 @@ impl ty::TyVariableDecl { // let v = ; // `v` will remain "{unknown}". // TODO: Refine and improve this further. E.g., // let v: Struct { /* MISSING FIELDS */ }; // Despite the error, `v` should be of type "Struct". - (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, - // In all other cases we use the type of the RHS. + (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, + // In all other cases we use the type of the RHS. _ => body.return_type, }; -// dbg!(&*type_engine.get(return_type)); - if !ctx.code_block_first_pass() { let previous_symbol = ctx .namespace() diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs index 01632a5ef62..fdb5edfdd8c 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs @@ -88,13 +88,6 @@ impl ty::TyMatchExpression { span, }; - let current_mod_path = ctx.namespace.current_mod_path(); - let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - if problem { - dbg!(&ctx.engines.te().get(typed_exp.value_type_id)); - } - - Ok((typed_exp, typed_scrutinees)) } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 92df58a2c92..78beb52d72c 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -787,12 +787,6 @@ impl ty::TyExpression { Err(_err) => (ty::TyCodeBlock::default(), type_engine.id_of_unit()), }; - let current_mod_path = ctx.namespace.current_mod_path(); - let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - if problem { - dbg!(&ctx.engines.te().get(block_return_type)); - } - let exp = ty::TyExpression { expression: ty::TyExpressionVariant::CodeBlock(typed_block), return_type: block_return_type, @@ -886,15 +880,6 @@ impl ty::TyExpression { }; let type_id = typed_value.return_type; - let current_mod_path = ctx.namespace.current_mod_path(); - let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - if problem { - dbg!(&value); - dbg!(&typed_value); - dbg!(&ctx.engines.te().get(type_id)); - } - - // check to make sure that the type of the value is something that can be matched upon type_engine .get(type_id) @@ -2372,7 +2357,6 @@ impl ty::TyExpression { ReassignmentTarget::ElementAccess(path) => { let lhs_span = path.span.clone(); let mut expr = path; -// dbg!(&expr); let mut indices = Vec::new(); // Loop through the LHS "backwards" starting from the outermost expression // (the whole LHS) and moving towards the first identifier that must @@ -2382,9 +2366,6 @@ impl ty::TyExpression { ExpressionKind::Variable(name) => { // check that the reassigned name exists let unknown_decl = ctx.resolve_symbol(handler, &name)?; -// if name.as_str() == "v2" { -// dbg!(&unknown_decl); -// } match unknown_decl { TyDecl::VariableDecl(variable_decl) => { if !variable_decl.mutability.is_mutable() { @@ -2395,7 +2376,7 @@ impl ty::TyExpression { }, )); } - break (name, variable_decl./*body.*/return_type); + break (name, variable_decl.return_type); } TyDecl::ConstantDecl(constant_decl) => { let constant_decl = @@ -2474,7 +2455,6 @@ impl ty::TyExpression { }; let indices = indices.into_iter().rev().collect::>(); -// dbg!(&base_type); let (ty_of_field, _ty_of_parent) = ctx.namespace().current_module().read(engines, |m| { Self::find_subfield_type( @@ -2486,7 +2466,6 @@ impl ty::TyExpression { &indices, ) })?; -// dbg!(&ty_of_field); ( TyReassignmentTarget::ElementAccess { @@ -2499,9 +2478,6 @@ impl ty::TyExpression { } }; -// dbg!(&lhs); -// dbg!(&rhs); -// dbg!(&*type_engine.get(expected_rhs_type)); let ctx = ctx .with_type_annotation(expected_rhs_type) .with_help_text(""); @@ -2572,9 +2548,6 @@ impl ty::TyExpression { ResolvedDeclaration::Parsed(_) => unreachable!(), ResolvedDeclaration::Typed(ty_decl) => ty_decl.return_type(handler, engines)?, }; -// if base_name.as_str() == "v2" { -// dbg!(&*type_engine.get(symbol)); -// } let mut symbol_span = base_name.span(); let mut parent_rover = symbol; let mut full_span_for_error = base_name.span(); diff --git a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs index 2840c8f62ac..f35ee74514b 100644 --- a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs +++ b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs @@ -711,16 +711,8 @@ impl Items { .symbols_unique_while_collecting_unifications .write() .insert(name.clone().into(), item.clone()); -// if name.as_str() == "v2" { -// dbg!(&name); -// dbg!(&item); -// } } -// if name.as_str() == "v2" { -// dbg!(&name); -// dbg!(&item); -// } module.current_items_mut().symbols.insert(name, item); Ok(()) diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index b05264f13aa..fb68d4e6ca6 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -74,7 +74,7 @@ pub struct TypeCheckContext<'a> { self_type: Option, /// While type-checking an expression, this indicates the types to be substituted when a /// type is resolved. This is required is to replace associated types, namely TypeInfo::TraitType. - pub type_subst: TypeSubstMap, + type_subst: TypeSubstMap, /// Whether or not we're within an `abi` implementation. /// /// This is `ImplAbiFn` while checking `abi` implementations whether at their original impl diff --git a/sway-core/src/semantic_analysis/type_resolve.rs b/sway-core/src/semantic_analysis/type_resolve.rs index b21ad1f3a1e..1b09dddd4ee 100644 --- a/sway-core/src/semantic_analysis/type_resolve.rs +++ b/sway-core/src/semantic_analysis/type_resolve.rs @@ -180,20 +180,9 @@ pub fn resolve_type( _ => type_id, }; -// let current_mod_path = namespace.current_mod_path(); -// let problem = current_mod_path.len() == 1 && current_mod_path[0].as_str() == "diverging_exprs"; - -// if problem { -// dbg!(&*type_engine.get(type_id)); -//// dbg!(&subst_ctx); -// } let mut type_id = type_id; type_id.subst(subst_ctx); -// if problem { -// dbg!(&*type_engine.get(type_id)); -// } - Ok(type_id) } diff --git a/sway-core/src/type_system/engine.rs b/sway-core/src/type_system/engine.rs index 267b801711c..8c97dd475af 100644 --- a/sway-core/src/type_system/engine.rs +++ b/sway-core/src/type_system/engine.rs @@ -2089,8 +2089,6 @@ impl TypeEngine { handler.emit_err(err_override); } None => { -// dbg!("unify_helper error"); -// dbg!(std::backtrace::Backtrace::capture()); handler.emit_err(CompileError::TypeError(TypeError::MismatchedType { expected: engines.help_out(expected).to_string(), received: engines.help_out(received).to_string(), diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw index 6f8c9c0aa2d..32bf9b1f47e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw @@ -1,376 +1,377 @@ script; -//fn diverge_in_let_body() -> u64 { -// let _x: bool = { -// return 5; -// }; -// 123 -//} -// -//struct Foo { -// x: bool, -// y: u32, -//} -// -//fn diverge_in_struct_0() -> u64 { -// let _foo: Foo = Foo { -// x: { -// return 5; -// true -// }, -// y: 123, -// }; -// 123 -//} -// -//fn diverge_in_struct_1() -> u64 { -// let _foo: Foo = Foo { -// x: true, -// y: { -// return 5; -// 123 -// }, -// }; -// 123 -//} -// -//fn diverge_in_tuple_0() -> u64 { -// let _tuple: (bool, u32) = ( -// { -// return 5; -// }, -// 123, -// ); -// 123 -//} -// -//fn diverge_in_tuple_1() -> u64 { -// let _tuple: (bool, u32) = ( -// true, -// { -// return 5; -// }, -// ); -// 123 -//} -// -//fn diverge_in_array() -> u64 { -// let _arr: [bool; 2] = [ { -// return 5; -// }; 2 ]; -// 123 -//} -// -//fn diverge_in_return() -> u64 { -// return { -// return 5; -// 6 -// }; -//} -// -//fn diverge_in_if_condition() -> u64 { -// let _b: bool = if { return 5; } { true } else { false }; -// 123 -//} -// -//fn diverge_in_if_then() -> u64 { -// let _b: bool = if true { return 5; } else { false }; -// 123 -//} -// -//fn diverge_in_if_else() -> u64 { -// let _b: bool = if false { true } else { return 5; }; -// 123 -//} +fn diverge_in_let_body() -> u64 { + let _x: bool = { + return 5; + }; + 123 +} + +struct Foo { + x: bool, + y: u32, +} + +fn diverge_in_struct_0() -> u64 { + let _foo: Foo = Foo { + x: { + return 5; + true + }, + y: 123, + }; + 123 +} + +fn diverge_in_struct_1() -> u64 { + let _foo: Foo = Foo { + x: true, + y: { + return 5; + 123 + }, + }; + 123 +} + +fn diverge_in_tuple_0() -> u64 { + let _tuple: (bool, u32) = ( + { + return 5; + }, + 123, + ); + 123 +} + +fn diverge_in_tuple_1() -> u64 { + let _tuple: (bool, u32) = ( + true, + { + return 5; + }, + ); + 123 +} + +fn diverge_in_array() -> u64 { + let _arr: [bool; 2] = [ { + return 5; + }; 2 ]; + 123 +} + +fn diverge_in_return() -> u64 { + return { + return 5; + 6 + }; +} + +fn diverge_in_if_condition() -> u64 { + let _b: bool = if { return 5; } { true } else { false }; + 123 +} + +fn diverge_in_if_then() -> u64 { + let _b: bool = if true { return 5; } else { false }; + 123 +} + +fn diverge_in_if_else() -> u64 { + let _b: bool = if false { true } else { return 5; }; + 123 +} fn diverge_in_match_condition() -> u64 { match { return 5; true } { + _ => 42, + } +} + +fn diverge_in_match_branch_0() -> u64 { + match true { + true => { + return 5; + }, + false => (), + }; + 123 +} + +fn diverge_in_match_branch_1() -> u64 { + match false { + true => (), + false => { + return 5; + }, + }; + 123 +} + +fn diverge_in_match_branch_2() -> u64 { + let _m:! = match false { + true => { + return 5; + }, + false => { + return 5; + }, + }; + 123 +} + + +fn diverge_in_while_condition() -> u64 { + while { + return 5; + } { } + 123 +} + +fn diverge_in_while_body() -> u64 { + while true { + return 5; } + 123 +} + +fn func(arg: bool) -> u64 { + if arg { 123 } else { 456 } +} + +fn diverge_in_func_arg() -> u64 { + func( { + return 5; + }) +} + +fn diverge_in_array_index_index() -> u64 { + let arr: [bool; 2] = [true, false]; + let _b: bool = arr[ { + return 5; + }]; + 123 +} + +fn diverge_in_op_not() -> u64 { + let _b: bool = ! { + return 5; + }; + 123 +} + +fn diverge_in_op_add_rhs() -> u64 { + let _x: u32 = 1u32 + ( { + return 5; + 1u32 + }); + 123 +} + +fn diverge_in_logical_and_lhs() -> u64 { + let _b: bool = ( { + return 5; + true + }) && true; + 123 +} + +fn diverge_in_logical_and_rhs() -> u64 { + let _b: bool = true && ( { + return 5; + true + }); + 123 +} + +fn diverge_in_logical_or_lhs() -> u64 { + let _b: bool = ( { + return 5; + true + }) || true; + 123 +} + +fn diverge_in_logical_or_rhs() -> u64 { + let _b: bool = false || ( { + return 5; + true + }); + 123 +} + +fn diverge_in_reassignment() -> u64 { + let mut _b: bool = true; + _b = { + return 5; + }; + 123 +} + +fn diverge_with_if_else(b: bool) -> u64 { + let x: u64 = if b { + return 5; + } else { + return 1; + }; + + return x; +} + +fn diverge_in_eq() -> u64 { + let mut _b: bool = true; + _b = { + return 5; + } == { + return 6; + }; + 123 +} + +fn diverge_in_lt() -> u64 { + let mut _b: bool = true; + _b = { + return 5; + } < { + return 6; + }; + 123 +} + +fn diverge_in_gt() -> u64 { + let mut _b: bool = true; + _b = { + return 5; + } > { + return 6; + }; + 123 +} + +#[inline(never)] +fn diverge_in_if_with_std_revert(cond: bool) -> (u64, u64) { + let result1 = if cond == true { + revert(0) + } else { + 5 + }; + + let result2 = if cond == false { + 5 + } else { + revert(0) + }; + + (result1, result2) +} + +#[inline(never)] +fn diverge_in_if_with_revert_intrinsic(cond: bool) -> (u64, u64) { + let result1 = if cond == true { + __revert(0) + } else { + 5 + }; + + let result2 = if cond == false { + 5 + } else { + __revert(0) + }; + + (result1, result2) } -//fn diverge_in_match_branch_0() -> u64 { -// match true { -// true => { -// return 5; -// }, -// false => (), -// }; -// 123 -//} -// -//fn diverge_in_match_branch_1() -> u64 { -// match false { -// true => (), -// false => { -// return 5; -// }, -// }; -// 123 -//} -// -//fn diverge_in_match_branch_2() -> u64 { -// let _m:! = match false { -// true => { -// return 5; -// }, -// false => { -// return 5; -// }, -// }; -// 123 -//} -// -// -//fn diverge_in_while_condition() -> u64 { -// while { -// return 5; -// } { } -// 123 -//} -// -//fn diverge_in_while_body() -> u64 { -// while true { -// return 5; -// } -// 123 -//} -// -//fn func(arg: bool) -> u64 { -// if arg { 123 } else { 456 } -//} -// -//fn diverge_in_func_arg() -> u64 { -// func( { -// return 5; -// }) -//} -// -//fn diverge_in_array_index_index() -> u64 { -// let arr: [bool; 2] = [true, false]; -// let _b: bool = arr[ { -// return 5; -// }]; -// 123 -//} -// -//fn diverge_in_op_not() -> u64 { -// let _b: bool = ! { -// return 5; -// }; -// 123 -//} -// -//fn diverge_in_op_add_rhs() -> u64 { -// let _x: u32 = 1u32 + ( { -// return 5; -// 1u32 -// }); -// 123 -//} -// -//fn diverge_in_logical_and_lhs() -> u64 { -// let _b: bool = ( { -// return 5; -// true -// }) && true; -// 123 -//} -// -//fn diverge_in_logical_and_rhs() -> u64 { -// let _b: bool = true && ( { -// return 5; -// true -// }); -// 123 -//} -// -//fn diverge_in_logical_or_lhs() -> u64 { -// let _b: bool = ( { -// return 5; -// true -// }) || true; -// 123 -//} -// -//fn diverge_in_logical_or_rhs() -> u64 { -// let _b: bool = false || ( { -// return 5; -// true -// }); -// 123 -//} -// -//fn diverge_in_reassignment() -> u64 { -// let mut _b: bool = true; -// _b = { -// return 5; -// }; -// 123 -//} -// -//fn diverge_with_if_else(b: bool) -> u64 { -// let x: u64 = if b { -// return 5; -// } else { -// return 1; -// }; -// -// return x; -//} -// -//fn diverge_in_eq() -> u64 { -// let mut _b: bool = true; -// _b = { -// return 5; -// } == { -// return 6; -// }; -// 123 -//} -// -//fn diverge_in_lt() -> u64 { -// let mut _b: bool = true; -// _b = { -// return 5; -// } < { -// return 6; -// }; -// 123 -//} -// -//fn diverge_in_gt() -> u64 { -// let mut _b: bool = true; -// _b = { -// return 5; -// } > { -// return 6; -// }; -// 123 -//} -// -//#[inline(never)] -//fn diverge_in_if_with_std_revert(cond: bool) -> (u64, u64) { -// let result1 = if cond == true { -// revert(0) -// } else { -// 5 -// }; -// -// let result2 = if cond == false { -// 5 -// } else { -// revert(0) -// }; -// -// (result1, result2) -//} -// -//#[inline(never)] -//fn diverge_in_if_with_revert_intrinsic(cond: bool) -> (u64, u64) { -// let result1 = if cond == true { -// __revert(0) -// } else { -// 5 -// }; -// -// let result2 = if cond == false { -// 5 -// } else { -// __revert(0) -// }; -// -// (result1, result2) -//} -// -//#[inline(never)] -//fn diverge_in_match_with_std_revert(cond: bool) -> (u64, u64) { -// let result1 = match cond { -// true => revert(0), -// false => 5, -// }; -// -// let result2 = match cond { -// false => 5, -// true => revert(0), -// }; -// -// (result1, result2) -//} -// -//#[inline(never)] -//fn diverge_in_match_with_revert_intrinsic(cond: bool) -> (u64, u64) { -// let result1 = match cond { -// true => __revert(0), -// false => 5, -// }; -// -// let result2 = match cond { -// false => 5, -// true => __revert(0), -// }; -// -// (result1, result2) -//} +#[inline(never)] +fn diverge_in_match_with_std_revert(cond: bool) -> (u64, u64) { + let result1 = match cond { + true => revert(0), + false => 5, + }; + + let result2 = match cond { + false => 5, + true => revert(0), + }; + + (result1, result2) +} + +#[inline(never)] +fn diverge_in_match_with_revert_intrinsic(cond: bool) -> (u64, u64) { + let result1 = match cond { + true => __revert(0), + false => 5, + }; + + let result2 = match cond { + false => 5, + true => __revert(0), + }; + + (result1, result2) +} fn main() -> u64 { -// assert(5 == diverge_in_let_body()); -// assert(5 == diverge_in_struct_0()); -// assert(5 == diverge_in_struct_1()); -// assert(5 == diverge_in_tuple_0()); -// assert(5 == diverge_in_tuple_1()); -// assert(5 == diverge_in_array()); -// assert(5 == diverge_in_return()); -// assert(5 == diverge_in_if_condition()); -// assert(5 == diverge_in_if_then()); -// assert(5 == diverge_in_if_else()); -// assert(5 == diverge_in_match_condition()); -// assert(5 == diverge_in_match_branch_0()); -// assert(5 == diverge_in_match_branch_1()); -// assert(5 == diverge_in_match_branch_2()); -// assert(5 == diverge_in_while_condition()); -// assert(5 == diverge_in_while_body()); -// assert(5 == diverge_in_func_arg()); -// assert(5 == diverge_in_array_index_index()); -// assert(5 == diverge_with_if_else(true)); -// assert(1 == diverge_with_if_else(false)); -// assert(5 == diverge_in_op_not()); -// assert(5 == diverge_in_op_add_rhs()); -// assert(5 == diverge_in_logical_and_lhs()); -// assert(5 == diverge_in_logical_and_rhs()); -// assert(5 == diverge_in_logical_or_lhs()); -// assert(5 == diverge_in_logical_or_rhs()); -// assert(5 == diverge_in_reassignment()); -// assert(5 == diverge_in_eq()); -// assert(5 == diverge_in_lt()); -// assert(5 == diverge_in_gt()); -// -// let result = diverge_in_if_with_std_revert(false); -// assert(result.0 == 5); -// assert(result.1 == 5); -// -// let result = diverge_in_if_with_revert_intrinsic(false); -// assert(result.0 == 5); -// assert(result.1 == 5); -// -// let result = diverge_in_match_with_std_revert(false); -// assert(result.0 == 5); -// assert(result.1 == 5); -// -// let result = diverge_in_match_with_revert_intrinsic(false); -// assert(result.0 == 5); -// assert(result.1 == 5); -// -// // Test type coercion -// if false { -// let _: u8 = __revert(1); // Ok. Never -> u8. -// let _: u8 = { return 123 }; // Ok. Never -> u8. -// let _: ! = __revert(1); // Ok. Never -> Never. -// let _: ! = { return 123 }; // Ok. Never -> Never. -// } + assert(5 == diverge_in_let_body()); + assert(5 == diverge_in_struct_0()); + assert(5 == diverge_in_struct_1()); + assert(5 == diverge_in_tuple_0()); + assert(5 == diverge_in_tuple_1()); + assert(5 == diverge_in_array()); + assert(5 == diverge_in_return()); + assert(5 == diverge_in_if_condition()); + assert(5 == diverge_in_if_then()); + assert(5 == diverge_in_if_else()); + assert(5 == diverge_in_match_condition()); + assert(5 == diverge_in_match_branch_0()); + assert(5 == diverge_in_match_branch_1()); + assert(5 == diverge_in_match_branch_2()); + assert(5 == diverge_in_while_condition()); + assert(5 == diverge_in_while_body()); + assert(5 == diverge_in_func_arg()); + assert(5 == diverge_in_array_index_index()); + assert(5 == diverge_with_if_else(true)); + assert(1 == diverge_with_if_else(false)); + assert(5 == diverge_in_op_not()); + assert(5 == diverge_in_op_add_rhs()); + assert(5 == diverge_in_logical_and_lhs()); + assert(5 == diverge_in_logical_and_rhs()); + assert(5 == diverge_in_logical_or_lhs()); + assert(5 == diverge_in_logical_or_rhs()); + assert(5 == diverge_in_reassignment()); + assert(5 == diverge_in_eq()); + assert(5 == diverge_in_lt()); + assert(5 == diverge_in_gt()); + + let result = diverge_in_if_with_std_revert(false); + assert(result.0 == 5); + assert(result.1 == 5); + + let result = diverge_in_if_with_revert_intrinsic(false); + assert(result.0 == 5); + assert(result.1 == 5); + + let result = diverge_in_match_with_std_revert(false); + assert(result.0 == 5); + assert(result.1 == 5); + + let result = diverge_in_match_with_revert_intrinsic(false); + assert(result.0 == 5); + assert(result.1 == 5); + + // Test type coercion + if false { + let _: u8 = __revert(1); // Ok. Never -> u8. + let _: u8 = { return 123 }; // Ok. Never -> u8. + let _: ! = __revert(1); // Ok. Never -> Never. + let _: ! = { return 123 }; // Ok. Never -> Never. + } 42 } From 016a94218b79c2141684a71760cf0eb02740ac4d Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 23:16:32 +0100 Subject: [PATCH 5/8] Tab issues --- .../semantic_analysis/ast_node/code_block.rs | 2 +- .../ast_node/declaration/variable.rs | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index 76c24661452..dfc37050faa 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -108,7 +108,7 @@ impl ty::TyCodeBlock { }) .flatten(); let span = implicit_return_span.unwrap_or_else(|| code_block.whole_block_span.clone()); - + let block_type = code_block .contents .iter() diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index 43b50ae6692..fa75c4da9d3 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -54,30 +54,30 @@ impl ty::TyVariableDecl { .with_type_annotation(type_ascription.type_id) .with_help_text( "Variable declaration's type annotation does not match up \ - with the assigned expression's type.", + with the assigned expression's type.", ); - let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); + let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); let body = result .unwrap_or_else(|err| ty::TyExpression::error(err, var_decl.name.span(), engines)); - // Determine the type of the variable going forward. Typically this is the type of the RHS, - // but in some cases we need to use the type ascription instead. - // TODO: We should not have these special cases. The typecheck expressions should be written - // in a way to always use the context provided by the LHS, and use the unified type of LHS - // and RHS as the return type of the RHS. Remove this special case as a part of the - // initiative of improving type inference. + // Determine the type of the variable going forward. Typically this is the type of the RHS, + // but in some cases we need to use the type ascription instead. + // TODO: We should not have these special cases. The typecheck expressions should be written + // in a way to always use the context provided by the LHS, and use the unified type of LHS + // and RHS as the return type of the RHS. Remove this special case as a part of the + // initiative of improving type inference. let return_type = - match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { - // Integers: We can't rely on the type of the RHS to give us the correct integer - // type, so the type of the variable *has* to follow `type_ascription` if - // `type_ascription` is a concrete integer type that does not conflict with the type - // of `body` (i.e. passes the type checking above). - (TypeInfo::UnsignedInteger(_), _) | - // Never: If the RHS resolves to Never, then any code following the declaration is - // unreachable. If the variable is used later on, then it should be treated as - // having the same type as the type ascription. - (_, TypeInfo::Never) | + match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { + // Integers: We can't rely on the type of the RHS to give us the correct integer + // type, so the type of the variable *has* to follow `type_ascription` if + // `type_ascription` is a concrete integer type that does not conflict with the type + // of `body` (i.e. passes the type checking above). + (TypeInfo::UnsignedInteger(_), _) | + // Never: If the RHS resolves to Never, then any code following the declaration is + // unreachable. If the variable is used later on, then it should be treated as + // having the same type as the type ascription. + (_, TypeInfo::Never) | // If RHS type check ends up in an error we want to use the // provided type ascription as the variable type. E.g.: // let v: Struct = Struct { x: 0 }; // `v` should be "Struct". @@ -85,8 +85,8 @@ impl ty::TyVariableDecl { // let v = ; // `v` will remain "{unknown}". // TODO: Refine and improve this further. E.g., // let v: Struct { /* MISSING FIELDS */ }; // Despite the error, `v` should be of type "Struct". - (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, - // In all other cases we use the type of the RHS. + (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, + // In all other cases we use the type of the RHS. _ => body.return_type, }; From 72cde358eafd14272f4fb6ab2cf9d2b463ce7f76 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 23:19:47 +0100 Subject: [PATCH 6/8] Whitespace --- .../semantic_analysis/ast_node/expression/typed_expression.rs | 2 ++ .../should_pass/language/diverging_exprs/src/main.sw | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 7a017e3f428..87ce6670b2d 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -2363,6 +2363,7 @@ impl ty::TyExpression { ExpressionKind::Variable(name) => { // check that the reassigned name exists let unknown_decl = ctx.resolve_symbol(handler, &name)?; + match unknown_decl { TyDecl::VariableDecl(variable_decl) => { if !variable_decl.mutability.is_mutable() { @@ -2373,6 +2374,7 @@ impl ty::TyExpression { }, )); } + break (name, variable_decl.return_type); } TyDecl::ConstantDecl(constant_decl) => { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw index 32bf9b1f47e..58844a67cd2 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/diverging_exprs/src/main.sw @@ -368,9 +368,9 @@ fn main() -> u64 { // Test type coercion if false { let _: u8 = __revert(1); // Ok. Never -> u8. - let _: u8 = { return 123 }; // Ok. Never -> u8. + let _: u8 = { return 123 }; // Ok. Never -> u8. let _: ! = __revert(1); // Ok. Never -> Never. - let _: ! = { return 123 }; // Ok. Never -> Never. + let _: ! = { return 123 }; // Ok. Never -> Never. } 42 From 5c02191b7e6f1b2428467ff34a4ba78f1d21b4a3 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Mon, 10 Feb 2025 23:35:34 +0100 Subject: [PATCH 7/8] clippy, fmt --- .../ast_node/declaration/variable.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index fa75c4da9d3..cfdbe209609 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -57,18 +57,17 @@ impl ty::TyVariableDecl { with the assigned expression's type.", ); - let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); + let result = ty::TyExpression::type_check(handler, ctx.by_ref(), &var_decl.body); let body = result .unwrap_or_else(|err| ty::TyExpression::error(err, var_decl.name.span(), engines)); - // Determine the type of the variable going forward. Typically this is the type of the RHS, - // but in some cases we need to use the type ascription instead. - // TODO: We should not have these special cases. The typecheck expressions should be written - // in a way to always use the context provided by the LHS, and use the unified type of LHS - // and RHS as the return type of the RHS. Remove this special case as a part of the - // initiative of improving type inference. - let return_type = - match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { + // Determine the type of the variable going forward. Typically this is the type of the RHS, + // but in some cases we need to use the type ascription instead. + // TODO: We should not have these special cases. The typecheck expressions should be written + // in a way to always use the context provided by the LHS, and use the unified type of LHS + // and RHS as the return type of the RHS. Remove this special case as a part of the + // initiative of improving type inference. + let return_type = match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { // Integers: We can't rely on the type of the RHS to give us the correct integer // type, so the type of the variable *has* to follow `type_ascription` if // `type_ascription` is a concrete integer type that does not conflict with the type From eadf708c7530b7367da32b63c7af3e0bbedeb5e7 Mon Sep 17 00:00:00 2001 From: Jacob Johannsen Date: Wed, 12 Feb 2025 21:37:01 +0100 Subject: [PATCH 8/8] Untabify --- .../ast_node/declaration/variable.rs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index cfdbe209609..c194dc6b7ca 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -68,26 +68,26 @@ impl ty::TyVariableDecl { // and RHS as the return type of the RHS. Remove this special case as a part of the // initiative of improving type inference. let return_type = match (&*type_engine.get(type_ascription.type_id), &*type_engine.get(body.return_type)) { - // Integers: We can't rely on the type of the RHS to give us the correct integer - // type, so the type of the variable *has* to follow `type_ascription` if - // `type_ascription` is a concrete integer type that does not conflict with the type - // of `body` (i.e. passes the type checking above). - (TypeInfo::UnsignedInteger(_), _) | - // Never: If the RHS resolves to Never, then any code following the declaration is - // unreachable. If the variable is used later on, then it should be treated as - // having the same type as the type ascription. - (_, TypeInfo::Never) | - // If RHS type check ends up in an error we want to use the - // provided type ascription as the variable type. E.g.: - // let v: Struct = Struct { x: 0 }; // `v` should be "Struct". - // let v: ExistingType = non_existing_identifier; // `v` should be "ExistingType". - // let v = ; // `v` will remain "{unknown}". - // TODO: Refine and improve this further. E.g., - // let v: Struct { /* MISSING FIELDS */ }; // Despite the error, `v` should be of type "Struct". - (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, - // In all other cases we use the type of the RHS. - _ => body.return_type, - }; + // Integers: We can't rely on the type of the RHS to give us the correct integer + // type, so the type of the variable *has* to follow `type_ascription` if + // `type_ascription` is a concrete integer type that does not conflict with the type + // of `body` (i.e. passes the type checking above). + (TypeInfo::UnsignedInteger(_), _) | + // Never: If the RHS resolves to Never, then any code following the declaration is + // unreachable. If the variable is used later on, then it should be treated as + // having the same type as the type ascription. + (_, TypeInfo::Never) | + // If RHS type check ends up in an error we want to use the + // provided type ascription as the variable type. E.g.: + // let v: Struct = Struct { x: 0 }; // `v` should be "Struct". + // let v: ExistingType = non_existing_identifier; // `v` should be "ExistingType". + // let v = ; // `v` will remain "{unknown}". + // TODO: Refine and improve this further. E.g., + // let v: Struct { /* MISSING FIELDS */ }; // Despite the error, `v` should be of type "Struct". + (_, TypeInfo::ErrorRecovery(_)) => type_ascription.type_id, + // In all other cases we use the type of the RHS. + _ => body.return_type, + }; if !ctx.code_block_first_pass() { let previous_symbol = ctx