[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 3/9] pkl: disallow immediate break/continue/return in EXCOND
From: |
Mohammad-Reza Nabipoor |
Subject: |
[PATCH 3/9] pkl: disallow immediate break/continue/return in EXCOND |
Date: |
Thu, 28 Dec 2023 02:19:28 +0100 |
This commit disallow change in control flow from EXCOND expressions
which the LHS of the operator is a compound statement.
The following Poke snippets are now illegal:
Sample 1:
whlie (condition)
{ break; } ?! E_elem;
Sample 2:
whlie (condition)
{ continue; } ?! E_elem;
Sample 3:
fun f = int:
{
{ return 1; } ?! E_elem;
return 0;
}
2023-12-27 Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
* libpoke/pkl-anal.h (struct pkl_anal_excond_ctx): New struct.
(struct pkl_anal_payload): Add new fields for EXCOND-related
analysis.
* libpoke/pkl-anal.c (pkl_anal1_pr_comp_stmt): New phase.
(pkl_anal1_pr_loop_stmt): Likewise.
(pkl_anal1_ps_loop_stmt): Likewise.
(pkl_anal1_ps_comp_stmt): Add checks to disallow
break/continue/return statements in EXCOND's compound statement.
(pkl_anal1_pr_func): Likewise.
(pkl_anal1_ps_func): Likewise.
(pkl_anal1_ps_break_continue_stmt): Likewise.
(pkl_anal1_ps_return_stmt): Likewise.
(pkl_phase_anal1): Register new phases.
* testsuite/poke.pkl/return-3.pk: Adapt to new semantics.
* testsuite/poke.pkl/return-4.pk: Likewise.
* testsuite/poke.pkl/return-5.pk: Likewise.
* testsuite/poke.pkl/return-6.pk: Removed.
* testsuite/poke.pkl/return-diag-5.pk: New test.
* testsuite/poke.pkl/excond-diag-2.pk: Likewise.
* testsuite/poke.pkl/excond-diag-3.pk: Likewise.
* testsuite/poke.pkl/excond-6.pk: Likewise.
* testsuite/Makefile.am (EXTRA_DIST): Update.
---
ChangeLog | 25 ++++++
libpoke/pkl-anal.c | 118 ++++++++++++++++++++++++++--
libpoke/pkl-anal.h | 24 +++++-
testsuite/Makefile.am | 5 +-
testsuite/poke.pkl/excond-6.pk | 20 +++++
testsuite/poke.pkl/excond-diag-2.pk | 4 +
testsuite/poke.pkl/excond-diag-3.pk | 9 +++
testsuite/poke.pkl/return-3.pk | 6 +-
testsuite/poke.pkl/return-4.pk | 2 +-
testsuite/poke.pkl/return-5.pk | 2 +-
testsuite/poke.pkl/return-6.pk | 6 --
testsuite/poke.pkl/return-diag-5.pk | 3 +
12 files changed, 206 insertions(+), 18 deletions(-)
create mode 100644 testsuite/poke.pkl/excond-6.pk
create mode 100644 testsuite/poke.pkl/excond-diag-2.pk
create mode 100644 testsuite/poke.pkl/excond-diag-3.pk
delete mode 100644 testsuite/poke.pkl/return-6.pk
create mode 100644 testsuite/poke.pkl/return-diag-5.pk
diff --git a/ChangeLog b/ChangeLog
index 20b1356a..bb2738a4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2023-12-27 Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
+
+ * libpoke/pkl-anal.h (struct pkl_anal_excond_ctx): New struct.
+ (struct pkl_anal_payload): Add new fields for EXCOND-related
+ analysis.
+ * libpoke/pkl-anal.c (pkl_anal1_pr_comp_stmt): New phase.
+ (pkl_anal1_pr_loop_stmt): Likewise.
+ (pkl_anal1_ps_loop_stmt): Likewise.
+ (pkl_anal1_ps_comp_stmt): Add checks to disallow
+ break/continue/return statements in EXCOND's compound statement.
+ (pkl_anal1_pr_func): Likewise.
+ (pkl_anal1_ps_func): Likewise.
+ (pkl_anal1_ps_break_continue_stmt): Likewise.
+ (pkl_anal1_ps_return_stmt): Likewise.
+ (pkl_phase_anal1): Register new phases.
+ * testsuite/poke.pkl/return-3.pk: Adapt to new semantics.
+ * testsuite/poke.pkl/return-4.pk: Likewise.
+ * testsuite/poke.pkl/return-5.pk: Likewise.
+ * testsuite/poke.pkl/return-6.pk: Removed.
+ * testsuite/poke.pkl/return-diag-5.pk: New test.
+ * testsuite/poke.pkl/excond-diag-2.pk: Likewise.
+ * testsuite/poke.pkl/excond-diag-3.pk: Likewise.
+ * testsuite/poke.pkl/excond-6.pk: Likewise.
+ * testsuite/Makefile.am (EXTRA_DIST): Update.
+
2023-12-25 Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
* libpoke/pkl-ast.h (PKL_AST_STRUCT_TYPE_FIELD_CONSTRAINT_LOC):
diff --git a/libpoke/pkl-anal.c b/libpoke/pkl-anal.c
index 00c94c54..5914bae5 100644
--- a/libpoke/pkl-anal.c
+++ b/libpoke/pkl-anal.c
@@ -71,6 +71,34 @@
PKL_ANAL_PAYLOAD->next_context--; \
} while (0)
+#define PKL_ANAL_EXCOND \
+ (PKL_ANAL_PAYLOAD->next_excond == 0 \
+ ? NULL \
+ : &PKL_ANAL_PAYLOAD->exconds[PKL_ANAL_PAYLOAD->next_excond - 1])
+
+#define PKL_ANAL_PUSH_EXCOND \
+ do \
+ { \
+ if (PKL_ANAL_PAYLOAD->next_excond >= PKL_ANAL_MAX_COMP_STMT_NEST) \
+ { \
+ PKL_ERROR (PKL_AST_NOLOC, \
+ "maximum nested EXCOND nesting level reached"); \
+ PKL_PASS_ERROR; \
+ } \
+ PKL_ANAL_PAYLOAD->exconds[PKL_ANAL_PAYLOAD->next_excond].nfunc = 0; \
+ PKL_ANAL_PAYLOAD->exconds[PKL_ANAL_PAYLOAD->next_excond].nloop = 0; \
+ PKL_ANAL_PAYLOAD->next_excond++; \
+ } \
+ while (0)
+
+#define PKL_ANAL_POP_EXCOND \
+ do \
+ { \
+ assert (PKL_ANAL_PAYLOAD->next_excond > 0); \
+ PKL_ANAL_PAYLOAD->next_excond--; \
+ } \
+ while (0)
+
/* The following handler is used in all anal phases, and initializes
the phase payload. */
@@ -239,8 +267,25 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_type_struct)
}
PKL_PHASE_END_HANDLER
+/* If the compound statement is the first operand of an excond
+ operator, then having any kind of constructs that escape the
+ current lexical scope (break, continue, return) is disallowed. */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_anal1_pr_comp_stmt)
+{
+ if (PKL_PASS_PARENT
+ && PKL_AST_CODE (PKL_PASS_PARENT) == PKL_AST_EXP
+ && PKL_AST_EXP_CODE (PKL_PASS_PARENT) == PKL_AST_OP_EXCOND)
+ {
+ PKL_ANAL_PUSH_EXCOND;
+ }
+}
+PKL_PHASE_END_HANDLER
+
/* Builtin compound statements can't contain statements
- themselves. */
+ themselves.
+
+ For EXCOND's comp_stmt, see the comments for PKL_ANAL_PR_COMP_STMT. */
PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_comp_stmt)
{
@@ -254,6 +299,13 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_comp_stmt)
PKL_ANAL_PAYLOAD->errors++;
PKL_PASS_ERROR;
}
+
+ if (PKL_PASS_PARENT
+ && PKL_AST_CODE (PKL_PASS_PARENT) == PKL_AST_EXP
+ && PKL_AST_EXP_CODE (PKL_PASS_PARENT) == PKL_AST_OP_EXCOND)
+ {
+ PKL_ANAL_POP_EXCOND;
+ }
}
PKL_PHASE_END_HANDLER
@@ -315,19 +367,26 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_funcall)
}
PKL_PHASE_END_HANDLER
-/* Methods introduce an analysis context. */
+/* Methods introduce an analysis context.
+
+ Track functions when we're in EXCOND's comp_stmt. */
PKL_PHASE_BEGIN_HANDLER (pkl_anal1_pr_func)
{
if (PKL_AST_FUNC_METHOD_P (PKL_PASS_NODE))
PKL_ANAL_PUSH_CONTEXT (PKL_ANAL_CONTEXT_METHOD);
+
+ if (PKL_ANAL_EXCOND)
+ PKL_ANAL_EXCOND->nfunc++;
}
PKL_PHASE_END_HANDLER
/* Check that all optional formal arguments in a function specifier
are at the end of the arguments list, and other checks.
- Also, pop the analysis context if this was a method. */
+ Also, pop the analysis context if this was a method.
+
+ Track functions when we're in EXCOND's comp_stmt. */
PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_func)
{
@@ -365,6 +424,9 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_func)
if (PKL_AST_FUNC_METHOD_P (PKL_PASS_NODE))
PKL_ANAL_POP_CONTEXT;
+
+ if (PKL_ANAL_EXCOND)
+ PKL_ANAL_EXCOND->nfunc--;
}
PKL_PHASE_END_HANDLER
@@ -393,8 +455,28 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_type_function)
}
PKL_PHASE_END_HANDLER
+/* Track loops when we're in EXCOND's comp_stmt. */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_anal1_pr_loop_stmt)
+{
+ if (PKL_ANAL_EXCOND)
+ PKL_ANAL_EXCOND->nloop++;
+}
+PKL_PHASE_END_HANDLER
+
+/* Track loops when we're in EXCOND's comp_stmt. */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_loop_stmt)
+{
+ if (PKL_ANAL_EXCOND)
+ PKL_ANAL_EXCOND->nloop--;
+}
+PKL_PHASE_END_HANDLER
+
/* Make sure every BREAK and CONTINUE statement has an associated
- entity. */
+ entity.
+
+ Disallow immediate BREAK or CONTINUE in EXCOND's comp_stmt. */
PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_break_continue_stmt)
{
@@ -411,11 +493,25 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_break_continue_stmt)
PKL_ANAL_PAYLOAD->errors++;
PKL_PASS_ERROR;
}
+
+ if (PKL_ANAL_EXCOND && PKL_ANAL_EXCOND->nloop == 0)
+ {
+ int kind = PKL_AST_BREAK_CONTINUE_STMT_KIND (stmt);
+
+ PKL_ERROR (PKL_AST_LOC (stmt),
+ "%s statement without containing statement in EXCOND (?!) "
+ "operator", kind == PKL_AST_BREAK_CONTINUE_STMT_KIND_BREAK
+ ? "`break'" : "`continue'");
+ PKL_ANAL_PAYLOAD->errors++;
+ PKL_PASS_ERROR;
+ }
}
PKL_PHASE_END_HANDLER
/* Every return statement should be associated with a containing
- function. */
+ function.
+
+ Disallow immediate RETURN statement in EXCOND's comp_stmt. */
PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_return_stmt)
{
@@ -428,6 +524,15 @@ PKL_PHASE_BEGIN_HANDLER (pkl_anal1_ps_return_stmt)
PKL_ANAL_PAYLOAD->errors++;
PKL_PASS_ERROR;
}
+
+ if (PKL_ANAL_EXCOND && PKL_ANAL_EXCOND->nfunc == 0)
+ {
+ PKL_ERROR (PKL_AST_LOC (return_stmt),
+ "`return' statement without containing function in "
+ "EXCOND (?!) operator");
+ PKL_ANAL_PAYLOAD->errors++;
+ PKL_PASS_ERROR;
+ }
}
PKL_PHASE_END_HANDLER
@@ -759,7 +864,10 @@ struct pkl_phase pkl_phase_anal1 =
PKL_PHASE_PR_HANDLER (PKL_AST_PROGRAM, pkl_anal_pr_program),
PKL_PHASE_PS_HANDLER (PKL_AST_PROGRAM, pkl_anal_ps_program),
PKL_PHASE_PS_HANDLER (PKL_AST_STRUCT, pkl_anal1_ps_struct),
+ PKL_PHASE_PR_HANDLER (PKL_AST_COMP_STMT, pkl_anal1_pr_comp_stmt),
PKL_PHASE_PS_HANDLER (PKL_AST_COMP_STMT, pkl_anal1_ps_comp_stmt),
+ PKL_PHASE_PR_HANDLER (PKL_AST_LOOP_STMT, pkl_anal1_pr_loop_stmt),
+ PKL_PHASE_PS_HANDLER (PKL_AST_LOOP_STMT, pkl_anal1_ps_loop_stmt),
PKL_PHASE_PS_HANDLER (PKL_AST_BREAK_CONTINUE_STMT,
pkl_anal1_ps_break_continue_stmt),
PKL_PHASE_PS_HANDLER (PKL_AST_FUNCALL, pkl_anal1_ps_funcall),
PKL_PHASE_PR_HANDLER (PKL_AST_FUNC, pkl_anal1_pr_func),
diff --git a/libpoke/pkl-anal.h b/libpoke/pkl-anal.h
index 29c2dd47..e181a845 100644
--- a/libpoke/pkl-anal.h
+++ b/libpoke/pkl-anal.h
@@ -22,6 +22,19 @@
#include <config.h>
#include "pkl-pass.h"
+/* In order to disallow immediate break/continue/return statements
+ in EXCOND's compound statement, we need to identifiy them by tracking
+ function definitions, lambda definitions and loops inside them.
+
+ NFUNC and NLOOP are, respectively, the number of functions and loops
+ being visited inside EXCOND's compound statement. */
+
+struct pkl_anal_excond_ctx
+{
+ int nfunc;
+ int nloop;
+};
+
/* The following struct defines the payload of the analysis phases.
ERRORS is the number of errors detected while running the phase.
@@ -31,9 +44,16 @@
If NEXT_CONTEXT is 0 then we are not in any particular context
(NO_CONTEXT) otherwise NEXT_CONTEXT - 1 is the index of the current
- context in the array CONTEXT. */
+ context in the array CONTEXT.
+
+ EXCONDS is a stack of EXCOND's analysis data.
+
+ If NEXT_EXCOND is 0 then we are not in any compound statement of
+ EXCOND, otherwise NEXT_EXCOND - 1 is the index of the current
+ active entry in the array EXCONDS. */
#define PKL_ANAL_MAX_CONTEXT_NEST 32
+#define PKL_ANAL_MAX_COMP_STMT_NEST 32
#define PKL_ANAL_NO_CONTEXT 0
#define PKL_ANAL_CONTEXT_STRUCT_TYPE 1
@@ -44,6 +64,8 @@ struct pkl_anal_payload
int errors;
int context[PKL_ANAL_MAX_CONTEXT_NEST];
int next_context;
+ struct pkl_anal_excond_ctx exconds[PKL_ANAL_MAX_COMP_STMT_NEST];
+ int next_excond;
};
typedef struct pkl_anal_payload *pkl_anal_payload;
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index ae6fb924..91183eed 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -1392,7 +1392,10 @@ EXTRA_DIST = \
poke.pkl/excond-3.pk \
poke.pkl/excond-4.pk \
poke.pkl/excond-5.pk \
+ poke.pkl/excond-6.pk \
poke.pkl/excond-diag-1.pk \
+ poke.pkl/excond-diag-2.pk \
+ poke.pkl/excond-diag-3.pk \
poke.pkl/field-init-1.pk \
poke.pkl/field-init-2.pk \
poke.pkl/field-init-3.pk \
@@ -2310,11 +2313,11 @@ EXTRA_DIST = \
poke.pkl/return-3.pk \
poke.pkl/return-4.pk \
poke.pkl/return-5.pk \
- poke.pkl/return-6.pk \
poke.pkl/return-diag-1.pk \
poke.pkl/return-diag-2.pk \
poke.pkl/return-diag-3.pk \
poke.pkl/return-diag-4.pk \
+ poke.pkl/return-diag-5.pk \
poke.pkl/rla-int-1.pk \
poke.pkl/rla-offset-1.pk \
poke.pkl/rtrace-1.pk \
diff --git a/testsuite/poke.pkl/excond-6.pk b/testsuite/poke.pkl/excond-6.pk
new file mode 100644
index 00000000..3f588284
--- /dev/null
+++ b/testsuite/poke.pkl/excond-6.pk
@@ -0,0 +1,20 @@
+/* { dg-do run } */
+
+fun f = int:
+ {
+ var i = 0;
+
+ if ({
+ while (1)
+ break;
+ while (0)
+ continue;
+ i = lambda int: { return 1; } ();
+ } ?! E_elem)
+ return 2;
+
+ return i;
+ }
+
+/* { dg-command { f } } */
+/* { dg-output "1" } */
diff --git a/testsuite/poke.pkl/excond-diag-2.pk
b/testsuite/poke.pkl/excond-diag-2.pk
new file mode 100644
index 00000000..de355b17
--- /dev/null
+++ b/testsuite/poke.pkl/excond-diag-2.pk
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+
+while (1)
+ { break; } ?! E_elem; /* { dg-error "`break' statement without containing
statement in EXCOND" } */
diff --git a/testsuite/poke.pkl/excond-diag-3.pk
b/testsuite/poke.pkl/excond-diag-3.pk
new file mode 100644
index 00000000..a5257bcd
--- /dev/null
+++ b/testsuite/poke.pkl/excond-diag-3.pk
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+while (1)
+ {
+ if ({ continue; } ?! E_elem) /* { dg-error "`continue' statement without
containing statement in EXCOND" } */
+ continue;
+ else
+ break;
+ }
diff --git a/testsuite/poke.pkl/return-3.pk b/testsuite/poke.pkl/return-3.pk
index 5501d3d9..7e42e3c9 100644
--- a/testsuite/poke.pkl/return-3.pk
+++ b/testsuite/poke.pkl/return-3.pk
@@ -1,6 +1,6 @@
/* { dg-do run } */
-fun f = int: { { return 1; } ?! E_elem; return 0; }
+fun f = int: { var i = 0; { i = 1; } ?! E_elem; return i; }
-/* { dg-command {f} } */
-/* { dg-output "1" } */
+/* { dg-command {try { f; raise E_inval; } catch if E_inval { print "caught";
} } } */
+/* { dg-output "caught" } */
diff --git a/testsuite/poke.pkl/return-4.pk b/testsuite/poke.pkl/return-4.pk
index 178f2671..be8d1567 100644
--- a/testsuite/poke.pkl/return-4.pk
+++ b/testsuite/poke.pkl/return-4.pk
@@ -1,6 +1,6 @@
/* { dg-do run } */
-fun f = int: { { raise E_elem; return 1; } ?! E_elem; return 0; }
+fun f = int: { var i = 0; { raise E_elem; i = 1; } ?! E_elem; return i; }
/* { dg-command {f} } */
/* { dg-output "0" } */
diff --git a/testsuite/poke.pkl/return-5.pk b/testsuite/poke.pkl/return-5.pk
index 0396d823..b6859e7e 100644
--- a/testsuite/poke.pkl/return-5.pk
+++ b/testsuite/poke.pkl/return-5.pk
@@ -1,6 +1,6 @@
/* { dg-do run } */
-fun f = int: { { raise E_eof; return 1; } ?! E_elem; return 0; }
+fun f = int: { var i = 0; { raise E_eof; i = 1; } ?! E_elem; return i; }
/* { dg-command {try f; catch if E_eof { print "caught"; } } } */
/* { dg-output "caught" } */
diff --git a/testsuite/poke.pkl/return-6.pk b/testsuite/poke.pkl/return-6.pk
deleted file mode 100644
index d95ef78d..00000000
--- a/testsuite/poke.pkl/return-6.pk
+++ /dev/null
@@ -1,6 +0,0 @@
-/* { dg-do run } */
-
-fun f = int: { { return 1; } ?! E_elem; return 0; }
-
-/* { dg-command {try { f; raise E_inval; } catch if E_inval { print "caught";
} } } */
-/* { dg-output "caught" } */
diff --git a/testsuite/poke.pkl/return-diag-5.pk
b/testsuite/poke.pkl/return-diag-5.pk
new file mode 100644
index 00000000..d228b38f
--- /dev/null
+++ b/testsuite/poke.pkl/return-diag-5.pk
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+
+fun f = int: { { return 1; } ?! E_elem; return 0; } /* { dg-error "`return'
statement without containing function in EXCOND" } */
--
2.42.1
- [PATCH 1/9] utils: fix build and distribution of `pk-jojopatch', Mohammad-Reza Nabipoor, 2023/12/27
- [PATCH 3/9] pkl: disallow immediate break/continue/return in EXCOND,
Mohammad-Reza Nabipoor <=
- [PATCH 2/9] pkl: report location of constraint definition, Mohammad-Reza Nabipoor, 2023/12/27
- [PATCH 4/9] pkl,poke: trivial fixes, Mohammad-Reza Nabipoor, 2023/12/27
- [PATCH 7/9] pkl: add location info to synthesized assignment stmts, Mohammad-Reza Nabipoor, 2023/12/27