diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index a29455df875..173c4c19655 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -9,6 +9,7 @@ use crate::{ asm_generation::from_ir::ir_type_size_in_bytes, constants, error::CompileError, + ir_generation::const_eval::compile_constant_expression, parse_tree::{AsmOp, AsmRegister, LazyOp, Literal, Purity, Visibility}, semantic_analysis::*, type_engine::{insert_type, resolve_type, TypeId, TypeInfo}, @@ -997,29 +998,33 @@ impl FnCompiler { // This is local to the function, so we add it to the locals, rather than the module // globals like other const decls. let TypedConstantDeclaration { name, value, .. } = ast_const_decl; + let const_expr_val = compile_constant_expression(context, self.module, &value)?; + let local_name = self.lexical_map.insert(name.as_str().to_owned()); + let return_type = convert_resolved_typeid(context, &value.return_type, &value.span)?; - if let TypedExpressionVariant::Literal(literal) = &value.expression { - let initialiser = convert_literal_to_constant(literal); - let return_type = convert_resolved_typeid(context, &value.return_type, &value.span)?; - let name = name.as_str().to_owned(); - self.function - .new_local_ptr(context, name.clone(), return_type, false, Some(initialiser)) - .map_err(|ir_error| { - CompileError::InternalOwned(ir_error.to_string(), Span::dummy()) - })?; - - // We still insert this into the symbol table, as itself... can they be shadowed? - // (Hrmm, name resolution in the variable expression code could be smarter about var - // decls vs const decls, for now they're essentially the same...) - self.lexical_map.insert(name); + // We compile consts the same as vars are compiled. This is because ASM generation + // cannot handle + // 1. initializing aggregates + // 2. get_ptr() + // into the data section. + let ptr = self + .function + .new_local_ptr(context, local_name, return_type, false, None) + .map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?; - Ok(Constant::get_unit(context, span_md_idx)) - } else { - Err(CompileError::Internal( - "Unsupported constant declaration type, expecting a literal.", - name.span(), - )) + // We can have empty aggregates, especially arrays, which shouldn't be initialised, but + // otherwise use a store. + let ptr_ty = *ptr.get_type(context); + if ir_type_size_in_bytes(context, &ptr_ty) > 0 { + let ptr_val = self + .current_block + .ins(context) + .get_ptr(ptr, ptr_ty, 0, span_md_idx); + self.current_block + .ins(context) + .store(ptr_val, const_expr_val, span_md_idx); } + Ok(const_expr_val) } fn compile_reassignment( diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 7b6cd646d40..1a03e347b60 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -193,14 +193,21 @@ pub(crate) fn type_check_method_application( warnings, errors ); - let variable_decl = check!( - unknown_decl.expect_variable().cloned(), - return err(warnings, errors), - warnings, - errors - ); - if !variable_decl.is_mutable.is_mutable() && *is_mutable { + let is_decl_mutable = match unknown_decl { + TypedDeclaration::ConstantDeclaration(_) => false, + _ => { + let variable_decl = check!( + unknown_decl.expect_variable().cloned(), + return err(warnings, errors), + warnings, + errors + ); + variable_decl.is_mutable.is_mutable() + } + }; + + if !is_decl_mutable && *is_mutable { errors.push(CompileError::MethodRequiresMutableSelf { method_name: method_name.easy_name(), variable_name: name.clone(), diff --git a/sway-core/src/semantic_analysis/ast_node/mod.rs b/sway-core/src/semantic_analysis/ast_node/mod.rs index 5f58d938bc6..6eed9e9e7d0 100644 --- a/sway-core/src/semantic_analysis/ast_node/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/mod.rs @@ -303,25 +303,16 @@ impl TypedAstNode { value, visibility, }) => { - let result = type_check_ascribed_expr( - ctx.by_ref(), - type_ascription.clone(), - value, - ); + let result = + type_check_ascribed_expr(ctx.by_ref(), type_ascription, value); is_screaming_snake_case(&name).ok(&mut warnings, &mut errors); let value = check!(result, error_recovery_expr(name.span()), warnings, errors); let typed_const_decl = - TypedDeclaration::VariableDeclaration(TypedVariableDeclaration { + TypedDeclaration::ConstantDeclaration(TypedConstantDeclaration { name: name.clone(), - body: value, - is_mutable: if visibility.is_public() { - VariableMutability::ExportedConst - } else { - VariableMutability::Immutable - }, - const_decl_origin: true, - type_ascription: insert_type(type_ascription), + value, + visibility, }); ctx.namespace.insert_symbol(name, typed_const_decl.clone()); typed_const_decl diff --git a/sway-core/tests/sway_to_ir/local_const_init.ir b/sway-core/tests/sway_to_ir/local_const_init.ir new file mode 100644 index 00000000000..c610e0b6608 --- /dev/null +++ b/sway-core/tests/sway_to_ir/local_const_init.ir @@ -0,0 +1,20 @@ +script { + fn main() -> u64, !1 { + local ptr { u64 } X + + entry: + v0 = get_ptr ptr { u64 } X, ptr { u64 }, 0, !2 + v1 = const { u64 } { u64 1 }, !3 + store v1, ptr v0, !2 + v2 = get_ptr ptr { u64 } X, ptr { u64 }, 0, !4 + v3 = extract_value v2, { u64 }, 0, !5 + ret u64 v3 + } +} + +!0 = filepath "/path/to/local_const_init.sw" +!1 = span !0 70 114 +!2 = span !0 91 106 +!3 = span !0 33 68 +!4 = span !0 109 110 +!5 = span !0 22 29 diff --git a/sway-core/tests/sway_to_ir/local_const_init.sw b/sway-core/tests/sway_to_ir/local_const_init.sw new file mode 100644 index 00000000000..cb05b34bea7 --- /dev/null +++ b/sway-core/tests/sway_to_ir/local_const_init.sw @@ -0,0 +1,14 @@ +script; + +struct S { + s : u64 +} + +fn s(x : u64) -> S { + S { s: x } +} + +fn main() -> u64 { + const X = s(1); + X.s +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock new file mode 100644 index 00000000000..fce30ef9643 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'const_nonconst_init' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-E57A3612ABF8CF11' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml new file mode 100644 index 00000000000..8c981ae7e1b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "const_nonconst_init" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw new file mode 100644 index 00000000000..207339315f9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw @@ -0,0 +1,10 @@ +script; + +fn bla(x: u64) -> u64 { + x + 1 +} + +fn main() -> u64 { + const X = bla(0); + X +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml new file mode 100644 index 00000000000..e92e4b0b7ee --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()const X = bla(0); +# nextln: $()Could not evaluate initializer to a const declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw index d4119b8f76d..ed15138eea7 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw @@ -36,17 +36,22 @@ const EN1c = En1::NoVal; const ETH_ID0_VALUE = ETH_ID0.value; const TUP1_idx2 = TUP1.2; +const INT1 = 1; + fn main() -> u64 { + const int1 = 1; + assert(int1 == INT1); + // initialization through function applications. - let eth_id0 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); - let eth_id1 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001); + const eth_id0 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); + const eth_id1 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001); assert(eth_id0 == ETH_ID0 && eth_id1 == ETH_ID1); // tuples and arrays. - let t1 = (2, 1, 21); + const t1 = (2, 1, 21); assert(t1.0 == TUP1.0 && t1.1 == TUP1.1 && t1.2 == TUP1.2); assert(t1.0 == TUP2.0 && t1.1 == TUP2.1 && t1.2 == TUP2.2); - let a1 = [1, 2, 3]; + const a1 = [1, 2, 3]; assert(a1[0] == ARR1[0] && a1[1] == ARR1[1] && a1[2] == ARR1[2]); assert(a1[0] == ARR2[0] && a1[1] == ARR2[1] && a1[2] == ARR2[2]);