Skip to content

Commit b87c66d

Browse files
committed
Diagnose use of variable in its own initializer, closes #1028
Examples, now diagnosed: x: int = x; x: int = f(x); NOT diagnosed: x := :(x) f(x);
1 parent 61deed8 commit b87c66d

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

Diff for: source/parse.h

+22-9
Original file line numberDiff line numberDiff line change
@@ -2696,8 +2696,8 @@ struct declaration_node
26962696
bool member_function_generation = true;
26972697

26982698
// Cache some context
2699-
bool is_template_parameter = false;
2700-
bool is_parameter = false;
2699+
bool is_a_template_parameter = false;
2700+
bool is_a_parameter = false;
27012701

27022702
// Constructor
27032703
//
@@ -2707,6 +2707,19 @@ struct declaration_node
27072707

27082708
// API
27092709
//
2710+
2711+
auto is_template_parameter() const
2712+
-> bool
2713+
{
2714+
return is_a_template_parameter;
2715+
}
2716+
2717+
auto is_parameter() const
2718+
-> bool
2719+
{
2720+
return is_a_parameter;
2721+
}
2722+
27102723
auto type_member_mark_for_removal()
27112724
-> bool
27122725
{
@@ -5115,7 +5128,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
51155128
initializer.pop_back();
51165129
}
51175130
}
5118-
else if (!n.is_parameter) {
5131+
else if (!n.is_parameter()) {
51195132
initializer = ";";
51205133
}
51215134

@@ -5128,7 +5141,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
51285141
if (
51295142
!n.parent_is_function()
51305143
&& !n.parent_is_object()
5131-
&& !n.is_parameter
5144+
&& !n.is_parameter()
51325145
)
51335146
{
51345147
static declaration_node const* last_parent_type = {};
@@ -5144,7 +5157,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
51445157
ret += "\n";
51455158
}
51465159
}
5147-
if (!n.is_parameter && n.name()) {
5160+
if (!n.is_parameter() && n.name()) {
51485161
ret += std::string{"\n"} + pre(indent);
51495162
}
51505163

@@ -5159,7 +5172,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
51595172
ret += pretty_print_visualize(*n.identifier, indent);
51605173
}
51615174

5162-
if (n.is_parameter && (n.has_name("this") || n.has_name("that"))) {
5175+
if (n.is_parameter() && (n.has_name("this") || n.has_name("that"))) {
51635176
return ret;
51645177
}
51655178

@@ -5758,7 +5771,7 @@ class parser
57585771

57595772
//G postfix-expression:
57605773
//G primary-expression
5761-
//G postfix-expression postfix-operator [Note: without whitespace before the operator]
5774+
//G postfix-expression postfix-operator // Note: without whitespace before the operator
57625775
//G postfix-expression '[' expression-list? ','? ']'
57635776
//G postfix-expression '(' expression-list? ','? ')'
57645777
//G postfix-expression '.' id-expression
@@ -9074,8 +9087,8 @@ class parser
90749087
}
90759088

90769089
// Cache some context
9077-
n->is_template_parameter = is_template_parameter;
9078-
n->is_parameter = is_parameter;
9090+
n->is_a_template_parameter = is_template_parameter;
9091+
n->is_a_parameter = is_parameter;
90799092

90809093
return n;
90819094
}

Diff for: source/sema.h

+55-8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ struct identifier_sym {
8888
enum kind { use, using_declaration, deactivation } kind_ = use;
8989
bool standalone_assignment_to = false;
9090
bool is_captured = false;
91+
bool is_after_dot = false;
9192
bool safe_to_move = true;
9293
int safe_to_move_context = 0;
9394
token const* identifier = {};
@@ -97,10 +98,12 @@ struct identifier_sym {
9798
token const* id,
9899
kind k = use,
99100
bool mv = true,
100-
int mvc = 0
101+
int mvc = 0,
102+
bool after_dot = false
101103
)
102104
: kind_{k}
103105
, standalone_assignment_to{a}
106+
, is_after_dot{after_dot}
104107
, safe_to_move{mv}
105108
, safe_to_move_context{mvc}
106109
, identifier{id}
@@ -731,7 +734,8 @@ class sema
731734
find_definite_last_uses(
732735
decl->identifier,
733736
sympos,
734-
decl->parameter ? std::optional{decl->parameter->pass} : std::optional<passing_style>{}
737+
decl->parameter ? std::optional{decl->parameter->pass} : std::optional<passing_style>{},
738+
decl->parameter
735739
);
736740
}
737741
}
@@ -746,7 +750,8 @@ class sema
746750
auto find_definite_last_uses(
747751
token const* id,
748752
int pos,
749-
std::optional<passing_style> pass
753+
std::optional<passing_style> pass,
754+
bool is_parameter
750755
) const
751756
-> void
752757
{
@@ -883,12 +888,47 @@ class sema
883888
};
884889

885890
// Scan forward to the end of this scope
891+
auto found_end_of_our_initialization = false;
886892
for (auto start_depth = symbols[pos].depth;
887893
i < std::ssize(symbols)
888894
&& symbols[i].depth >= start_depth;
889895
++i
890896
)
891897
{
898+
// While we're here, if this is a non-parameter local, check for
899+
// any uses before the end of the initializer
900+
if (
901+
!is_parameter
902+
&& !found_end_of_our_initialization
903+
)
904+
{
905+
if (symbols[i].depth == start_depth)
906+
{
907+
if (auto decl = std::get_if<symbol::active::declaration>(&symbols[i].sym);
908+
decl
909+
&& decl->declaration->is_object()
910+
&& decl->declaration->has_name(*id)
911+
&& !decl->start
912+
)
913+
{
914+
found_end_of_our_initialization = true;
915+
}
916+
}
917+
918+
if (auto sym = std::get_if<symbol::active::identifier>(&symbols[i].sym);
919+
sym
920+
&& !sym->is_after_dot
921+
&& is_a_use(sym)
922+
)
923+
{
924+
assert(sym->identifier);
925+
errors.emplace_back(
926+
sym->identifier->position(),
927+
"local variable " + sym->identifier->to_string()
928+
+ " cannot be used in its own initializer");
929+
}
930+
}
931+
892932
if (
893933
skip_function_expression()
894934
|| skip_hidden_name(true)
@@ -1565,9 +1605,9 @@ class sema
15651605
if (n.identifier && *n.identifier->identifier == "this")
15661606
{
15671607
if (
1568-
n.is_template_parameter
1608+
n.is_template_parameter()
15691609
|| (
1570-
!n.is_parameter
1610+
!n.is_parameter()
15711611
&& !n.parent_is_type()
15721612
)
15731613
)
@@ -2430,12 +2470,19 @@ class sema
24302470
// or it's a 'copy' parameter (but to be a use it must be after
24312471
// the declaration, not the token in the decl's name itself)
24322472
// including an implicit 'this' access of a member name
2433-
auto push_use = [&](token const* u) {
2434-
this->push_use( identifier_sym( false, u, {}, !inside_next_expression, safe_to_move_context ) );
2473+
auto push_use = [&](token const* u, bool is_dot_access = false) {
2474+
this->push_use( identifier_sym(
2475+
false,
2476+
u,
2477+
{},
2478+
!inside_next_expression,
2479+
safe_to_move_context,
2480+
is_dot_access
2481+
) );
24352482
};
24362483
if (accessed_member_for_ufcs) {
24372484
if (t == "(") {
2438-
push_use(accessed_member_for_ufcs);
2485+
push_use(accessed_member_for_ufcs, true);
24392486
}
24402487
accessed_member_for_ufcs = nullptr;
24412488
}

0 commit comments

Comments
 (0)