Skip to content

Commit de27e96

Browse files
committed
Require definite first use initialization to be a standalone statement, closes #368
1 parent db63152 commit de27e96

File tree

4 files changed

+123
-30
lines changed

4 files changed

+123
-30
lines changed

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.2.1 Build 8811:0910
2+
cppfront compiler v0.2.1 Build 8812:0920
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"8811:0910"
1+
"8812:0920"

source/parse.h

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,19 @@ struct prefix_expression_node
263263
};
264264

265265

266+
struct expression_node;
267+
268+
266269
template<
267270
String Name,
268271
typename Term
269272
>
270273
struct binary_expression_node
271274
{
272-
std::unique_ptr<Term> expr;
275+
std::unique_ptr<Term> expr;
276+
expression_node const* my_expression = {};
277+
278+
binary_expression_node();
273279

274280
struct term
275281
{
@@ -280,6 +286,15 @@ struct binary_expression_node
280286

281287
// API
282288
//
289+
auto is_standalone_expression() const
290+
-> bool
291+
{
292+
return
293+
my_expression
294+
&& my_expression->is_standalone_expression()
295+
;
296+
}
297+
283298
auto terms_size() const
284299
-> int
285300
{
@@ -437,12 +452,29 @@ struct assignment_expression_lhs_rhs {
437452
};
438453

439454

455+
struct expression_statement_node;
456+
440457
struct expression_node
441458
{
459+
static inline std::vector<expression_node*> current_expressions = {};
460+
442461
std::unique_ptr<assignment_expression_node> expr;
462+
int num_subexpressions = 0;
463+
expression_statement_node const* my_statement = {};
464+
465+
expression_node();
443466

444467
// API
445468
//
469+
auto is_standalone_expression() const
470+
-> bool;
471+
472+
auto subexpression_count() const
473+
-> int
474+
{
475+
return num_subexpressions;
476+
}
477+
446478
auto is_identifier() const
447479
-> bool
448480
{
@@ -504,6 +536,17 @@ struct expression_node
504536
};
505537

506538

539+
template<
540+
String Name,
541+
typename Term
542+
>
543+
binary_expression_node<Name, Term>::binary_expression_node() {
544+
if (!expression_node::current_expressions.empty()) {
545+
my_expression = expression_node::current_expressions.back();
546+
}
547+
}
548+
549+
507550
enum class passing_style { in=0, copy, inout, out, move, forward, invalid };
508551
auto to_passing_style(token const& t) -> passing_style {
509552
if (t.type() == lexeme::Identifier) {
@@ -604,11 +647,20 @@ auto primary_expression_node::is_literal() const
604647

605648
struct expression_statement_node
606649
{
650+
static inline std::vector<expression_statement_node*> current_expression_statements = {};
651+
607652
std::unique_ptr<expression_node> expr;
608653
bool has_semicolon = false;
609654

610655
// API
611656
//
657+
auto subexpression_count() const
658+
-> int
659+
{
660+
assert (expr);
661+
return expr->subexpression_count();
662+
}
663+
612664
auto to_string() const
613665
-> std::string
614666
{
@@ -636,6 +688,24 @@ struct expression_statement_node
636688
};
637689

638690

691+
expression_node::expression_node()
692+
{
693+
if (!expression_statement_node::current_expression_statements.empty()) {
694+
my_statement = expression_statement_node::current_expression_statements.back();
695+
}
696+
}
697+
698+
699+
auto expression_node::is_standalone_expression() const
700+
-> bool
701+
{
702+
return
703+
my_statement
704+
&& my_statement->subexpression_count() == subexpression_count()
705+
;
706+
}
707+
708+
639709
struct capture {
640710
postfix_expression_node* capture_expr;
641711
std::string cap_sym = {};
@@ -4275,6 +4345,10 @@ class parser
42754345
return {};
42764346
}
42774347

4348+
for (auto& e : expression_node::current_expressions) {
4349+
e->num_subexpressions += std::ssize(n->ops);
4350+
}
4351+
42784352
return n;
42794353
}
42804354

@@ -4621,7 +4695,7 @@ class parser
46214695
);
46224696

46234697
if (ret && ret->terms_size() > 1) {
4624-
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'");
4698+
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'", false);
46254699
return {};
46264700
}
46274701

@@ -4635,7 +4709,7 @@ class parser
46354709
);
46364710

46374711
if (ret && ret->terms_size() > 1) {
4638-
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'");
4712+
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'", false);
46394713
return {};
46404714
}
46414715

@@ -4651,6 +4725,11 @@ class parser
46514725
-> std::unique_ptr<expression_node>
46524726
{
46534727
auto n = std::make_unique<expression_node>();
4728+
4729+
{
4730+
expression_node::current_expressions.push_back(n.get());
4731+
auto guard = finally([&]{ expression_node::current_expressions.pop_back(); });
4732+
46544733
if (!(n->expr = assignment_expression(allow_angle_operators))) {
46554734
return {};
46564735
}
@@ -4663,7 +4742,11 @@ class parser
46634742
error("'->' is not Cpp2 deference syntax - write '*.' instead");
46644743
return {};
46654744
}
4745+
}
46664746

4747+
for (auto& e : expression_node::current_expressions) {
4748+
++e->num_subexpressions;
4749+
}
46674750
return n;
46684751
}
46694752

@@ -5127,6 +5210,10 @@ class parser
51275210
-> std::unique_ptr<expression_statement_node>
51285211
{
51295212
auto n = std::make_unique<expression_statement_node>();
5213+
5214+
expression_statement_node::current_expression_statements.push_back(n.get());
5215+
auto guard = finally([&]{ expression_statement_node::current_expression_statements.pop_back(); });
5216+
51305217
if (!(n->expr = expression())) {
51315218
return {};
51325219
}
@@ -7233,9 +7320,11 @@ class parse_tree_printer : printing_visitor
72337320
o << pre(indent) << "literal" << "\n";
72347321
}
72357322

7236-
auto start(expression_node const&, int indent) -> void
7323+
auto start(expression_node const& n, int indent) -> void
72377324
{
7238-
o << pre(indent) << "expression\n";
7325+
o << pre(indent) << "expression - "
7326+
<< n.num_subexpressions << " subexpressions, my_statement ["
7327+
<< static_cast<void const*>(n.my_statement) << "]\n";
72397328
}
72407329

72417330
auto start(expression_list_node::term const&n, int indent) -> void
@@ -7272,9 +7361,9 @@ class parse_tree_printer : printing_visitor
72727361
o << pre(indent) << Name.value << "-expression\n";
72737362
}
72747363

7275-
auto start(expression_statement_node const&, int indent) -> void
7364+
auto start(expression_statement_node const& n, int indent) -> void
72767365
{
7277-
o << pre(indent) << "expression-statement\n";
7366+
o << pre(indent) << "expression-statement - [" << static_cast<void const*>(&n) << "]\n";
72787367
}
72797368

72807369
auto start(postfix_expression_node const&, int indent) -> void

source/sema.h

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ struct declaration_sym {
6262
};
6363

6464
struct identifier_sym {
65-
bool assignment_to = false;
65+
bool standalone_assignment_to = false;
6666
token const* identifier = {};
6767

6868
identifier_sym(
6969
bool a,
7070
token const* id
7171
)
72-
: assignment_to{a}
72+
: standalone_assignment_to{a}
7373
, identifier{id}
7474
{ }
7575

@@ -411,7 +411,7 @@ class sema
411411
o << "*** " << sym.identifier->position().to_string()
412412
<< " DEFINITE INITIALIZATION OF ";
413413
}
414-
else if (sym.assignment_to) {
414+
else if (sym.standalone_assignment_to) {
415415
o << "*** assignment to ";
416416
}
417417
else {
@@ -737,7 +737,7 @@ class sema
737737
// If we're not inside a selection statement, we're at the top level --
738738
// just return true if it's an assignment to it, else return false
739739
if (std::ssize(selection_stack) == 0) {
740-
if (sym.assignment_to) {
740+
if (sym.standalone_assignment_to) {
741741
definite_initializations.push_back( sym.identifier );
742742
}
743743
else {
@@ -746,7 +746,7 @@ class sema
746746
"local variable " + name
747747
+ " is used before it was initialized");
748748
}
749-
return sym.assignment_to;
749+
return sym.standalone_assignment_to;
750750
}
751751

752752
// Else if we're inside a selection statement but still in the condition
@@ -755,7 +755,7 @@ class sema
755755
// If this is a top-level selection statement, handle it the same as
756756
// if we weren't an a selection statement
757757
if (std::ssize(selection_stack) == 1) {
758-
if (sym.assignment_to) {
758+
if (sym.standalone_assignment_to) {
759759
definite_initializations.push_back( sym.identifier );
760760
}
761761
else {
@@ -764,14 +764,14 @@ class sema
764764
"local variable " + name
765765
+ " is used in a condition before it was initialized");
766766
}
767-
return sym.assignment_to;
767+
return sym.standalone_assignment_to;
768768
}
769769
// Else we can skip the rest of this selection statement, and record
770770
// this as the result of the next outer selection statement's current branch
771771
else {
772772
selection_stack.pop_back();
773773
assert (std::ssize(selection_stack.back().branches) > 0);
774-
selection_stack.back().branches.back().result = sym.assignment_to;
774+
selection_stack.back().branches.back().result = sym.standalone_assignment_to;
775775

776776
int this_depth = symbols[pos].depth;
777777
while (symbols[pos + 1].depth >= this_depth) {
@@ -783,7 +783,7 @@ class sema
783783
// Else we're in a selection branch and can skip the rest of this branch
784784
// and record this as the result for the current branch
785785
else {
786-
if (sym.assignment_to) {
786+
if (sym.standalone_assignment_to) {
787787
definite_initializations.push_back( sym.identifier );
788788
}
789789
else {
@@ -792,7 +792,7 @@ class sema
792792
"local variable " + name
793793
+ " is used in a branch before it was initialized");
794794
}
795-
selection_stack.back().branches.back().result = sym.assignment_to;
795+
selection_stack.back().branches.back().result = sym.standalone_assignment_to;
796796

797797
// The depth of this branch should always be the depth of
798798
// the current selection statement + 1
@@ -1450,13 +1450,13 @@ class sema
14501450
//-----------------------------------------------------------------------
14511451
// Visitor functions
14521452
//
1453-
int scope_depth = 0;
1454-
bool started_assignment_expression = false;
1455-
bool started_postfix_expression = false;
1456-
bool is_out_expression = false;
1457-
bool inside_parameter_list = false;
1458-
bool inside_returns_list = false;
1459-
bool just_entered_for = false;
1453+
int scope_depth = 0;
1454+
bool started_standalone_assignment_expression = false;
1455+
bool started_postfix_expression = false;
1456+
bool is_out_expression = false;
1457+
bool inside_parameter_list = false;
1458+
bool inside_returns_list = false;
1459+
bool just_entered_for = false;
14601460
parameter_declaration_node const* inside_out_parameter = {};
14611461

14621462
auto start(parameter_declaration_list_node const&, int) -> void
@@ -1588,10 +1588,10 @@ class sema
15881588

15891589
// If this is the first identifier since we started a new assignment,
15901590
// expression, then it's the left-hand side (target) of the assignment
1591-
else if (started_assignment_expression)
1591+
else if (started_standalone_assignment_expression)
15921592
{
15931593
symbols.emplace_back( scope_depth, identifier_sym( true, &t ) );
1594-
started_assignment_expression = false;
1594+
started_standalone_assignment_expression = false; // we were the consumer for this information
15951595
}
15961596

15971597
// If this is the first identifier since we saw an `out` expression,
@@ -1681,10 +1681,14 @@ class sema
16811681

16821682
auto start(assignment_expression_node const& n, int)
16831683
{
1684-
if (std::ssize(n.terms) > 0) {
1684+
if (
1685+
n.is_standalone_expression()
1686+
&& std::ssize(n.terms) > 0
1687+
)
1688+
{
16851689
assert (n.terms.front().op);
16861690
if (n.terms.front().op->type() == lexeme::Assignment) {
1687-
started_assignment_expression = true;
1691+
started_standalone_assignment_expression = true;
16881692
}
16891693
}
16901694
}

0 commit comments

Comments
 (0)