Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

const initialization: a few fixes #2158

Merged
merged 5 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 25 additions & 20 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
19 changes: 5 additions & 14 deletions sway-core/src/semantic_analysis/ast_node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions sway-core/tests/sway_to_ir/local_const_init.ir
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions sway-core/tests/sway_to_ir/local_const_init.sw
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[[package]]
name = 'const_nonconst_init'
source = 'root'
dependencies = ['core']

[[package]]
name = 'core'
source = 'path+from-root-E57A3612ABF8CF11'
dependencies = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "const_nonconst_init"

[dependencies]
core = { path = "../../../../../../sway-lib-core" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
script;

fn bla(x: u64) -> u64 {
x + 1
}

fn main() -> u64 {
const X = bla(0);
X
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "fail"

# check: $()const X = bla(0);
# nextln: $()Could not evaluate initializer to a const declaration.
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down