Skip to content

Print error on assign to input parameter #1837

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

Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ jobs:
run: |
# Package lpynn has lpython_emulation as dependency
# Hence, it should by default install lpython_emulation
python -m pip install lpython_emulation==0.0.1.8 lpynn==0.0.1.3 numpy==1.24.3
python -m pip install lpython_emulation==0.0.1.9 lpynn==0.0.1.4 numpy==1.24.3

- name: PIP show installed packages
shell: bash -l {0}
Expand Down
8 changes: 4 additions & 4 deletions integration_tests/lnn/perceptron/perceptron_main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import dataclass, i32, f64
from lpython import dataclass, i32, f64, InOut
from sys import exit

@dataclass
Expand Down Expand Up @@ -27,7 +27,7 @@ def init_weights(size: i32) -> list[f64]:
weights.append(0.0) # append bias
return weights

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

def train_perceptron(p: Perceptron, input_vector: list[f64], actual_output: i32):
def train_perceptron(p: InOut[Perceptron], input_vector: list[f64], actual_output: i32):
predicted_output: i32 = predict_perceptron(p, input_vector)
error: i32 = actual_output - predicted_output
i: i32
Expand All @@ -65,7 +65,7 @@ def train_epoch(p: Perceptron, input_vectors: list[list[f64]], outputs: list[i32
if predict_perceptron(p, input_vector) != outputs[i]:
train_perceptron(p, input_vector, outputs[i])

def train_dataset(p: Perceptron, input_vectors: list[list[f64]], outputs: list[i32]):
def train_dataset(p: InOut[Perceptron], input_vectors: list[list[f64]], outputs: list[i32]):
p.cur_accuracy = 0.0
p.epochs_cnt = 0
while p.cur_accuracy < p.des_accuracy and p.epochs_cnt < p.iterations_limit:
Expand Down
8 changes: 4 additions & 4 deletions integration_tests/lnn/regression/regression_main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import dataclass, i32, f64
from lpython import dataclass, i32, f64, InOut
from sys import exit

@dataclass
Expand Down Expand Up @@ -27,7 +27,7 @@ def init_weights(size: i32) -> list[f64]:
weights.append(0.0) # append bias
return weights

def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, err_limit: f64):
def init_perceptron(p: InOut[Perceptron], n: i32, rate: f64, iterations_limit: i32, err_limit: f64):
p.no_of_inputs = n
p.weights = init_weights(n)
p.learn_rate = rate
Expand All @@ -36,7 +36,7 @@ def init_perceptron(p: Perceptron, n: i32, rate: f64, iterations_limit: i32, err
p.err = 1.0
p.epochs_cnt = 0

def train_perceptron(p: Perceptron, input_vector: list[f64], actual_output: f64):
def train_perceptron(p: InOut[Perceptron], input_vector: list[f64], actual_output: f64):
predicted_output: f64 = predict_perceptron(p, input_vector)
error: f64 = actual_output - predicted_output
i: i32
Expand All @@ -60,7 +60,7 @@ def train_epoch(p: Perceptron, input_vectors: list[list[f64]], outputs: list[f64
if predict_perceptron(p, input_vector) != outputs[i]:
train_perceptron(p, input_vector, outputs[i])

def train_dataset(p: Perceptron, input_vectors: list[list[f64]], outputs: list[f64]):
def train_dataset(p: InOut[Perceptron], input_vectors: list[list[f64]], outputs: list[f64]):
prev_err: f64 = 0.0
p.err = 1.0
p.epochs_cnt = 0
Expand Down
6 changes: 3 additions & 3 deletions integration_tests/lnn/utils/utils_main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import i32, f64
from lpython import i32, f64, InOut

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

def normalize_input_vectors(input_vectors: list[list[f64]]):
def normalize_input_vectors(input_vectors: InOut[list[list[f64]]]):
rows: i32 = len(input_vectors)
cols: i32 = len(input_vectors[0])

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

def normalize_output_vector(output_vector: list[f64]):
def normalize_output_vector(output_vector: InOut[list[f64]]):
rows: i32 = len(output_vector)
colMinVal: f64 = output_vector[0]
colMaxVal: f64 = output_vector[0]
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/print_list_tuple_01.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import f64, i32, c64
from lpython import f64, i32, c64, InOut


def test_print_list():
Expand All @@ -20,7 +20,7 @@ def test_print_list():
print(x, y, z, t)


def f(y: list[i32]) -> list[i32]:
def f(y: InOut[list[i32]]) -> list[i32]:
y.append(4)
return y

Expand Down
4 changes: 2 additions & 2 deletions integration_tests/print_list_tuple_02.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from lpython import i32, f64
from lpython import i32, f64, InOut

def insert_tuples_into_list(l: list[tuple[i32, f64, str]], size: i32) -> list[tuple[i32, f64, str]]:
def insert_tuples_into_list(l: InOut[list[tuple[i32, f64, str]]], size: i32) -> list[tuple[i32, f64, str]]:
i: i32
string: str
t: tuple[i32, f64, str]
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/structs_01.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import i32, f32, f64, dataclass
from lpython import i32, f32, f64, dataclass, InOut

@dataclass
class A:
Expand All @@ -9,7 +9,7 @@ def f(a: A):
print(a.x)
print(a.y)

def change_struct(a: A):
def change_struct(a: InOut[A]):
a.x = a.x + 1
a.y = a.y + f32(1)

Expand Down
4 changes: 2 additions & 2 deletions integration_tests/structs_05.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import i32, f64, i64, i16, i8, f32, dataclass
from lpython import i32, f64, i64, i16, i8, f32, dataclass, InOut
from numpy import empty

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

def update_1(s: A):
def update_1(s: InOut[A]):
s.x = 2
s.y = 1.2
s.z = i64(2)
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/test_list_03.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import i32
from lpython import i32, InOut

def test_list_01(n: i32) -> i32:
a: list[i32] = []
Expand All @@ -10,7 +10,7 @@ def test_list_01(n: i32) -> i32:
sum += a[i]
return sum

def test_list_insert_02(x: list[i32], n: i32) -> list[i32]:
def test_list_insert_02(x: InOut[list[i32]], n: i32) -> list[i32]:
i: i32
imod: i32
for i in range(n):
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/test_list_05.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lpython import i32, f64
from lpython import i32, f64, InOut

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

return l1

def insert_tuples_into_list(l: list[tuple[i32, f64, str]], size: i32) -> list[tuple[i32, f64, str]]:
def insert_tuples_into_list(l: InOut[list[tuple[i32, f64, str]]], size: i32) -> list[tuple[i32, f64, str]]:
i: i32
string: str
t: tuple[i32, f64, str]
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/test_list_08.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from lpython import i32, f64
from lpython import i32, f64, InOut
from copy import deepcopy

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

for i in range(len(l)):
Expand Down
40 changes: 33 additions & 7 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4662,6 +4662,38 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
targets.size());
}

bool check_is_assign_to_input_param(AST::expr_t* x) {
if (AST::is_a<AST::Name_t>(*x)) {
AST::Name_t* n = AST::down_cast<AST::Name_t>(x);
ASR::symbol_t* s = current_scope->resolve_symbol(n->m_id);
if (!s) {
throw SemanticError("Variable: '" + std::string(n->m_id) + "' is not declared",
x->base.loc);
}
ASR::Variable_t* v = ASR::down_cast<ASR::Variable_t>(s);
if (v->m_intent == ASR::intentType::In) {
throw SemanticError("Assignment to an input function parameter `"
+ std::string(v->m_name) + "` is not allowed", x->base.loc);
}
return true;
} else if (AST::is_a<AST::Subscript_t>(*x)) {
AST::Subscript_t* s = AST::down_cast<AST::Subscript_t>(x);
return check_is_assign_to_input_param(s->m_value);
} else if (AST::is_a<AST::Attribute_t>(*x)) {
AST::Attribute_t* s = AST::down_cast<AST::Attribute_t>(x);
return check_is_assign_to_input_param(s->m_value);
} else if (AST::is_a<AST::Tuple_t>(*x)) {
AST::Tuple_t* t = AST::down_cast<AST::Tuple_t>(x);
bool is_assign_to_input = false;
for (size_t i = 0; i < t->n_elts && !is_assign_to_input; i++) {
is_assign_to_input = is_assign_to_input || check_is_assign_to_input_param(t->m_elts[i]);
}
return is_assign_to_input;
}else {
throw SemanticError("Unsupported type in check_is_assign_to_input_param()", x->base.loc);
}
}

void visit_Assign(const AST::Assign_t &x) {
ASR::expr_t *target, *assign_value = nullptr, *tmp_value;
bool is_c_p_pointer_call_copy = is_c_p_pointer_call;
Expand Down Expand Up @@ -4689,6 +4721,7 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
}
for (size_t i=0; i<x.n_targets; i++) {
tmp_value = assign_value;
check_is_assign_to_input_param(x.m_targets[i]);
if (AST::is_a<AST::Subscript_t>(*x.m_targets[i])) {
AST::Subscript_t *sb = AST::down_cast<AST::Subscript_t>(x.m_targets[i]);
if (AST::is_a<AST::Name_t>(*sb->m_value)) {
Expand Down Expand Up @@ -4831,13 +4864,6 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
std::string var_name = std::string(v->m_name);
throw SemanticError("Assignment to loop variable `" + std::string(to_lower(var_name)) +"` is not allowed", target->base.loc);
}
if (sym->type == ASR::symbolType::Variable) {
ASR::Variable_t *v = ASR::down_cast<ASR::Variable_t>(sym);
if (v->m_intent == ASR::intentType::In) {
throw SemanticError("Assignment to an input function parameter `"
+ std::string(v->m_name) + "` is not allowed", target->base.loc);
}
}
}
tmp_vec.push_back(ASR::make_Assignment_t(al, x.base.base.loc, target, tmp_value,
overloaded));
Expand Down
12 changes: 12 additions & 0 deletions src/lpython/semantics/python_attribute_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct AttributeHandler {
const Location &, Vec<ASR::expr_t*> &, diag::Diagnostics &);

std::map<std::string, attribute_eval_callback> attribute_map;
std::set<std::string> modify_attr_set;

AttributeHandler() {
attribute_map = {
Expand All @@ -35,6 +36,10 @@ struct AttributeHandler {
{"dict@get", &eval_dict_get},
{"dict@pop", &eval_dict_pop}
};

modify_attr_set = {"list@append", "list@remove",
"list@reverse", "list@clear", "list@insert", "list@pop",
"set@pop", "set@add", "set@remove", "dict@pop"};
}

std::string get_type_name(ASR::ttype_t *t) {
Expand All @@ -60,6 +65,13 @@ struct AttributeHandler {
throw SemanticError("Type name is not implemented yet.", loc);
}
std::string key = class_name + "@" + attr_name;
if (modify_attr_set.find(key) != modify_attr_set.end()) {
ASR::Variable_t* v = ASRUtils::EXPR2VAR(e);
if (v->m_intent == ASRUtils::intent_in) {
throw SemanticError("Modifying input function parameter `"
+ std::string(v->m_name) + "` is not allowed", loc);
}
}
auto search = attribute_map.find(key);
if (search != attribute_map.end()) {
attribute_eval_callback cb = search->second;
Expand Down
2 changes: 1 addition & 1 deletion tests/dictionary1.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_dict_pop():
x = y.pop("a")


def f(x: dict[i32, i32]):
def f(x: InOut[dict[i32, i32]]):
x[2] = 4

def test_issue_204():
Expand Down
4 changes: 2 additions & 2 deletions tests/reference/asr-dictionary1-a105a36.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"basename": "asr-dictionary1-a105a36",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/dictionary1.py",
"infile_hash": "2d9f15b746aa8185afb3f2dc6415c20a7edccc8e711df1b323178d94",
"infile_hash": "f164e8dbe52a04e4fd541d6ff6edae2994d683074087d8785a1759e4",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-dictionary1-a105a36.stdout",
"stdout_hash": "0bd85eafba7412639903c3cf431f99a5cec2e2e48e265f0f8014a641",
"stdout_hash": "54e029691491889b71111718910b430fa193a93b95b6258fc0c8fbff",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-dictionary1-a105a36.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
6
x
[]
In
InOut
()
()
Default
Expand Down
4 changes: 2 additions & 2 deletions tests/reference/asr-structs_01-be14d49.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"basename": "asr-structs_01-be14d49",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/structs_01.py",
"infile_hash": "c8012b0c841b0d8e304c18ca7c6d4365f1d5e41235dc6f4e2dc21664",
"infile_hash": "9f36a4abcc3a50ccc10df1f6b04998b7a20041853142c85a2e86b724",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-structs_01-be14d49.stdout",
"stdout_hash": "d23a95b31aa0235947d1720b9f812b84e9c67cafa0e32a3b27802025",
"stdout_hash": "cca5515fad19fdf65edf97e51effc67b37295513eaaab45f174854a9",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-structs_01-be14d49.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
4
a
[]
In
InOut
()
()
Default
Expand Down
4 changes: 2 additions & 2 deletions tests/reference/asr-structs_05-fa98307.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"basename": "asr-structs_05-fa98307",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/structs_05.py",
"infile_hash": "94d06fb2844e1060d5df5291c3ccc4bd0e2bc8a8a59651814f0c4f0e",
"infile_hash": "5c587158fe09782d15aa8f5f9c24468c62795e9f537a9eb439d8e8a4",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-structs_05-fa98307.stdout",
"stdout_hash": "fb8c872aaca346b06d130d093b14f58f616b667e4613f765fb32a2e6",
"stdout_hash": "bde17720270048c509903eac1703b839f3e34c0a9cb1d8df1811bd55",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-structs_05-fa98307.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@
195
s
[]
In
InOut
()
()
Default
Expand Down
4 changes: 2 additions & 2 deletions tests/reference/ast-dictionary1-1a7e00a.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"basename": "ast-dictionary1-1a7e00a",
"cmd": "lpython --show-ast --no-color {infile} -o {outfile}",
"infile": "tests/dictionary1.py",
"infile_hash": "2d9f15b746aa8185afb3f2dc6415c20a7edccc8e711df1b323178d94",
"infile_hash": "f164e8dbe52a04e4fd541d6ff6edae2994d683074087d8785a1759e4",
"outfile": null,
"outfile_hash": null,
"stdout": "ast-dictionary1-1a7e00a.stdout",
"stdout_hash": "cd85b7fa2f2f43a4be7a88c54d582616f529b9acee1de757eb361bd7",
"stdout_hash": "c347af8c0abbed4664f354dc24c1bd061218c0584aef27889fbf1745",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
Loading