[m-dev.] for review: fix MLDS static ground term bugs
Fergus Henderson
fjh at cs.mu.OZ.AU
Sun Oct 22 18:10:02 AEDT 2000
On 22-Oct-2000, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> This change fixes several closely related bugs where the MLDS compiler
> would generatic static ground terms whose initializers referred to
> variables which weren't in scope. Unfortunately the fix isn't
> yet complete: AFAIK it fixes all such problems in the MLDS generated
> by the code generator, but the ml_elim_nested pass that follows still
> screws things up.
OK, now I've fixed ml_elim_nested too.
If they pass the bootcheck, I'll go ahead and commit these changes.
I've enclosed below a new full diff and a relative diff.
******* FULL DIFF *******************************************
Estimated hours taken: 16
Fix some bugs in the static ground term optimization for the MLDS
back-end.
compiler/ml_code_util.m:
compiler/ml_code_gen.m:
Ensure that declarations for a goal are generated in such a
way that they scope over the C code generated for any
following goals. This is needed to ensure that we don't
generate references to undeclared names in static constants.
compiler/ml_unify_gen.m:
compiler/ml_code_util.m:
Ensure that all static consts get unique names, so that
ml_elim_nested.m can hoist them to the top level.
Also move ml_gen_static_const_decl_flags from ml_unify_gen.m
to ml_code_util.m, for use by ml_code_gen.m.
compiler/ml_elim_nested.m:
Hoist out the definitions of static constants to the top level
in cases where they might be referenced from nested functions.
Also change the name of the local_vars field of the ml_elim_info
to local_data, to make it clear that it can hold constants too.
compiler/mark_static_terms.m:
Fix some typos in the comments.
tests/valid/Mmakefile:
tests/valid/static.m:
Add some regression tests.
compiler/mlds_to_c.m:
Fix an XXX: it was not outputting `static' in the right places.
Workspace: /home/pgrad/fjh/ws/hg
Index: compiler/mark_static_terms.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mark_static_terms.m,v
retrieving revision 1.3
diff -u -d -r1.3 mark_static_terms.m
--- compiler/mark_static_terms.m 2000/10/06 10:18:25 1.3
+++ compiler/mark_static_terms.m 2000/10/21 11:41:00
@@ -67,16 +67,16 @@
conj_mark_static_terms(Goals0, Goals, SI0, SI).
goal_expr_mark_static_terms(disj(Goals0, B), disj(Goals, B), SI0, SI0) :-
- % we rever to the original static_info at the end of branched goals
+ % we revert to the original static_info at the end of branched goals
disj_mark_static_terms(Goals0, Goals, SI0).
goal_expr_mark_static_terms(switch(A, B, Cases0, D), switch(A, B, Cases, D),
SI0, SI0) :-
- % we rever to the original static_info at the end of branched goals
+ % we revert to the original static_info at the end of branched goals
cases_mark_static_terms(Cases0, Cases, SI0).
goal_expr_mark_static_terms(not(Goal0), not(Goal), SI0, SI0) :-
- % we rever to the original static_info at the end of the negation
+ % we revert to the original static_info at the end of the negation
goal_mark_static_terms(Goal0, Goal, SI0, _SI).
goal_expr_mark_static_terms(some(A, B, Goal0), some(A, B, Goal), SI0, SI) :-
Index: compiler/ml_code_gen.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/ml_code_gen.m,v
retrieving revision 1.60
diff -u -d -r1.60 ml_code_gen.m
--- compiler/ml_code_gen.m 2000/10/14 04:00:14 1.60
+++ compiler/ml_code_gen.m 2000/10/22 05:19:21
@@ -83,6 +83,25 @@
% means that in the situation described by [situation],
% for the the specified [construct] we will generate the specified [code].
+% There is one other important thing which can be considered part of the
+% calling convention for the code that we generate for each goal.
+% If static ground term optimization is enabled, then for the terms
+% marked as static by mark_static_terms.m, we will generate static consts.
+% These static consts can refer to other static consts defined earlier.
+% We need to be careful about the scopes of variables to ensure that
+% for any term that mark_static_terms.m marks as static, the C constants
+% representing the arguments of that term are in scope at the point
+% where that term is constructed. Basically this means that
+% all the static consts generated inside a goal must be hoist out to
+% the top level scope for that goal, except for goal types where
+% goal_expr_mark_static_terms (in mark_static_terms.m) returns the
+% same static_info unchanged, i.e. branched goals and negations.
+%
+% Handling static constants also requires that the calls to ml_gen_goal
+% for each subgoal must be done in the right order, so that the
+% const_num_map in the ml_gen_info holds the right sequence numbers
+% for the constants in scope.
+
%-----------------------------------------------------------------------------%
%
% Code for wrapping goals
@@ -106,12 +125,10 @@
% semi goal in nondet context:
% <Goal && SUCCEED()>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Goal>
% if (succeeded) SUCCEED();
-% }
%-----------------------------------------------------------------------------%
%
@@ -244,6 +261,10 @@
% <Goal && success()>
% }
+% Note that for all of these versions, we must hoist any static declarations
+% generated for <Goal> out to the top level; this is needed so that such
+% declarations remain in scope for any following goals.
+
%-----------------------------------------------------------------------------%
%
% Code for empty conjunctions (`true')
@@ -289,12 +310,12 @@
% <Goals>
% If the first goal is model_semidet, then there are two cases:
-% if the disj as a whole is semidet, things are simple, and
-% if the disj as a whole is model_non, then we do the same as
+% if the conj as a whole is semidet, things are simple, and
+% if the conj as a whole is model_non, then we do the same as
% for the semidet case, except that we also (ought to) declare
% a local `succeeded' variable.
%
-% model_semi Goal in model_semi disj:
+% model_semi Goal in model_semi conj:
% <succeeded = (Goal, Goals)>
% ===>
% <succeeded = Goal>;
@@ -302,17 +323,22 @@
% <Goals>;
% }
%
-% model_semi Goal in model_non disj:
+% model_semi Goal in model_non conj:
% <Goal && Goals>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Goal>;
% if (succeeded) {
% <Goals>;
% }
-% }
+%
+% The actual code generation scheme we use is slightly
+% different to that: we hoist any declarations generated
+% for <Goals> to the outer scope, rather than keeping
+% them inside the `if', so that they remain in
+% scope for any later goals which follow this.
+% This is needed for declarations of static consts.
% For model_non goals, there are a couple of different
% ways that we could generate code, depending on whether
@@ -325,7 +351,6 @@
% model_non Goal (optimized for readability)
% <Goal, Goals>
% ===>
-% {
% entry_func() {
% <Goal && succ_func()>;
% }
@@ -334,7 +359,6 @@
% }
%
% entry_func();
-% }
%
% The more efficient method generates the goals in
% reverse order, so it's less readable, but it has fewer
@@ -344,13 +368,11 @@
% model_non Goal (optimized for efficiency):
% <Goal, Goals>
% ===>
-% {
% succ_func() {
% <Goals && SUCCEED()>;
% }
%
% <Goal && succ_func()>;
-% }
%
% The more efficient method is the one we actually use.
%
@@ -360,7 +382,6 @@
% model_non goals (optimized for readability):
% <Goal1, Goal2, Goal3, Goals>
% ===>
-% {
% label0_func() {
% <Goal1 && label1_func()>;
% }
@@ -375,12 +396,10 @@
% }
%
% label0_func();
-% }
%
% model_non goals (optimized for efficiency):
% <Goal1, Goal2, Goal3, Goals>
% ===>
-% {
% label1_func() {
% label2_func() {
% label3_func() {
@@ -391,7 +410,6 @@
% <Goal2 && label2_func()>;
% }
% <Goal1 && label1_func()>;
-% }
%
% Note that it might actually make more sense to generate
% conjunctions of nondet goals like this:
@@ -399,7 +417,6 @@
% model_non goals (optimized for efficiency, alternative version):
% <Goal1, Goal2, Goal3, Goals>
% ===>
-% {
% label3_func() {
% <Goals && SUCCEED()>;
% }
@@ -411,13 +428,21 @@
% }
%
% <Goal1 && label1_func()>;
-% }
%
% This would avoid the undesirable deep nesting that we sometimes get
% with our current scheme. However, if we're eliminating nested
% functions, as is normally the case, then after the ml_elim_nested
% transformation all the functions and variables have been hoisted
% to the top level, so there is no difference between these two.
+%
+% As with semidet conjunctions, we hoist declarations
+% out so that they remain in scope for any following goals.
+% This is needed for declarations of static consts.
+% However, we want to keep the declarations of non-static
+% variables local, since accessing local variables is more
+% efficient that accessing variables in the environment
+% from a nested function. So we only hoist declarations
+% of static constants.
%-----------------------------------------------------------------------------%
%
@@ -450,32 +475,27 @@
% model_semi Goal:
% <do (Goal ; Goals)>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Goal>;
% if (!succeeded) {
% <do Goals>;
% }
-% }
% model_semi disj:
% model_det Goal:
% <succeeded = (Goal ; Goals)>
% ===>
-% {
% bool succeeded;
%
% <do Goal>
% succeeded = TRUE
% /* <Goals> will never be reached */
-% }
% model_semi Goal:
% <succeeded = (Goal ; Goals)>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Goal>;
@@ -495,13 +515,11 @@
% model_semi Goal:
% <(Goal ; Goals) && SUCCEED()>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Goal>
% if (succeeded) SUCCEED();
% <Goals && SUCCEED()>
-% }
%
% model_non Goal:
% <(Goal ; Goals) && SUCCEED()>
@@ -517,7 +535,6 @@
% model_semi Cond:
% <(Cond -> Then ; Else)>
% ===>
-% {
% bool succeeded;
%
% <succeeded = Cond>
@@ -526,7 +543,6 @@
% } else {
% <Else>
% }
-% }
% /*
% ** XXX The following transformation does not do as good a job of GC
@@ -538,7 +554,6 @@
% model_non Cond:
% <(Cond -> Then ; Else)>
% ===>
-% {
% bool cond_<N>;
%
% void then_func() {
@@ -551,7 +566,10 @@
% if (!cond_<N>) {
% <Else>
% }
-% }
+% except that we hoist any declarations generated
+% for <Cond> to the top of the scope, so that they
+% are in scope for the <Then> goal
+% (this is needed for declarations of static consts)
%-----------------------------------------------------------------------------%
%
@@ -561,12 +579,10 @@
% model_det negation
% <not(Goal)>
% ===>
-% {
% bool succeeded;
% <succeeded = Goal>
% /* now ignore the value of succeeded,
% which we know will be FALSE */
-% }
% model_semi negation, model_det Goal:
% <succeeded = not(Goal)>
@@ -1349,12 +1365,10 @@
% semi goal in nondet context:
% <Goal && SUCCEED()>
% ===>
- % {
% bool succeeded;
%
% <succeeded = Goal>
% if (succeeded) SUCCEED()
- % }
%
ml_gen_test_success(Succeeded),
ml_gen_call_current_success_cont(Context, CallCont),
@@ -1383,6 +1397,7 @@
ml_gen_commit(Goal, CodeModel, Context, MLDS_Decls, MLDS_Statements) -->
{ Goal = _ - GoalInfo },
{ goal_info_get_code_model(GoalInfo, GoalCodeModel) },
+ { goal_info_get_context(GoalInfo, GoalContext) },
( { GoalCodeModel = model_non, CodeModel = model_semi } ->
@@ -1434,7 +1449,13 @@
{ SuccessCont = success_cont(SuccessFuncLabelRval,
EnvPtrRval, [], []) },
ml_gen_info_push_success_cont(SuccessCont),
- ml_gen_goal(model_non, Goal, GoalStatement),
+ ml_gen_goal(model_non, Goal, GoalDecls, GoalStatements),
+ % hoist any static constant declarations for Goal
+ % out to the top level
+ { list__filter(ml_decl_is_static_const, GoalDecls,
+ GoalStaticDecls, GoalOtherDecls) },
+ { GoalStatement = ml_gen_block(GoalOtherDecls,
+ GoalStatements, GoalContext) },
ml_gen_info_pop_success_cont,
ml_gen_set_success(const(false), Context, SetSuccessFalse),
ml_gen_set_success(const(true), Context, SetSuccessTrue),
@@ -1446,7 +1467,8 @@
{ TryCommitStatement = mlds__statement(TryCommitStmt,
MLDS_Context) },
- { MLDS_Decls = [CommitRefDecl, SuccessFunc | LocalVarDecls] },
+ { MLDS_Decls = list__append([CommitRefDecl,
+ SuccessFunc | LocalVarDecls], GoalStaticDecls) },
{ MLDS_Statements = [TryCommitStatement] },
ml_gen_info_set_var_lvals(OrigVarLvalMap)
@@ -1496,7 +1518,13 @@
{ SuccessCont = success_cont(SuccessFuncLabelRval,
EnvPtrRval, [], []) },
ml_gen_info_push_success_cont(SuccessCont),
- ml_gen_goal(model_non, Goal, GoalStatement),
+ ml_gen_goal(model_non, Goal, GoalDecls, GoalStatements),
+ % hoist any static constant declarations for Goal
+ % out to the top level
+ { list__filter(ml_decl_is_static_const, GoalDecls,
+ GoalStaticDecls, GoalOtherDecls) },
+ { GoalStatement = ml_gen_block(GoalOtherDecls,
+ GoalStatements, GoalContext) },
ml_gen_info_pop_success_cont,
{ TryCommitStmt = try_commit(CommitRefLval, GoalStatement,
@@ -1504,7 +1532,8 @@
{ TryCommitStatement = mlds__statement(TryCommitStmt,
MLDS_Context) },
- { MLDS_Decls = [CommitRefDecl, SuccessFunc | LocalVarDecls] },
+ { MLDS_Decls = list__append([CommitRefDecl,
+ SuccessFunc | LocalVarDecls], GoalStaticDecls) },
{ MLDS_Statements = [TryCommitStatement] },
ml_gen_info_set_var_lvals(OrigVarLvalMap)
@@ -2506,7 +2535,6 @@
% model_semi cond:
% <(Cond -> Then ; Else)>
% ===>
- % {
% bool succeeded;
%
% <succeeded = Cond>
@@ -2515,7 +2543,6 @@
% } else {
% <Else>
% }
- % }
{ CondCodeModel = model_semi },
ml_gen_goal(model_semi, Cond, CondDecls, CondStatements),
ml_gen_test_success(Succeeded),
@@ -2541,7 +2568,6 @@
% model_non cond:
% <(Cond -> Then ; Else)>
% ===>
- % {
% bool cond_<N>;
%
% void then_func() {
@@ -2554,17 +2580,33 @@
% if (!cond_<N>) {
% <Else>
% }
- % }
+ % except that we hoist any declarations generated
+ % for <Cond> to the top of the scope, so that they
+ % are in scope for the <Then> goal
+ % (this is needed for declarations of static consts)
+
{ CondCodeModel = model_non },
- % generate the `cond_<N>' var
+ % generate the `cond_<N>' var and the code to initialize it to false
ml_gen_info_new_cond_var(CondVar),
{ MLDS_Context = mlds__make_context(Context) },
{ CondVarDecl = ml_gen_cond_var_decl(CondVar, MLDS_Context) },
+ ml_gen_set_cond_var(CondVar, const(false), Context,
+ SetCondFalse),
- % generate the `then_func'
+ % allocate a name for the `then_func'
ml_gen_new_func_label(no, ThenFuncLabel, ThenFuncLabelRval),
+
+ % generate <Cond && then_func()>
+ ml_get_env_ptr(EnvPtrRval),
+ { SuccessCont = success_cont(ThenFuncLabelRval, EnvPtrRval,
+ [], []) },
+ ml_gen_info_push_success_cont(SuccessCont),
+ ml_gen_goal(model_non, Cond, CondDecls, CondStatements),
+ ml_gen_info_pop_success_cont,
+
+ % generate the `then_func'
/* push nesting level */
{ Then = _ - ThenGoalInfo },
{ goal_info_get_context(ThenGoalInfo, ThenContext) },
@@ -2577,15 +2619,7 @@
ml_gen_nondet_label_func(ThenFuncLabel, ThenContext,
ThenFuncBody, ThenFunc),
- % generate the main body
- ml_gen_set_cond_var(CondVar, const(false), Context,
- SetCondFalse),
- ml_get_env_ptr(EnvPtrRval),
- { SuccessCont = success_cont(ThenFuncLabelRval, EnvPtrRval,
- [], []) },
- ml_gen_info_push_success_cont(SuccessCont),
- ml_gen_goal(model_non, Cond, CondDecls, CondStatements),
- ml_gen_info_pop_success_cont,
+ % generate `if (!cond_<N>) { <Else> }'
ml_gen_test_cond_var(CondVar, CondSucceeded),
ml_gen_goal(CodeModel, Else, ElseStatement),
{ IfStmt = if_then_else(unop(std_unop(not), CondSucceeded),
@@ -2593,7 +2627,7 @@
{ IfStatement = mlds__statement(IfStmt, MLDS_Context) },
% package it all up in the right order
- { MLDS_Decls = [CondVarDecl, ThenFunc | CondDecls] },
+ { MLDS_Decls = list__append([CondVarDecl | CondDecls], [ThenFunc]) },
{ MLDS_Statements = list__append(
[SetCondFalse | CondStatements], [IfStatement]) }
).
Index: compiler/ml_code_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/ml_code_util.m,v
retrieving revision 1.23
diff -u -d -r1.23 ml_code_util.m
--- compiler/ml_code_util.m 2000/10/14 04:00:16 1.23
+++ compiler/ml_code_util.m 2000/10/22 05:09:37
@@ -241,6 +241,21 @@
%-----------------------------------------------------------------------------%
%
+% Routines for dealing with static constants
+%
+
+ % Return the declaration flags appropriate for an
+ % initialized local static constant.
+ %
+:- func ml_static_const_decl_flags = mlds__decl_flags.
+
+ % Succeed iff the specified mlds__defn defines
+ % a static constant.
+ %
+:- pred ml_decl_is_static_const(mlds__defn::in) is semidet.
+
+%-----------------------------------------------------------------------------%
+%
% Routines for dealing with fields
%
@@ -508,6 +523,26 @@
ml_gen_info, ml_gen_info).
:- mode ml_gen_info_new_conv_var(out, in, out) is det.
+ % Generate a new `const' sequence number.
+ % This is used to give unique names to the local constants
+ % generated for --static-ground-terms.
+:- type const_seq == int.
+:- pred ml_gen_info_new_const(const_seq,
+ ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_new_const(out, in, out) is det.
+
+ % Set the `const' sequence number
+ % corresponding to a given HLDS variable.
+ %
+:- pred ml_gen_info_set_const_num(prog_var, const_seq, ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_set_const_num(in, in, in, out) is det.
+
+ % Lookup the `const' sequence number
+ % corresponding to a given HLDS variable.
+ %
+:- pred ml_gen_info_lookup_const_num(prog_var, const_seq, ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_lookup_const_num(in, out, in, out) is det.
+
%
% A success continuation specifies the (rval for the variable
% holding the address of the) function that a nondet procedure
@@ -674,52 +709,58 @@
% model_semi goal:
% <Goal, Goals>
% ===>
- % {
% bool succeeded;
%
% <succeeded = Goal>;
% if (succeeded) {
% <Goals>;
% }
- % }
+ % except that we hoist any declarations generated
+ % for <Goals> to the outer scope, rather than
+ % inside the `if', so that they remain in
+ % scope for any later goals which follow this
+ % (this is needed for declarations of static consts)
{ FirstCodeModel = model_semi },
DoGenFirst(FirstDecls, FirstStatements),
ml_gen_test_success(Succeeded),
DoGenRest(RestDecls, RestStatements),
- { IfBody = ml_gen_block(RestDecls, RestStatements, Context) },
+ { IfBody = ml_gen_block([], RestStatements, Context) },
{ IfStmt = if_then_else(Succeeded, IfBody, no) },
{ IfStatement = mlds__statement(IfStmt,
mlds__make_context(Context)) },
- { MLDS_Decls = FirstDecls },
+ { MLDS_Decls = list__append(FirstDecls, RestDecls) },
{ MLDS_Statements = list__append(FirstStatements,
[IfStatement]) }
;
% model_non goal:
% <First, Rest>
% ===>
- % {
% succ_func() {
% <Rest && SUCCEED()>;
% }
%
% <First && succ_func()>;
- % }
+ % except that we hoist any declarations generated
+ % for <First> and any _static_ declarations generated
+ % for <Rest> to the top of the scope, rather
+ % than inside or after the succ_func(), so that they
+ % remain in scope for any code following them
+ % (this is needed for declarations of static consts).
%
- % XXX this leads to deep nesting for long conjunctions;
- % we should avoid that.
+ % We take care to only hoist _static_ declarations
+ % outside nested functions, since access to non-local
+ % variables is less efficient.
+ %
+ % XXX The pattern above leads to deep nesting for long
+ % conjunctions; we should avoid that.
+ %
{ FirstCodeModel = model_non },
- % generate the `succ_func'
+ % allocate a name for the `succ_func'
ml_gen_new_func_label(no, RestFuncLabel, RestFuncLabelRval),
- /* push nesting level */
- DoGenRest(RestDecls, RestStatements),
- { RestStatement = ml_gen_block(RestDecls, RestStatements,
- Context) },
- /* pop nesting level */
- ml_gen_nondet_label_func(RestFuncLabel, Context, RestStatement,
- RestFunc),
+ % generate <First && succ_func()>
ml_get_env_ptr(EnvPtrRval),
{ SuccessCont = success_cont(RestFuncLabelRval,
EnvPtrRval, [], []) },
@@ -727,12 +768,30 @@
DoGenFirst(FirstDecls, FirstStatements),
ml_gen_info_pop_success_cont,
- % it might be better to put the decls in the other order:
- /* { MLDS_Decls = list__append(FirstDecls, [RestFunc]) }, */
- { MLDS_Decls = [RestFunc | FirstDecls] },
+ % generate the `succ_func'
+ /* push nesting level */
+ DoGenRest(RestDecls, RestStatements),
+ { list__filter(ml_decl_is_static_const, RestDecls,
+ RestStaticDecls, RestOtherDecls) },
+ { RestStatement = ml_gen_block(RestOtherDecls, RestStatements,
+ Context) },
+ /* pop nesting level */
+ ml_gen_nondet_label_func(RestFuncLabel, Context, RestStatement,
+ RestFunc),
+
+ { MLDS_Decls = list__condense(
+ [FirstDecls, RestStaticDecls, [RestFunc]]) },
{ MLDS_Statements = FirstStatements }
).
+ % Succeed iff the specified mlds__defn defines
+ % a static constant.
+ %
+ml_decl_is_static_const(Defn) :-
+ Defn = mlds__defn(Name, _Context, Flags, _DefnBody),
+ Name = data(var(_)),
+ Flags = ml_static_const_decl_flags.
+
% Given a function label and the statement which will comprise
% the function body for that function, generate an mlds__defn
% which defines that function.
@@ -1211,6 +1270,7 @@
MLDS_Defn = mlds__defn(Name, Context, DeclFlags, Defn).
% Return the declaration flags appropriate for a local variable.
+ %
:- func ml_gen_var_decl_flags = mlds__decl_flags.
ml_gen_var_decl_flags = MLDS_DeclFlags :-
Access = public,
@@ -1222,6 +1282,19 @@
MLDS_DeclFlags = init_decl_flags(Access, PerInstance,
Virtuality, Finality, Constness, Abstractness).
+ % Return the declaration flags appropriate for an
+ % initialized local static constant.
+ %
+ml_static_const_decl_flags = MLDS_DeclFlags :-
+ Access = private,
+ PerInstance = one_copy,
+ Virtuality = non_virtual,
+ Finality = overridable,
+ Constness = const,
+ Abstractness = concrete,
+ MLDS_DeclFlags = init_decl_flags(Access, PerInstance,
+ Virtuality, Finality, Constness, Abstractness).
+
%-----------------------------------------------------------------------------%
%
% Code for dealing with fields
@@ -1532,7 +1605,7 @@
% The `ml_gen_info' type holds information used during MLDS code generation
% for a given procedure.
%
-% Only the `func_label', `commit_label', `cond_var', `conv_var',
+% Only the `func_label', `commit_label', `cond_var', `conv_var', `const_num',
% `var_lvals', `success_cont_stack', and `extra_defns' fields are mutable;
% the others are set when the `ml_gen_info' is created and then never
% modified.
@@ -1560,6 +1633,8 @@
commit_label :: commit_sequence_num,
cond_var :: cond_seq,
conv_var :: conv_seq,
+ const_num :: const_seq,
+ const_num_map :: map(prog_var, const_seq),
success_cont_stack :: stack(success_cont),
% a partial mapping from vars to lvals,
% used to override the normal lval
@@ -1586,6 +1661,8 @@
CommitLabelCounter = 0,
CondVarCounter = 0,
ConvVarCounter = 0,
+ ConstCounter = 0,
+ map__init(ConstNumMap),
stack__init(SuccContStack),
map__init(VarLvals),
ExtraDefns = [],
@@ -1601,6 +1678,8 @@
CommitLabelCounter,
CondVarCounter,
ConvVarCounter,
+ ConstCounter,
+ ConstNumMap,
SuccContStack,
VarLvals,
ExtraDefns
@@ -1644,6 +1723,15 @@
ml_gen_info_new_conv_var(ConvVar, Info, Info^conv_var := ConvVar) :-
ConvVar = Info^conv_var + 1.
+
+ml_gen_info_new_const(ConstVar, Info, Info^const_num := ConstVar) :-
+ ConstVar = Info^const_num + 1.
+
+ml_gen_info_set_const_num(Var, ConstVar, Info,
+ Info^const_num_map := map__set(Info^const_num_map, Var, ConstVar)).
+
+ml_gen_info_lookup_const_num(Var, ConstVar, Info, Info) :-
+ ConstVar = map__lookup(Info^const_num_map, Var).
ml_gen_info_push_success_cont(SuccCont, Info,
Info^success_cont_stack :=
Index: compiler/mlds_to_c.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mlds_to_c.m,v
retrieving revision 1.58
diff -u -d -r1.58 mlds_to_c.m
--- compiler/mlds_to_c.m 2000/10/06 10:18:26 1.58
+++ compiler/mlds_to_c.m 2000/10/22 02:26:12
@@ -759,14 +759,7 @@
% Now output the declaration for this mlds__defn.
%
mlds_indent(Context, Indent),
- ( { Name = data(_) } ->
- % XXX for private data and private functions,
- % we should use "static"
- io__write_string("extern ")
- ;
- []
- ),
- mlds_output_decl_flags(Flags),
+ mlds_output_decl_flags(Flags, forward_decl, Name),
mlds_output_decl_body(Indent, qual(ModuleName, Name), Context,
DefnBody)
).
@@ -846,7 +839,7 @@
[]
),
mlds_indent(Context, Indent),
- mlds_output_decl_flags(Flags),
+ mlds_output_decl_flags(Flags, definition, Name),
mlds_output_defn_body(Indent, qual(ModuleName, Name), Context,
DefnBody).
@@ -1646,41 +1639,88 @@
% Code to output declaration specifiers
%
-:- pred mlds_output_decl_flags(mlds__decl_flags, io__state, io__state).
-:- mode mlds_output_decl_flags(in, di, uo) is det.
+:- type decl_or_defn
+ ---> forward_decl
+ ; definition.
-mlds_output_decl_flags(Flags) -->
- mlds_output_access(access(Flags)),
- mlds_output_per_instance(per_instance(Flags)),
+:- pred mlds_output_decl_flags(mlds__decl_flags, decl_or_defn,
+ mlds__entity_name, io__state, io__state).
+:- mode mlds_output_decl_flags(in, in, in, di, uo) is det.
+
+mlds_output_decl_flags(Flags, DeclOrDefn, Name) -->
+ %
+ % mlds_output_extern_or_static handles both the
+ % `access' and the `per_instance' fields of the mlds__decl_flags.
+ % We have to handle them together because C overloads `static'
+ % to mean both `private' and `one_copy', rather than having
+ % separate keywords for each. To make it clear which MLDS
+ % construct each `static' keyword means, we precede the `static'
+ % without (optionally-enabled) comments saying whether it is
+ % `private', `one_copy', or both.
+ %
+ mlds_output_access_comment(access(Flags)),
+ mlds_output_per_instance_comment(per_instance(Flags)),
+ mlds_output_extern_or_static(access(Flags), per_instance(Flags),
+ DeclOrDefn, Name),
mlds_output_virtuality(virtuality(Flags)),
mlds_output_finality(finality(Flags)),
mlds_output_constness(constness(Flags)),
mlds_output_abstractness(abstractness(Flags)).
-:- pred mlds_output_access(access, io__state, io__state).
-:- mode mlds_output_access(in, di, uo) is det.
+:- pred mlds_output_access_comment(access, io__state, io__state).
+:- mode mlds_output_access_comment(in, di, uo) is det.
-mlds_output_access(Access) -->
+mlds_output_access_comment(Access) -->
globals__io_lookup_bool_option(auto_comments, Comments),
( { Comments = yes } ->
- mlds_output_access_2(Access)
+ mlds_output_access_comment_2(Access)
;
[]
).
-:- pred mlds_output_access_2(access, io__state, io__state).
-:- mode mlds_output_access_2(in, di, uo) is det.
+:- pred mlds_output_access_comment_2(access, io__state, io__state).
+:- mode mlds_output_access_comment_2(in, di, uo) is det.
-mlds_output_access_2(public) --> [].
-mlds_output_access_2(private) --> io__write_string("/* private: */ ").
-mlds_output_access_2(protected) --> io__write_string("/* protected: */ ").
-mlds_output_access_2(default) --> io__write_string("/* default access */ ").
+mlds_output_access_comment_2(public) --> [].
+mlds_output_access_comment_2(private) --> io__write_string("/* private: */ ").
+mlds_output_access_comment_2(protected) --> io__write_string("/* protected: */ ").
+mlds_output_access_comment_2(default) --> io__write_string("/* default access */ ").
-:- pred mlds_output_per_instance(per_instance, io__state, io__state).
-:- mode mlds_output_per_instance(in, di, uo) is det.
+:- pred mlds_output_per_instance_comment(per_instance, io__state, io__state).
+:- mode mlds_output_per_instance_comment(in, di, uo) is det.
-mlds_output_per_instance(one_copy) --> io__write_string("static ").
-mlds_output_per_instance(per_instance) --> [].
+mlds_output_per_instance_comment(PerInstance) -->
+ globals__io_lookup_bool_option(auto_comments, Comments),
+ ( { Comments = yes } ->
+ mlds_output_per_instance_comment_2(PerInstance)
+ ;
+ []
+ ).
+
+:- pred mlds_output_per_instance_comment_2(per_instance, io__state, io__state).
+:- mode mlds_output_per_instance_comment_2(in, di, uo) is det.
+
+mlds_output_per_instance_comment_2(per_instance) --> [].
+mlds_output_per_instance_comment_2(one_copy) --> io__write_string("/* one_copy */ ").
+
+:- pred mlds_output_extern_or_static(access, per_instance, decl_or_defn,
+ mlds__entity_name, io__state, io__state).
+:- mode mlds_output_extern_or_static(in, in, in, in, di, uo) is det.
+
+mlds_output_extern_or_static(Access, PerInstance, DeclOrDefn, Name) -->
+ (
+ { Access = private ; PerInstance = one_copy },
+ { Name \= type(_, _) }
+ ->
+ io__write_string("static ")
+ ;
+ { DeclOrDefn = forward_decl },
+ { Name = data(_) }
+ ->
+ io__write_string("extern ")
+ ;
+ []
+ ).
:- pred mlds_output_virtuality(virtuality, io__state, io__state).
:- mode mlds_output_virtuality(in, di, uo) is det.
Index: compiler/ml_elim_nested.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/ml_elim_nested.m,v
retrieving revision 1.13
diff -u -d -r1.13 ml_elim_nested.m
--- compiler/ml_elim_nested.m 2000/10/14 04:00:16 1.13
+++ compiler/ml_elim_nested.m 2000/10/22 06:52:41
@@ -102,7 +102,10 @@
% Actually the description above is slightly over-simplified: not all local
% variables need to be put in the environment struct. Only those local
% variables which are referenced by nested functions need to be
-% put in the environment struct.
+% put in the environment struct. Also, if none of the nested functions
+% refer to the locals in the outer function, we don't need to create
+% an environment struct at all, we just need to hoist the definitions
+% of the nested functions out to the top level.
%
% The `env_ptr' variables generated here serve as definitions for
% the (previously dangling) references to such variables that
@@ -130,8 +133,12 @@
:- implementation.
:- import_module bool, int, list, std_util, string, require.
+
+:- import_module ml_code_util.
+
% the following imports are needed for mangling pred names
:- import_module hlds_pred, prog_data, prog_out.
+
:- import_module globals, options.
% Eliminated nested functions for the whole MLDS.
@@ -164,22 +171,30 @@
%
% traverse the function body, finding (and removing)
% any nested functions, and fixing up any references
- % to the arguments or to local variables which
- % occur in nested functions
+ % to the arguments or to local variables or local
+ % static constants which occur in nested functions
%
ElimInfo0 = elim_info_init(ModuleName, OuterVars, EnvTypeName),
Params = mlds__func_params(Arguments, _RetValues),
ml_maybe_add_args(Arguments, FuncBody0, ModuleName,
Context, ElimInfo0, ElimInfo1),
flatten_statement(FuncBody0, FuncBody1, ElimInfo1, ElimInfo),
- elim_info_finish(ElimInfo, NestedFuncs0, LocalVars),
+ elim_info_finish(ElimInfo, NestedFuncs0, Locals),
%
- % if there were no nested functions, then we're done
+ % Split the locals that we need to process
+ % into local variables and local static constants
+ %
+ list__filter(ml_decl_is_static_const, Locals,
+ LocalStatics, LocalVars),
+
+ %
+ % if there were no nested functions, then we just
+ % hoist the local static constants
%
( NestedFuncs0 = [] ->
FuncBody = FuncBody1,
- HoistedDefns = []
+ HoistedDefns = LocalStatics
;
%
% Create a struct to hold the local variables,
@@ -195,6 +210,9 @@
Globals), NestedFuncs0, NestedFuncs,
no, InsertedEnv),
+ % Hoist out the local statics and the nested functions
+ HoistedDefns0 = list__append(LocalStatics, NestedFuncs),
+
%
% It's possible that none of the nested
% functions reference the arguments or locals of
@@ -233,17 +251,15 @@
[InitEnv | CodeToCopyArgs],
[FuncBody1]), Context),
%
- % hoist the nested functions out, by
- % inserting the environment struct type
- % and the previously nested functions at
- % the start of the list of definitions,
- % followed by the new version of the
- % top-level function
+ % insert the environment struct type
+ % at the start of the list of hoisted definitions
+ % (preceding the previously nested functions
+ % and static constants in HoistedDefns0),
%
- HoistedDefns = [EnvTypeDefn | NestedFuncs]
+ HoistedDefns = [EnvTypeDefn | HoistedDefns0]
;
FuncBody = FuncBody1,
- HoistedDefns = NestedFuncs
+ HoistedDefns = HoistedDefns0
)
),
DefnBody = mlds__function(PredProcId, Params, yes(FuncBody)),
@@ -256,7 +272,7 @@
%
% Add any arguments which are used in nested functions
- % to the local_vars field in the elim_info.
+ % to the local_data field in the elim_info.
%
:- pred ml_maybe_add_args(mlds__arguments, mlds__statement,
mlds_module_name, mlds__context, elim_info, elim_info).
@@ -266,10 +282,10 @@
ml_maybe_add_args([Arg|Args], FuncBody, ModuleName, Context) -->
(
{ Arg = data(var(VarName)) - _Type },
- { ml_should_add_local_var(ModuleName, VarName, [], [FuncBody]) }
+ { ml_should_add_local_data(ModuleName, VarName, [], [FuncBody]) }
->
{ ml_conv_arg_to_var(Context, Arg, ArgToCopy) },
- elim_info_add_local_var(ArgToCopy)
+ elim_info_add_local_data(ArgToCopy)
;
[]
),
@@ -291,7 +307,7 @@
ArgsToCopy0, CodeToCopyArgs0),
(
Arg = data(var(VarName)) - FieldType,
- ml_should_add_local_var(ModuleName, VarName, [], [FuncBody])
+ ml_should_add_local_data(ModuleName, VarName, [], [FuncBody])
->
ml_conv_arg_to_var(Context, Arg, ArgToCopy),
@@ -721,7 +737,7 @@
% If that wasn't the case, we'd need code something
% like this:
/***************
- { LocalVars = elim_info_get_local_vars(ElimInfo) },
+ { LocalVars = elim_info_get_local_data(ElimInfo) },
{ OuterVars0 = elim_info_get_outer_vars(ElimInfo) },
{ OuterVars = [LocalVars | OuterVars0] },
{ FlattenedDefns = ml_elim_nested_defns(ModuleName,
@@ -746,10 +762,10 @@
{ ModuleName = elim_info_get_module_name(ElimInfo) },
(
{ Name = data(var(VarName)) },
- { ml_should_add_local_var(ModuleName, VarName,
+ { ml_should_add_local_data(ModuleName, VarName,
FollowingDefns, FollowingStatements) }
->
- elim_info_add_local_var(Defn0),
+ elim_info_add_local_data(Defn0),
{ Defns = [] }
;
{ Defns = [Defn0] }
@@ -767,14 +783,29 @@
).
%
- % check for a nested function definition
- % that references this variable
+ % Succeed iff we should add the definition of this variable
+ % to the local_data field of the ml_elim_info, meaning that
+ % it should be added to the environment struct
+ % (if it's a variable) or hoisted out to the top level
+ % (if it's a static const).
%
-:- pred ml_should_add_local_var(mlds_module_name, mlds__var_name,
+ % This checks for a nested function definition
+ % or static initializer that references the variable.
+ % This is conservative; we only need to hoist out
+ % static variables if they are referenced by
+ % static initializers which themselves need to be
+ % hoisted because they are referenced from a nested
+ % function. But checking the last part of that
+ % is tricky, so currently we just hoist more
+ % of the static consts than we strictly need to.
+ % Perhaps it would be simpler to just hoist *all*
+ % static consts.
+ %
+:- pred ml_should_add_local_data(mlds_module_name, mlds__var_name,
mlds__defns, mlds__statements).
-:- mode ml_should_add_local_var(in, in, in, in) is semidet.
+:- mode ml_should_add_local_data(in, in, in, in) is semidet.
-ml_should_add_local_var(ModuleName, VarName,
+ml_should_add_local_data(ModuleName, VarName,
FollowingDefns, FollowingStatements) :-
QualVarName = qual(ModuleName, VarName),
(
@@ -783,9 +814,16 @@
statements_contains_defn(FollowingStatements,
FollowingDefn)
),
- FollowingDefn = mlds__defn(_, _, _,
- mlds__function(_, _, _)),
- defn_contains_var(FollowingDefn, QualVarName).
+ (
+ FollowingDefn = mlds__defn(_, _, _,
+ mlds__function(_, _, _)),
+ defn_contains_var(FollowingDefn, QualVarName)
+ ;
+ FollowingDefn = mlds__defn(_, _, _,
+ mlds__data(_, Initializer)),
+ ml_decl_is_static_const(FollowingDefn),
+ initializer_contains_var(Initializer, QualVarName)
+ ).
%-----------------------------------------------------------------------------%
@@ -923,7 +961,7 @@
fixup_var(ThisVar, Lval, ElimInfo, ElimInfo) :-
ThisVar = qual(ThisVarModuleName, ThisVarName),
ModuleName = elim_info_get_module_name(ElimInfo),
- LocalVars = elim_info_get_local_vars(ElimInfo),
+ Locals = elim_info_get_local_data(ElimInfo),
ClassType = elim_info_get_env_type_name(ElimInfo),
(
%
@@ -932,12 +970,13 @@
% and replace them with `env_ptr->foo'.
%
ThisVarModuleName = ModuleName,
- IsLocal = (pred(VarType::out) is nondet :-
- list__member(Var, LocalVars),
+ IsLocalVar = (pred(VarType::out) is nondet :-
+ list__member(Var, Locals),
Var = mlds__defn(data(var(ThisVarName)), _, _,
- data(VarType, _))
+ data(VarType, _)),
+ \+ ml_decl_is_static_const(Var)
),
- solutions(IsLocal, [FieldType])
+ solutions(IsLocalVar, [FieldType])
->
EnvPtr = lval(var(qual(ModuleName, "env_ptr"))),
EnvModuleName = ml_env_module_name(ClassType),
@@ -963,7 +1002,7 @@
% for `env.foo'.)
%
ThisVarModuleName = ModuleName,
- list__member(Var, LocalVars),
+ list__member(Var, Locals),
Var = mlds__defn(data(var(ThisVarName)), _, _, _)
->
Env = var(qual(ModuleName, "env")),
@@ -1381,7 +1420,7 @@
% The list of local variables that we must
% put in the environment structure
% This list is stored in reverse order.
- local_vars :: list(mlds__defn),
+ local_data :: list(mlds__defn),
% Type of the introduced environment struct
env_type_name :: mlds__type
@@ -1402,8 +1441,8 @@
:- func elim_info_get_outer_vars(elim_info) = outervars.
elim_info_get_outer_vars(ElimInfo) = ElimInfo ^ outer_vars.
-:- func elim_info_get_local_vars(elim_info) = list(mlds__defn).
-elim_info_get_local_vars(ElimInfo) = ElimInfo ^ local_vars.
+:- func elim_info_get_local_data(elim_info) = list(mlds__defn).
+elim_info_get_local_data(ElimInfo) = ElimInfo ^ local_data.
:- func elim_info_get_env_type_name(elim_info) = mlds__type.
elim_info_get_env_type_name(ElimInfo) = ElimInfo ^ env_type_name.
@@ -1413,15 +1452,15 @@
elim_info_add_nested_func(NestedFunc, ElimInfo,
ElimInfo ^ nested_funcs := [NestedFunc | ElimInfo ^ nested_funcs]).
-:- pred elim_info_add_local_var(mlds__defn, elim_info, elim_info).
-:- mode elim_info_add_local_var(in, in, out) is det.
-elim_info_add_local_var(LocalVar, ElimInfo,
- ElimInfo ^ local_vars := [LocalVar | ElimInfo ^ local_vars]).
+:- pred elim_info_add_local_data(mlds__defn, elim_info, elim_info).
+:- mode elim_info_add_local_data(in, in, out) is det.
+elim_info_add_local_data(LocalVar, ElimInfo,
+ ElimInfo ^ local_data := [LocalVar | ElimInfo ^ local_data]).
:- pred elim_info_finish(elim_info, list(mlds__defn), list(mlds__defn)).
:- mode elim_info_finish(in, out, out) is det.
-elim_info_finish(ElimInfo, Funcs, LocalVars) :-
+elim_info_finish(ElimInfo, Funcs, Locals) :-
Funcs = list__reverse(ElimInfo ^ nested_funcs),
- LocalVars = list__reverse(ElimInfo ^ local_vars).
+ Locals = list__reverse(ElimInfo ^ local_data).
%-----------------------------------------------------------------------------%
Index: compiler/ml_unify_gen.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/ml_unify_gen.m,v
retrieving revision 1.19
diff -u -d -r1.19 ml_unify_gen.m
--- compiler/ml_unify_gen.m 2000/10/09 08:48:53 1.19
+++ compiler/ml_unify_gen.m 2000/10/22 06:52:06
@@ -1091,7 +1091,7 @@
%
% Generate a local static constant for this float
%
- ml_gen_info_new_conv_var(SequenceNum),
+ ml_gen_info_new_const(SequenceNum),
{ string__format("float_%d", [i(SequenceNum)], ConstName) },
{ Initializer = init_obj(Rval) },
{ ConstDefn = ml_gen_static_const_defn(ConstName, Type,
@@ -1124,16 +1124,39 @@
{ error("ml_gen_static_const_arg_list: length mismatch") }.
% Generate the name of the local static constant
- % for a given variable.
+ % for a given variable. To ensure that the names
+ % are unique, we qualify them with both the pred_id
+ % number, and a sequence number. This is needed to
+ % allow ml_elim_nested.m to hoist such constants out
+ % to top level.
%
:- pred ml_gen_static_const_name(prog_var, mlds__var_name,
ml_gen_info, ml_gen_info).
:- mode ml_gen_static_const_name(in, out, in, out) is det.
ml_gen_static_const_name(Var, ConstName) -->
+ ml_gen_info_new_const(SequenceNum),
+ ml_gen_info_set_const_num(Var, SequenceNum),
+ ml_format_static_const_name(Var, SequenceNum, ConstName).
+
+:- pred ml_lookup_static_const_name(prog_var, mlds__var_name,
+ ml_gen_info, ml_gen_info).
+:- mode ml_lookup_static_const_name(in, out, in, out) is det.
+ml_lookup_static_const_name(Var, ConstName) -->
+ ml_gen_info_lookup_const_num(Var, SequenceNum),
+ ml_format_static_const_name(Var, SequenceNum, ConstName).
+
+:- pred ml_format_static_const_name(prog_var, const_seq, mlds__var_name,
+ ml_gen_info, ml_gen_info).
+:- mode ml_format_static_const_name(in, in, out, in, out) is det.
+
+ml_format_static_const_name(Var, SequenceNum, ConstName) -->
=(MLDSGenInfo),
+ { ml_gen_info_get_pred_id(MLDSGenInfo, PredId) },
+ { pred_id_to_int(PredId, PredIdNum) },
{ ml_gen_info_get_varset(MLDSGenInfo, VarSet) },
{ VarName = ml_gen_var_name(VarSet, Var) },
- { string__format("const_%s", [s(VarName)], ConstName) }.
+ { string__format("const_%d_%d_%s", [i(PredIdNum), i(SequenceNum),
+ s(VarName)], ConstName) }.
% Generate an rval containing the address of the local static constant
% for a given variable.
@@ -1142,7 +1165,7 @@
ml_gen_info, ml_gen_info).
:- mode ml_gen_static_const_addr(in, out, in, out) is det.
ml_gen_static_const_addr(Var, ConstAddrRval) -->
- ml_gen_static_const_name(Var, ConstName),
+ ml_lookup_static_const_name(Var, ConstName),
ml_qualify_var(ConstName, ConstLval),
{ ConstAddrRval = mem_addr(ConstLval) }.
@@ -1158,20 +1181,6 @@
DeclFlags = ml_static_const_decl_flags,
MLDS_Context = mlds__make_context(Context),
MLDS_Defn = mlds__defn(Name, MLDS_Context, DeclFlags, Defn).
-
- % Return the declaration flags appropriate for an
- % initialized local static constant.
- %
-:- func ml_static_const_decl_flags = mlds__decl_flags.
-ml_static_const_decl_flags = MLDS_DeclFlags :-
- Access = private,
- PerInstance = one_copy,
- Virtuality = non_virtual,
- Finality = overridable,
- Constness = const,
- Abstractness = concrete,
- MLDS_DeclFlags = init_decl_flags(Access, PerInstance,
- Virtuality, Finality, Constness, Abstractness).
:- pred ml_cons_name(cons_id, ctor_name, ml_gen_info, ml_gen_info).
:- mode ml_cons_name(in, out, in, out) is det.
Index: tests/valid/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/valid/Mmakefile,v
retrieving revision 1.74
diff -u -d -r1.74 Mmakefile
--- tests/valid/Mmakefile 2000/10/13 13:56:17 1.74
+++ tests/valid/Mmakefile 2000/10/21 13:50:22
@@ -130,6 +130,7 @@
soln_context.m \
some_switch.m \
stack_alloc.m \
+ static.m \
subtype_switch.m \
switch_detection_bug.m \
switch_detection_bug2.m \
Index: tests/valid/static.m
===================================================================
RCS file: static.m
diff -N static.m
--- /dev/null Thu Mar 30 14:06:13 2000
+++ static.m Sun Oct 22 17:09:57 2000
@@ -0,0 +1,106 @@
+% mmc -c --grade hlc.gc static.c
+% static.c(455) : error C2065: 'static__const_Result_5' : undeclared identifier
+:- module static.
+
+:- interface.
+
+:- type t.
+:- type t4.
+:- type t5.
+
+:- pred q(t::in, t5::out) is det.
+:- pred r(t::in, t5::out, int::out) is multi.
+:- pred s(int::in, t5::out) is cc_nondet.
+:- pred t(t::in, t4::out, t5::out) is semidet.
+:- pred u(t4::out, t5::out, int::out) is nondet.
+:- pred v(t4::out, t5::out, int::out) is nondet.
+
+:- implementation.
+:- import_module int, list.
+
+:- type t ---> a ; b ; c.
+:- type t4 ---> f(string, int).
+:- type t5 ---> g(t4, t4) ; i.
+
+% Test for ordinary if-then-else
+q(X, Y) :-
+ (
+ X = a,
+ % This line causes the problem. Move it into
+ % the then to avoid the above code gen problem.
+ % We can move it into the then because this line
+ % isn't part of the test.
+ Result = f("hello", 0)
+ ->
+ Y = g(Result, Result)
+ ;
+ Y = i
+ ).
+
+% Test for if-then-else with nondet condition
+r(X, Y, Z) :-
+ (
+ X = a,
+ (Z0 = 1 ; Z0 = 2),
+
+ % This line causes the problem. Move it into
+ % the then to avoid the above code gen problem.
+ % We can move it into the then because this line
+ % isn't part of the test.
+ Result = f("hello", 0)
+ ->
+ Z = Z0,
+ Y = g(Result, Result)
+ ;
+ Z = 0,
+ Y = i
+ ).
+
+% Test for commit
+s(X, Y) :-
+ some [Z] (
+ (Z = 1 ; Z = 2),
+ X = Z * Z,
+ Result = f("hello", 0)
+ ),
+ Y = g(Result, Result).
+
+% Test same variable having different constant values in different branches (semidet)
+t(X, Result, Y) :-
+ (
+ X = a,
+ Result = f("hello", 0),
+ Y = g(Result, Result)
+ ;
+ X = b,
+ Result = f("goodbye", 0),
+ Y = g(Result, Result)
+ ).
+
+% Test same variable having different constant values in different branches (nondet)
+u(Result, Y, Z) :-
+ (
+ Result = f("hello", 0),
+ list__member(Z, [1,2]),
+ Y = g(Result, Result)
+ ;
+ Result = f("hello again", 0),
+ Result2 = f("GoodBye", 0),
+ list__member(Z, [3,4]),
+ Y = g(Result, Result2)
+ ).
+
+% Exactly the same as u/3, but with different constants;
+% this tests to ensure that any constant values hoisted
+% out to the top level are given distinct names.
+v(Result, Y, Z) :-
+ (
+ Result = f("xxxxx", 0),
+ list__member(Z, [1,2]),
+ Y = g(Result, Result)
+ ;
+ Result = f("yyyyyyyyyyy", 0),
+ Result2 = f("zzzzzzz", 0),
+ list__member(Z, [3,4]),
+ Y = g(Result, Result2)
+ ).
******* RELATIVE DIFF *******************************************
--- CHANGES.mlds_sgt_bug.old Sun Oct 22 16:20:02 2000
+++ CHANGES.mlds_sgt_bug Sun Oct 22 17:42:01 2000
@@ -13,16 +13,24 @@
compiler/ml_unify_gen.m:
compiler/ml_code_util.m:
- Move ml_gen_static_const_decl_flags from ml_unify_gen.m
+ Ensure that all static consts get unique names, so that
+ ml_elim_nested.m can hoist them to the top level.
+ Also move ml_gen_static_const_decl_flags from ml_unify_gen.m
to ml_code_util.m, for use by ml_code_gen.m.
compiler/ml_elim_nested.m:
- XXX FIXME This module doesn't handle static constants!
-
+ Hoist out the definitions of static constants to the top level
+ in cases where they might be referenced from nested functions.
+ Also change the name of the local_vars field of the ml_elim_info
+ to local_data, to make it clear that it can hold constants too.
+
compiler/mark_static_terms.m:
Fix some typos in the comments.
tests/valid/Mmakefile:
tests/valid/static.m:
Add some regression tests.
+
+compiler/mlds_to_c.m:
+ Fix an XXX: it was not outputting `static' in the right places.
--- compiler/ml_code_gen.m
+++ compiler/ml_code_gen.m
@@ -99,5 +99,5 @@
-% For branched goals, we must in fact ensure that we do NOT hoist out
-% static variable definitions, since the same Mercury variable can have
-% different constant values in different branches, and so there may be
-% more than one C static const with the same name but with different
-% values and occurring in different scopes.
+%
+% Handling static constants also requires that the calls to ml_gen_goal
+% for each subgoal must be done in the right order, so that the
+% const_num_map in the ml_gen_info holds the right sequence numbers
+% for the constants in scope.
@@ -1455,2 +1455,2 @@
- { list__filter(ml_decl_is_static, GoalDecls, GoalStaticDecls,
- GoalOtherDecls) },
+ { list__filter(ml_decl_is_static_const, GoalDecls,
+ GoalStaticDecls, GoalOtherDecls) },
@@ -1524,2 +1524,2 @@
- { list__filter(ml_decl_is_static, GoalDecls, GoalStaticDecls,
- GoalOtherDecls) },
+ { list__filter(ml_decl_is_static_const, GoalDecls,
+ GoalStaticDecls, GoalOtherDecls) },
@@ -2591 +2591 @@
- % generate the `cond_<N>' var
+ % generate the `cond_<N>' var and the code to initialize it to false
@@ -2594,0 +2595,2 @@
+ ml_gen_set_cond_var(CondVar, const(false), Context,
+ SetCondFalse),
@@ -2596 +2598 @@
- % generate the `then_func'
+ % allocate a name for the `then_func'
@@ -2597,0 +2600,10 @@
+
+ % generate <Cond && then_func()>
+ ml_get_env_ptr(EnvPtrRval),
+ { SuccessCont = success_cont(ThenFuncLabelRval, EnvPtrRval,
+ [], []) },
+ ml_gen_info_push_success_cont(SuccessCont),
+ ml_gen_goal(model_non, Cond, CondDecls, CondStatements),
+ ml_gen_info_pop_success_cont,
+
+ % generate the `then_func'
@@ -2610,9 +2622 @@
- % generate the main body
- ml_gen_set_cond_var(CondVar, const(false), Context,
- SetCondFalse),
- ml_get_env_ptr(EnvPtrRval),
- { SuccessCont = success_cont(ThenFuncLabelRval, EnvPtrRval,
- [], []) },
- ml_gen_info_push_success_cont(SuccessCont),
- ml_gen_goal(model_non, Cond, CondDecls, CondStatements),
- ml_gen_info_pop_success_cont,
+ % generate `if (!cond_<N>) { <Else> }'
--- compiler/ml_code_util.m
+++ compiler/ml_code_util.m
@@ -255 +255 @@
-:- pred ml_decl_is_static(mlds__defn::in) is semidet.
+:- pred ml_decl_is_static_const(mlds__defn::in) is semidet.
@@ -525,0 +526,20 @@
+ % Generate a new `const' sequence number.
+ % This is used to give unique names to the local constants
+ % generated for --static-ground-terms.
+:- type const_seq == int.
+:- pred ml_gen_info_new_const(const_seq,
+ ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_new_const(out, in, out) is det.
+
+ % Set the `const' sequence number
+ % corresponding to a given HLDS variable.
+ %
+:- pred ml_gen_info_set_const_num(prog_var, const_seq, ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_set_const_num(in, in, in, out) is det.
+
+ % Lookup the `const' sequence number
+ % corresponding to a given HLDS variable.
+ %
+:- pred ml_gen_info_lookup_const_num(prog_var, const_seq, ml_gen_info, ml_gen_info).
+:- mode ml_gen_info_lookup_const_num(in, out, in, out) is det.
+
@@ -740 +760 @@
- % generate the `succ_func'
+ % allocate a name for the `succ_func'
@@ -742,9 +761,0 @@
- /* push nesting level */
- DoGenRest(RestDecls, RestStatements),
- { list__filter(ml_decl_is_static, RestDecls, RestStaticDecls,
- RestOtherDecls) },
- { RestStatement = ml_gen_block(RestOtherDecls, RestStatements,
- Context) },
- /* pop nesting level */
- ml_gen_nondet_label_func(RestFuncLabel, Context, RestStatement,
- RestFunc),
@@ -751,0 +763 @@
+ % generate <First && succ_func()>
@@ -758,0 +771,11 @@
+ % generate the `succ_func'
+ /* push nesting level */
+ DoGenRest(RestDecls, RestStatements),
+ { list__filter(ml_decl_is_static_const, RestDecls,
+ RestStaticDecls, RestOtherDecls) },
+ { RestStatement = ml_gen_block(RestOtherDecls, RestStatements,
+ Context) },
+ /* pop nesting level */
+ ml_gen_nondet_label_func(RestFuncLabel, Context, RestStatement,
+ RestFunc),
+
@@ -767 +790 @@
-ml_decl_is_static(Defn) :-
+ml_decl_is_static_const(Defn) :-
@@ -1585 +1608 @@
-% Only the `func_label', `commit_label', `cond_var', `conv_var',
+% Only the `func_label', `commit_label', `cond_var', `conv_var', `const_num',
@@ -1612,0 +1636,2 @@
+ const_num :: const_seq,
+ const_num_map :: map(prog_var, const_seq),
@@ -1638,0 +1664,2 @@
+ ConstCounter = 0,
+ map__init(ConstNumMap),
@@ -1653,0 +1681,2 @@
+ ConstCounter,
+ ConstNumMap,
@@ -1696,0 +1726,9 @@
+
+ml_gen_info_new_const(ConstVar, Info, Info^const_num := ConstVar) :-
+ ConstVar = Info^const_num + 1.
+
+ml_gen_info_set_const_num(Var, ConstVar, Info,
+ Info^const_num_map := map__set(Info^const_num_map, Var, ConstVar)).
+
+ml_gen_info_lookup_const_num(Var, ConstVar, Info, Info) :-
+ ConstVar = map__lookup(Info^const_num_map, Var).
--- compiler/ml_unify_gen.m
+++ compiler/ml_unify_gen.m
@@ -1094 +1094 @@
- ml_gen_info_new_conv_var(SequenceNum),
+ ml_gen_info_new_const(SequenceNum),
@@ -1127 +1127,5 @@
- % for a given variable.
+ % for a given variable. To ensure that the names
+ % are unique, we qualify them with both the pred_id
+ % number, and a sequence number. This is needed to
+ % allow ml_elim_nested.m to hoist such constants out
+ % to top level.
@@ -1132,0 +1137,16 @@
+ ml_gen_info_new_const(SequenceNum),
+ ml_gen_info_set_const_num(Var, SequenceNum),
+ ml_format_static_const_name(Var, SequenceNum, ConstName).
+
+:- pred ml_lookup_static_const_name(prog_var, mlds__var_name,
+ ml_gen_info, ml_gen_info).
+:- mode ml_lookup_static_const_name(in, out, in, out) is det.
+ml_lookup_static_const_name(Var, ConstName) -->
+ ml_gen_info_lookup_const_num(Var, SequenceNum),
+ ml_format_static_const_name(Var, SequenceNum, ConstName).
+
+:- pred ml_format_static_const_name(prog_var, const_seq, mlds__var_name,
+ ml_gen_info, ml_gen_info).
+:- mode ml_format_static_const_name(in, in, out, in, out) is det.
+
+ml_format_static_const_name(Var, SequenceNum, ConstName) -->
@@ -1133,0 +1154,2 @@
+ { ml_gen_info_get_pred_id(MLDSGenInfo, PredId) },
+ { pred_id_to_int(PredId, PredIdNum) },
@@ -1136 +1158,2 @@
- { string__format("const_%s", [s(VarName)], ConstName) }.
+ { string__format("const_%d_%d_%s", [i(PredIdNum), i(SequenceNum),
+ s(VarName)], ConstName) }.
@@ -1145 +1168 @@
- ml_gen_static_const_name(Var, ConstName),
+ ml_lookup_static_const_name(Var, ConstName),
--- static.m
+++ static.m
@@ -13,0 +14,3 @@
+:- pred t(t::in, t4::out, t5::out) is semidet.
+:- pred u(t4::out, t5::out, int::out) is nondet.
+:- pred v(t4::out, t5::out, int::out) is nondet.
@@ -16 +19 @@
-:- import_module int.
+:- import_module int, list.
@@ -63,0 +67,40 @@
+
+% Test same variable having different constant values in different branches (semidet)
+t(X, Result, Y) :-
+ (
+ X = a,
+ Result = f("hello", 0),
+ Y = g(Result, Result)
+ ;
+ X = b,
+ Result = f("goodbye", 0),
+ Y = g(Result, Result)
+ ).
+
+% Test same variable having different constant values in different branches (nondet)
+u(Result, Y, Z) :-
+ (
+ Result = f("hello", 0),
+ list__member(Z, [1,2]),
+ Y = g(Result, Result)
+ ;
+ Result = f("hello again", 0),
+ Result2 = f("GoodBye", 0),
+ list__member(Z, [3,4]),
+ Y = g(Result, Result2)
+ ).
+
+% Exactly the same as u/3, but with different constants;
+% this tests to ensure that any constant values hoisted
+% out to the top level are given distinct names.
+v(Result, Y, Z) :-
+ (
+ Result = f("xxxxx", 0),
+ list__member(Z, [1,2]),
+ Y = g(Result, Result)
+ ;
+ Result = f("yyyyyyyyyyy", 0),
+ Result2 = f("zzzzzzz", 0),
+ list__member(Z, [3,4]),
+ Y = g(Result, Result2)
+ ).
--- compiler/ml_elim_nested.m 2000/10/14 04:00:16 1.13
+++ compiler/ml_elim_nested.m 2000/10/22 06:52:41
@@ -102,7 +102,10 @@
% Actually the description above is slightly over-simplified: not all local
% variables need to be put in the environment struct. Only those local
% variables which are referenced by nested functions need to be
-% put in the environment struct.
+% put in the environment struct. Also, if none of the nested functions
+% refer to the locals in the outer function, we don't need to create
+% an environment struct at all, we just need to hoist the definitions
+% of the nested functions out to the top level.
%
% The `env_ptr' variables generated here serve as definitions for
% the (previously dangling) references to such variables that
@@ -130,8 +133,12 @@
:- implementation.
:- import_module bool, int, list, std_util, string, require.
+
+:- import_module ml_code_util.
+
% the following imports are needed for mangling pred names
:- import_module hlds_pred, prog_data, prog_out.
+
:- import_module globals, options.
% Eliminated nested functions for the whole MLDS.
@@ -164,22 +171,30 @@
%
% traverse the function body, finding (and removing)
% any nested functions, and fixing up any references
- % to the arguments or to local variables which
- % occur in nested functions
+ % to the arguments or to local variables or local
+ % static constants which occur in nested functions
%
ElimInfo0 = elim_info_init(ModuleName, OuterVars, EnvTypeName),
Params = mlds__func_params(Arguments, _RetValues),
ml_maybe_add_args(Arguments, FuncBody0, ModuleName,
Context, ElimInfo0, ElimInfo1),
flatten_statement(FuncBody0, FuncBody1, ElimInfo1, ElimInfo),
- elim_info_finish(ElimInfo, NestedFuncs0, LocalVars),
+ elim_info_finish(ElimInfo, NestedFuncs0, Locals),
%
- % if there were no nested functions, then we're done
+ % Split the locals that we need to process
+ % into local variables and local static constants
+ %
+ list__filter(ml_decl_is_static_const, Locals,
+ LocalStatics, LocalVars),
+
+ %
+ % if there were no nested functions, then we just
+ % hoist the local static constants
%
( NestedFuncs0 = [] ->
FuncBody = FuncBody1,
- HoistedDefns = []
+ HoistedDefns = LocalStatics
;
%
% Create a struct to hold the local variables,
@@ -195,6 +210,9 @@
Globals), NestedFuncs0, NestedFuncs,
no, InsertedEnv),
+ % Hoist out the local statics and the nested functions
+ HoistedDefns0 = list__append(LocalStatics, NestedFuncs),
+
%
% It's possible that none of the nested
% functions reference the arguments or locals of
@@ -233,17 +251,15 @@
[InitEnv | CodeToCopyArgs],
[FuncBody1]), Context),
%
- % hoist the nested functions out, by
- % inserting the environment struct type
- % and the previously nested functions at
- % the start of the list of definitions,
- % followed by the new version of the
- % top-level function
+ % insert the environment struct type
+ % at the start of the list of hoisted definitions
+ % (preceding the previously nested functions
+ % and static constants in HoistedDefns0),
%
- HoistedDefns = [EnvTypeDefn | NestedFuncs]
+ HoistedDefns = [EnvTypeDefn | HoistedDefns0]
;
FuncBody = FuncBody1,
- HoistedDefns = NestedFuncs
+ HoistedDefns = HoistedDefns0
)
),
DefnBody = mlds__function(PredProcId, Params, yes(FuncBody)),
@@ -256,7 +272,7 @@
%
% Add any arguments which are used in nested functions
- % to the local_vars field in the elim_info.
+ % to the local_data field in the elim_info.
%
:- pred ml_maybe_add_args(mlds__arguments, mlds__statement,
mlds_module_name, mlds__context, elim_info, elim_info).
@@ -266,10 +282,10 @@
ml_maybe_add_args([Arg|Args], FuncBody, ModuleName, Context) -->
(
{ Arg = data(var(VarName)) - _Type },
- { ml_should_add_local_var(ModuleName, VarName, [], [FuncBody]) }
+ { ml_should_add_local_data(ModuleName, VarName, [], [FuncBody]) }
->
{ ml_conv_arg_to_var(Context, Arg, ArgToCopy) },
- elim_info_add_local_var(ArgToCopy)
+ elim_info_add_local_data(ArgToCopy)
;
[]
),
@@ -291,7 +307,7 @@
ArgsToCopy0, CodeToCopyArgs0),
(
Arg = data(var(VarName)) - FieldType,
- ml_should_add_local_var(ModuleName, VarName, [], [FuncBody])
+ ml_should_add_local_data(ModuleName, VarName, [], [FuncBody])
->
ml_conv_arg_to_var(Context, Arg, ArgToCopy),
@@ -721,7 +737,7 @@
% If that wasn't the case, we'd need code something
% like this:
/***************
- { LocalVars = elim_info_get_local_vars(ElimInfo) },
+ { LocalVars = elim_info_get_local_data(ElimInfo) },
{ OuterVars0 = elim_info_get_outer_vars(ElimInfo) },
{ OuterVars = [LocalVars | OuterVars0] },
{ FlattenedDefns = ml_elim_nested_defns(ModuleName,
@@ -746,10 +762,10 @@
{ ModuleName = elim_info_get_module_name(ElimInfo) },
(
{ Name = data(var(VarName)) },
- { ml_should_add_local_var(ModuleName, VarName,
+ { ml_should_add_local_data(ModuleName, VarName,
FollowingDefns, FollowingStatements) }
->
- elim_info_add_local_var(Defn0),
+ elim_info_add_local_data(Defn0),
{ Defns = [] }
;
{ Defns = [Defn0] }
@@ -767,14 +783,29 @@
).
%
- % check for a nested function definition
- % that references this variable
+ % Succeed iff we should add the definition of this variable
+ % to the local_data field of the ml_elim_info, meaning that
+ % it should be added to the environment struct
+ % (if it's a variable) or hoisted out to the top level
+ % (if it's a static const).
%
-:- pred ml_should_add_local_var(mlds_module_name, mlds__var_name,
+ % This checks for a nested function definition
+ % or static initializer that references the variable.
+ % This is conservative; we only need to hoist out
+ % static variables if they are referenced by
+ % static initializers which themselves need to be
+ % hoisted because they are referenced from a nested
+ % function. But checking the last part of that
+ % is tricky, so currently we just hoist more
+ % of the static consts than we strictly need to.
+ % Perhaps it would be simpler to just hoist *all*
+ % static consts.
+ %
+:- pred ml_should_add_local_data(mlds_module_name, mlds__var_name,
mlds__defns, mlds__statements).
-:- mode ml_should_add_local_var(in, in, in, in) is semidet.
+:- mode ml_should_add_local_data(in, in, in, in) is semidet.
-ml_should_add_local_var(ModuleName, VarName,
+ml_should_add_local_data(ModuleName, VarName,
FollowingDefns, FollowingStatements) :-
QualVarName = qual(ModuleName, VarName),
(
@@ -783,9 +814,16 @@
statements_contains_defn(FollowingStatements,
FollowingDefn)
),
- FollowingDefn = mlds__defn(_, _, _,
- mlds__function(_, _, _)),
- defn_contains_var(FollowingDefn, QualVarName).
+ (
+ FollowingDefn = mlds__defn(_, _, _,
+ mlds__function(_, _, _)),
+ defn_contains_var(FollowingDefn, QualVarName)
+ ;
+ FollowingDefn = mlds__defn(_, _, _,
+ mlds__data(_, Initializer)),
+ ml_decl_is_static_const(FollowingDefn),
+ initializer_contains_var(Initializer, QualVarName)
+ ).
%-----------------------------------------------------------------------------%
@@ -923,7 +961,7 @@
fixup_var(ThisVar, Lval, ElimInfo, ElimInfo) :-
ThisVar = qual(ThisVarModuleName, ThisVarName),
ModuleName = elim_info_get_module_name(ElimInfo),
- LocalVars = elim_info_get_local_vars(ElimInfo),
+ Locals = elim_info_get_local_data(ElimInfo),
ClassType = elim_info_get_env_type_name(ElimInfo),
(
%
@@ -932,12 +970,13 @@
% and replace them with `env_ptr->foo'.
%
ThisVarModuleName = ModuleName,
- IsLocal = (pred(VarType::out) is nondet :-
- list__member(Var, LocalVars),
+ IsLocalVar = (pred(VarType::out) is nondet :-
+ list__member(Var, Locals),
Var = mlds__defn(data(var(ThisVarName)), _, _,
- data(VarType, _))
+ data(VarType, _)),
+ \+ ml_decl_is_static_const(Var)
),
- solutions(IsLocal, [FieldType])
+ solutions(IsLocalVar, [FieldType])
->
EnvPtr = lval(var(qual(ModuleName, "env_ptr"))),
EnvModuleName = ml_env_module_name(ClassType),
@@ -963,7 +1002,7 @@
% for `env.foo'.)
%
ThisVarModuleName = ModuleName,
- list__member(Var, LocalVars),
+ list__member(Var, Locals),
Var = mlds__defn(data(var(ThisVarName)), _, _, _)
->
Env = var(qual(ModuleName, "env")),
@@ -1381,7 +1420,7 @@
% The list of local variables that we must
% put in the environment structure
% This list is stored in reverse order.
- local_vars :: list(mlds__defn),
+ local_data :: list(mlds__defn),
% Type of the introduced environment struct
env_type_name :: mlds__type
@@ -1402,8 +1441,8 @@
:- func elim_info_get_outer_vars(elim_info) = outervars.
elim_info_get_outer_vars(ElimInfo) = ElimInfo ^ outer_vars.
-:- func elim_info_get_local_vars(elim_info) = list(mlds__defn).
-elim_info_get_local_vars(ElimInfo) = ElimInfo ^ local_vars.
+:- func elim_info_get_local_data(elim_info) = list(mlds__defn).
+elim_info_get_local_data(ElimInfo) = ElimInfo ^ local_data.
:- func elim_info_get_env_type_name(elim_info) = mlds__type.
elim_info_get_env_type_name(ElimInfo) = ElimInfo ^ env_type_name.
@@ -1413,15 +1452,15 @@
elim_info_add_nested_func(NestedFunc, ElimInfo,
ElimInfo ^ nested_funcs := [NestedFunc | ElimInfo ^ nested_funcs]).
-:- pred elim_info_add_local_var(mlds__defn, elim_info, elim_info).
-:- mode elim_info_add_local_var(in, in, out) is det.
-elim_info_add_local_var(LocalVar, ElimInfo,
- ElimInfo ^ local_vars := [LocalVar | ElimInfo ^ local_vars]).
+:- pred elim_info_add_local_data(mlds__defn, elim_info, elim_info).
+:- mode elim_info_add_local_data(in, in, out) is det.
+elim_info_add_local_data(LocalVar, ElimInfo,
+ ElimInfo ^ local_data := [LocalVar | ElimInfo ^ local_data]).
:- pred elim_info_finish(elim_info, list(mlds__defn), list(mlds__defn)).
:- mode elim_info_finish(in, out, out) is det.
-elim_info_finish(ElimInfo, Funcs, LocalVars) :-
+elim_info_finish(ElimInfo, Funcs, Locals) :-
Funcs = list__reverse(ElimInfo ^ nested_funcs),
- LocalVars = list__reverse(ElimInfo ^ local_vars).
+ Locals = list__reverse(ElimInfo ^ local_data).
%-----------------------------------------------------------------------------%
--- compiler/mlds_to_c.m 2000/10/06 10:18:26 1.58
+++ compiler/mlds_to_c.m 2000/10/22 02:26:12
@@ -759,14 +759,7 @@
% Now output the declaration for this mlds__defn.
%
mlds_indent(Context, Indent),
- ( { Name = data(_) } ->
- % XXX for private data and private functions,
- % we should use "static"
- io__write_string("extern ")
- ;
- []
- ),
- mlds_output_decl_flags(Flags),
+ mlds_output_decl_flags(Flags, forward_decl, Name),
mlds_output_decl_body(Indent, qual(ModuleName, Name), Context,
DefnBody)
).
@@ -846,7 +839,7 @@
[]
),
mlds_indent(Context, Indent),
- mlds_output_decl_flags(Flags),
+ mlds_output_decl_flags(Flags, definition, Name),
mlds_output_defn_body(Indent, qual(ModuleName, Name), Context,
DefnBody).
@@ -1646,41 +1639,88 @@
% Code to output declaration specifiers
%
-:- pred mlds_output_decl_flags(mlds__decl_flags, io__state, io__state).
-:- mode mlds_output_decl_flags(in, di, uo) is det.
+:- type decl_or_defn
+ ---> forward_decl
+ ; definition.
-mlds_output_decl_flags(Flags) -->
- mlds_output_access(access(Flags)),
- mlds_output_per_instance(per_instance(Flags)),
+:- pred mlds_output_decl_flags(mlds__decl_flags, decl_or_defn,
+ mlds__entity_name, io__state, io__state).
+:- mode mlds_output_decl_flags(in, in, in, di, uo) is det.
+
+mlds_output_decl_flags(Flags, DeclOrDefn, Name) -->
+ %
+ % mlds_output_extern_or_static handles both the
+ % `access' and the `per_instance' fields of the mlds__decl_flags.
+ % We have to handle them together because C overloads `static'
+ % to mean both `private' and `one_copy', rather than having
+ % separate keywords for each. To make it clear which MLDS
+ % construct each `static' keyword means, we precede the `static'
+ % without (optionally-enabled) comments saying whether it is
+ % `private', `one_copy', or both.
+ %
+ mlds_output_access_comment(access(Flags)),
+ mlds_output_per_instance_comment(per_instance(Flags)),
+ mlds_output_extern_or_static(access(Flags), per_instance(Flags),
+ DeclOrDefn, Name),
mlds_output_virtuality(virtuality(Flags)),
mlds_output_finality(finality(Flags)),
mlds_output_constness(constness(Flags)),
mlds_output_abstractness(abstractness(Flags)).
-:- pred mlds_output_access(access, io__state, io__state).
-:- mode mlds_output_access(in, di, uo) is det.
+:- pred mlds_output_access_comment(access, io__state, io__state).
+:- mode mlds_output_access_comment(in, di, uo) is det.
-mlds_output_access(Access) -->
+mlds_output_access_comment(Access) -->
globals__io_lookup_bool_option(auto_comments, Comments),
( { Comments = yes } ->
- mlds_output_access_2(Access)
+ mlds_output_access_comment_2(Access)
;
[]
).
-:- pred mlds_output_access_2(access, io__state, io__state).
-:- mode mlds_output_access_2(in, di, uo) is det.
+:- pred mlds_output_access_comment_2(access, io__state, io__state).
+:- mode mlds_output_access_comment_2(in, di, uo) is det.
-mlds_output_access_2(public) --> [].
-mlds_output_access_2(private) --> io__write_string("/* private: */ ").
-mlds_output_access_2(protected) --> io__write_string("/* protected: */ ").
-mlds_output_access_2(default) --> io__write_string("/* default access */ ").
+mlds_output_access_comment_2(public) --> [].
+mlds_output_access_comment_2(private) --> io__write_string("/* private: */ ").
+mlds_output_access_comment_2(protected) --> io__write_string("/* protected: */ ").
+mlds_output_access_comment_2(default) --> io__write_string("/* default access */ ").
-:- pred mlds_output_per_instance(per_instance, io__state, io__state).
-:- mode mlds_output_per_instance(in, di, uo) is det.
+:- pred mlds_output_per_instance_comment(per_instance, io__state, io__state).
+:- mode mlds_output_per_instance_comment(in, di, uo) is det.
-mlds_output_per_instance(one_copy) --> io__write_string("static ").
-mlds_output_per_instance(per_instance) --> [].
+mlds_output_per_instance_comment(PerInstance) -->
+ globals__io_lookup_bool_option(auto_comments, Comments),
+ ( { Comments = yes } ->
+ mlds_output_per_instance_comment_2(PerInstance)
+ ;
+ []
+ ).
+
+:- pred mlds_output_per_instance_comment_2(per_instance, io__state, io__state).
+:- mode mlds_output_per_instance_comment_2(in, di, uo) is det.
+
+mlds_output_per_instance_comment_2(per_instance) --> [].
+mlds_output_per_instance_comment_2(one_copy) --> io__write_string("/* one_copy */ ").
+
+:- pred mlds_output_extern_or_static(access, per_instance, decl_or_defn,
+ mlds__entity_name, io__state, io__state).
+:- mode mlds_output_extern_or_static(in, in, in, in, di, uo) is det.
+
+mlds_output_extern_or_static(Access, PerInstance, DeclOrDefn, Name) -->
+ (
+ { Access = private ; PerInstance = one_copy },
+ { Name \= type(_, _) }
+ ->
+ io__write_string("static ")
+ ;
+ { DeclOrDefn = forward_decl },
+ { Name = data(_) }
+ ->
+ io__write_string("extern ")
+ ;
+ []
+ ).
:- pred mlds_output_virtuality(virtuality, io__state, io__state).
:- mode mlds_output_virtuality(in, di, uo) is det.
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
| -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list