Skip to content

Commit 0c8fbe0

Browse files
authored
Merge pull request #1837 from Shaikh-Ubaid/print_error_on_assign_to_input_param
Print error on assign to input parameter
2 parents 8da8f21 + 20f04f2 commit 0c8fbe0

22 files changed

+97
-52
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ jobs:
227227
run: |
228228
# Package lpynn has lpython_emulation as dependency
229229
# Hence, it should by default install lpython_emulation
230-
python -m pip install lpython_emulation==0.0.1.8 lpynn==0.0.1.3 numpy==1.24.3
230+
python -m pip install lpython_emulation==0.0.1.9 lpynn==0.0.1.4 numpy==1.24.3
231231
232232
- name: PIP show installed packages
233233
shell: bash -l {0}

integration_tests/lnn/perceptron/perceptron_main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import dataclass, i32, f64
1+
from lpython import dataclass, i32, f64, InOut
22
from sys import exit
33

44
@dataclass
@@ -27,7 +27,7 @@ def init_weights(size: i32) -> list[f64]:
2727
weights.append(0.0) # append bias
2828
return weights
2929

30-
def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, des_accuracy: f64):
30+
def init_perceptron(p: InOut[Perceptron], n: i32, rate: f64, iterations_limit: i32, des_accuracy: f64):
3131
if (n < 1 or n > 1000):
3232
print("no_of_inputs must be between [1, 1000]")
3333
exit(1)
@@ -39,7 +39,7 @@ def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, des
3939
p.cur_accuracy = 0.0
4040
p.epochs_cnt = 0
4141

42-
def train_perceptron(p: Perceptron, input_vector: list[f64], actual_output: i32):
42+
def train_perceptron(p: InOut[Perceptron], input_vector: list[f64], actual_output: i32):
4343
predicted_output: i32 = predict_perceptron(p, input_vector)
4444
error: i32 = actual_output - predicted_output
4545
i: i32
@@ -65,7 +65,7 @@ def train_epoch(p: Perceptron, input_vectors: list[list[f64]], outputs: list[i32
6565
if predict_perceptron(p, input_vector) != outputs[i]:
6666
train_perceptron(p, input_vector, outputs[i])
6767

68-
def train_dataset(p: Perceptron, input_vectors: list[list[f64]], outputs: list[i32]):
68+
def train_dataset(p: InOut[Perceptron], input_vectors: list[list[f64]], outputs: list[i32]):
6969
p.cur_accuracy = 0.0
7070
p.epochs_cnt = 0
7171
while p.cur_accuracy < p.des_accuracy and p.epochs_cnt < p.iterations_limit:

integration_tests/lnn/regression/regression_main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import dataclass, i32, f64
1+
from lpython import dataclass, i32, f64, InOut
22
from sys import exit
33

44
@dataclass
@@ -27,7 +27,7 @@ def init_weights(size: i32) -> list[f64]:
2727
weights.append(0.0) # append bias
2828
return weights
2929

30-
def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, err_limit: f64):
30+
def init_perceptron(p: InOut[Perceptron], n: i32, rate: f64, iterations_limit: i32, err_limit: f64):
3131
p.no_of_inputs = n
3232
p.weights = init_weights(n)
3333
p.learn_rate = rate
@@ -36,7 +36,7 @@ def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, err
3636
p.err = 1.0
3737
p.epochs_cnt = 0
3838

39-
def train_perceptron(p: Perceptron, input_vector: list[f64], actual_output: f64):
39+
def train_perceptron(p: InOut[Perceptron], input_vector: list[f64], actual_output: f64):
4040
predicted_output: f64 = predict_perceptron(p, input_vector)
4141
error: f64 = actual_output - predicted_output
4242
i: i32
@@ -60,7 +60,7 @@ def train_epoch(p: Perceptron, input_vectors: list[list[f64]], outputs: list[f64
6060
if predict_perceptron(p, input_vector) != outputs[i]:
6161
train_perceptron(p, input_vector, outputs[i])
6262

63-
def train_dataset(p: Perceptron, input_vectors: list[list[f64]], outputs: list[f64]):
63+
def train_dataset(p: InOut[Perceptron], input_vectors: list[list[f64]], outputs: list[f64]):
6464
prev_err: f64 = 0.0
6565
p.err = 1.0
6666
p.epochs_cnt = 0

integration_tests/lnn/utils/utils_main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import i32, f64
1+
from lpython import i32, f64, InOut
22

33
def normalize(value: f64, leftMin: f64, leftMax: f64, rightMin: f64, rightMax: f64) -> f64:
44
# Figure out how 'wide' each range is
@@ -11,7 +11,7 @@ def normalize(value: f64, leftMin: f64, leftMax: f64, rightMin: f64, rightMax: f
1111
# Convert the 0-1 range into a value in the right range.
1212
return rightMin + (valueScaled * rightSpan)
1313

14-
def normalize_input_vectors(input_vectors: list[list[f64]]):
14+
def normalize_input_vectors(input_vectors: InOut[list[list[f64]]]):
1515
rows: i32 = len(input_vectors)
1616
cols: i32 = len(input_vectors[0])
1717

@@ -29,7 +29,7 @@ def normalize_input_vectors(input_vectors: list[list[f64]]):
2929
for i in range(rows):
3030
input_vectors[i][j] = normalize(input_vectors[i][j], colMinVal, colMaxVal, -1.0, 1.0)
3131

32-
def normalize_output_vector(output_vector: list[f64]):
32+
def normalize_output_vector(output_vector: InOut[list[f64]]):
3333
rows: i32 = len(output_vector)
3434
colMinVal: f64 = output_vector[0]
3535
colMaxVal: f64 = output_vector[0]

integration_tests/print_list_tuple_01.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import f64, i32, c64
1+
from lpython import f64, i32, c64, InOut
22

33

44
def test_print_list():
@@ -20,7 +20,7 @@ def test_print_list():
2020
print(x, y, z, t)
2121

2222

23-
def f(y: list[i32]) -> list[i32]:
23+
def f(y: InOut[list[i32]]) -> list[i32]:
2424
y.append(4)
2525
return y
2626

integration_tests/print_list_tuple_02.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from lpython import i32, f64
1+
from lpython import i32, f64, InOut
22

3-
def insert_tuples_into_list(l: list[tuple[i32, f64, str]], size: i32) -> list[tuple[i32, f64, str]]:
3+
def insert_tuples_into_list(l: InOut[list[tuple[i32, f64, str]]], size: i32) -> list[tuple[i32, f64, str]]:
44
i: i32
55
string: str
66
t: tuple[i32, f64, str]

integration_tests/structs_01.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import i32, f32, f64, dataclass
1+
from lpython import i32, f32, f64, dataclass, InOut
22

33
@dataclass
44
class A:
@@ -9,7 +9,7 @@ def f(a: A):
99
print(a.x)
1010
print(a.y)
1111

12-
def change_struct(a: A):
12+
def change_struct(a: InOut[A]):
1313
a.x = a.x + 1
1414
a.y = a.y + f32(1)
1515

integration_tests/structs_05.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import i32, f64, i64, i16, i8, f32, dataclass
1+
from lpython import i32, f64, i64, i16, i8, f32, dataclass, InOut
22
from numpy import empty
33

44
@dataclass
@@ -33,7 +33,7 @@ def verify(s: A[:], x1: i32, y1: f64, x2: i32, y2: f64):
3333
assert s1.c == i8(x2)
3434
assert s1.d
3535

36-
def update_1(s: A):
36+
def update_1(s: InOut[A]):
3737
s.x = 2
3838
s.y = 1.2
3939
s.z = i64(2)

integration_tests/test_list_03.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import i32
1+
from lpython import i32, InOut
22

33
def test_list_01(n: i32) -> i32:
44
a: list[i32] = []
@@ -10,7 +10,7 @@ def test_list_01(n: i32) -> i32:
1010
sum += a[i]
1111
return sum
1212

13-
def test_list_insert_02(x: list[i32], n: i32) -> list[i32]:
13+
def test_list_insert_02(x: InOut[list[i32]], n: i32) -> list[i32]:
1414
i: i32
1515
imod: i32
1616
for i in range(n):

integration_tests/test_list_05.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lpython import i32, f64
1+
from lpython import i32, f64, InOut
22

33
def check_list_of_tuples(l: list[tuple[i32, f64, str]], sign: i32):
44
size: i32 = len(l)
@@ -29,7 +29,7 @@ def fill_list_of_tuples(size: i32) -> list[tuple[i32, f64, str]]:
2929

3030
return l1
3131

32-
def insert_tuples_into_list(l: list[tuple[i32, f64, str]], size: i32) -> list[tuple[i32, f64, str]]:
32+
def insert_tuples_into_list(l: InOut[list[tuple[i32, f64, str]]], size: i32) -> list[tuple[i32, f64, str]]:
3333
i: i32
3434
string: str
3535
t: tuple[i32, f64, str]

integration_tests/test_list_08.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from lpython import i32, f64
1+
from lpython import i32, f64, InOut
22
from copy import deepcopy
33

4-
def sort(l: list[i32]) -> list[i32]:
4+
def sort(l: InOut[list[i32]]) -> list[i32]:
55
i: i32; j: i32
66

77
for i in range(len(l)):

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4662,6 +4662,38 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
46624662
targets.size());
46634663
}
46644664

4665+
bool check_is_assign_to_input_param(AST::expr_t* x) {
4666+
if (AST::is_a<AST::Name_t>(*x)) {
4667+
AST::Name_t* n = AST::down_cast<AST::Name_t>(x);
4668+
ASR::symbol_t* s = current_scope->resolve_symbol(n->m_id);
4669+
if (!s) {
4670+
throw SemanticError("Variable: '" + std::string(n->m_id) + "' is not declared",
4671+
x->base.loc);
4672+
}
4673+
ASR::Variable_t* v = ASR::down_cast<ASR::Variable_t>(s);
4674+
if (v->m_intent == ASR::intentType::In) {
4675+
throw SemanticError("Assignment to an input function parameter `"
4676+
+ std::string(v->m_name) + "` is not allowed", x->base.loc);
4677+
}
4678+
return true;
4679+
} else if (AST::is_a<AST::Subscript_t>(*x)) {
4680+
AST::Subscript_t* s = AST::down_cast<AST::Subscript_t>(x);
4681+
return check_is_assign_to_input_param(s->m_value);
4682+
} else if (AST::is_a<AST::Attribute_t>(*x)) {
4683+
AST::Attribute_t* s = AST::down_cast<AST::Attribute_t>(x);
4684+
return check_is_assign_to_input_param(s->m_value);
4685+
} else if (AST::is_a<AST::Tuple_t>(*x)) {
4686+
AST::Tuple_t* t = AST::down_cast<AST::Tuple_t>(x);
4687+
bool is_assign_to_input = false;
4688+
for (size_t i = 0; i < t->n_elts && !is_assign_to_input; i++) {
4689+
is_assign_to_input = is_assign_to_input || check_is_assign_to_input_param(t->m_elts[i]);
4690+
}
4691+
return is_assign_to_input;
4692+
}else {
4693+
throw SemanticError("Unsupported type in check_is_assign_to_input_param()", x->base.loc);
4694+
}
4695+
}
4696+
46654697
void visit_Assign(const AST::Assign_t &x) {
46664698
ASR::expr_t *target, *assign_value = nullptr, *tmp_value;
46674699
bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
@@ -4689,6 +4721,7 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
46894721
}
46904722
for (size_t i=0; i<x.n_targets; i++) {
46914723
tmp_value = assign_value;
4724+
check_is_assign_to_input_param(x.m_targets[i]);
46924725
if (AST::is_a<AST::Subscript_t>(*x.m_targets[i])) {
46934726
AST::Subscript_t *sb = AST::down_cast<AST::Subscript_t>(x.m_targets[i]);
46944727
if (AST::is_a<AST::Name_t>(*sb->m_value)) {
@@ -4831,13 +4864,6 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
48314864
std::string var_name = std::string(v->m_name);
48324865
throw SemanticError("Assignment to loop variable `" + std::string(to_lower(var_name)) +"` is not allowed", target->base.loc);
48334866
}
4834-
if (sym->type == ASR::symbolType::Variable) {
4835-
ASR::Variable_t *v = ASR::down_cast<ASR::Variable_t>(sym);
4836-
if (v->m_intent == ASR::intentType::In) {
4837-
throw SemanticError("Assignment to an input function parameter `"
4838-
+ std::string(v->m_name) + "` is not allowed", target->base.loc);
4839-
}
4840-
}
48414867
}
48424868
tmp_vec.push_back(ASR::make_Assignment_t(al, x.base.base.loc, target, tmp_value,
48434869
overloaded));

src/lpython/semantics/python_attribute_eval.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct AttributeHandler {
1616
const Location &, Vec<ASR::expr_t*> &, diag::Diagnostics &);
1717

1818
std::map<std::string, attribute_eval_callback> attribute_map;
19+
std::set<std::string> modify_attr_set;
1920

2021
AttributeHandler() {
2122
attribute_map = {
@@ -35,6 +36,10 @@ struct AttributeHandler {
3536
{"dict@get", &eval_dict_get},
3637
{"dict@pop", &eval_dict_pop}
3738
};
39+
40+
modify_attr_set = {"list@append", "list@remove",
41+
"list@reverse", "list@clear", "list@insert", "list@pop",
42+
"set@pop", "set@add", "set@remove", "dict@pop"};
3843
}
3944

4045
std::string get_type_name(ASR::ttype_t *t) {
@@ -60,6 +65,13 @@ struct AttributeHandler {
6065
throw SemanticError("Type name is not implemented yet.", loc);
6166
}
6267
std::string key = class_name + "@" + attr_name;
68+
if (modify_attr_set.find(key) != modify_attr_set.end()) {
69+
ASR::Variable_t* v = ASRUtils::EXPR2VAR(e);
70+
if (v->m_intent == ASRUtils::intent_in) {
71+
throw SemanticError("Modifying input function parameter `"
72+
+ std::string(v->m_name) + "` is not allowed", loc);
73+
}
74+
}
6375
auto search = attribute_map.find(key);
6476
if (search != attribute_map.end()) {
6577
attribute_eval_callback cb = search->second;

tests/dictionary1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_dict_pop():
3333
x = y.pop("a")
3434

3535

36-
def f(x: dict[i32, i32]):
36+
def f(x: InOut[dict[i32, i32]]):
3737
x[2] = 4
3838

3939
def test_issue_204():

tests/reference/asr-dictionary1-a105a36.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"basename": "asr-dictionary1-a105a36",
33
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/dictionary1.py",
5-
"infile_hash": "2d9f15b746aa8185afb3f2dc6415c20a7edccc8e711df1b323178d94",
5+
"infile_hash": "f164e8dbe52a04e4fd541d6ff6edae2994d683074087d8785a1759e4",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-dictionary1-a105a36.stdout",
9-
"stdout_hash": "0bd85eafba7412639903c3cf431f99a5cec2e2e48e265f0f8014a641",
9+
"stdout_hash": "54e029691491889b71111718910b430fa193a93b95b6258fc0c8fbff",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

tests/reference/asr-dictionary1-a105a36.stdout

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
6
1818
x
1919
[]
20-
In
20+
InOut
2121
()
2222
()
2323
Default

tests/reference/asr-structs_01-be14d49.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"basename": "asr-structs_01-be14d49",
33
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/structs_01.py",
5-
"infile_hash": "c8012b0c841b0d8e304c18ca7c6d4365f1d5e41235dc6f4e2dc21664",
5+
"infile_hash": "9f36a4abcc3a50ccc10df1f6b04998b7a20041853142c85a2e86b724",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-structs_01-be14d49.stdout",
9-
"stdout_hash": "d23a95b31aa0235947d1720b9f812b84e9c67cafa0e32a3b27802025",
9+
"stdout_hash": "cca5515fad19fdf65edf97e51effc67b37295513eaaab45f174854a9",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

tests/reference/asr-structs_01-be14d49.stdout

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
4
104104
a
105105
[]
106-
In
106+
InOut
107107
()
108108
()
109109
Default

tests/reference/asr-structs_05-fa98307.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"basename": "asr-structs_05-fa98307",
33
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/structs_05.py",
5-
"infile_hash": "94d06fb2844e1060d5df5291c3ccc4bd0e2bc8a8a59651814f0c4f0e",
5+
"infile_hash": "5c587158fe09782d15aa8f5f9c24468c62795e9f537a9eb439d8e8a4",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-structs_05-fa98307.stdout",
9-
"stdout_hash": "fb8c872aaca346b06d130d093b14f58f616b667e4613f765fb32a2e6",
9+
"stdout_hash": "bde17720270048c509903eac1703b839f3e34c0a9cb1d8df1811bd55",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

tests/reference/asr-structs_05-fa98307.stdout

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@
419419
195
420420
s
421421
[]
422-
In
422+
InOut
423423
()
424424
()
425425
Default

tests/reference/ast-dictionary1-1a7e00a.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"basename": "ast-dictionary1-1a7e00a",
33
"cmd": "lpython --show-ast --no-color {infile} -o {outfile}",
44
"infile": "tests/dictionary1.py",
5-
"infile_hash": "2d9f15b746aa8185afb3f2dc6415c20a7edccc8e711df1b323178d94",
5+
"infile_hash": "f164e8dbe52a04e4fd541d6ff6edae2994d683074087d8785a1759e4",
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "ast-dictionary1-1a7e00a.stdout",
9-
"stdout_hash": "cd85b7fa2f2f43a4be7a88c54d582616f529b9acee1de757eb361bd7",
9+
"stdout_hash": "c347af8c0abbed4664f354dc24c1bd061218c0584aef27889fbf1745",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

0 commit comments

Comments
 (0)