source: src/Parser/StatementNode.cc@ 372b6d3

ADT ast-experimental
Last change on this file since 372b6d3 was c468150, checked in by Andrew Beach <ajbeach@…>, 3 years ago

Split up ParseNode.h so that headers match implementation. May have a bit less to include total because of it.

  • Property mode set to 100644
File size: 17.3 KB
RevLine 
[b87a5ed]1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
[4e7171f]7// StatementNode.cc -- Transform from parse data-structures to AST data-structures, usually deleting the parse
8// data-structure after the transformation.
[b87a5ed]9//
10// Author : Rodolfo G. Esteves
11// Created On : Sat May 16 14:59:41 2015
[bb7422a]12// Last Modified By : Andrew Beach
13// Last Modified On : Tue Apr 4 11:40:00 2023
14// Update Count : 427
[b87a5ed]15//
16
[c468150]17#include "StatementNode.h"
18
[e3e16bc]19#include <cassert> // for assert, strict_dynamic_cast, assertf
[d180746]20#include <memory> // for unique_ptr
21#include <string> // for string
[51b73452]22
[bb7422a]23#include "AST/Label.hpp" // for Label
24#include "AST/Stmt.hpp" // for Stmt, AsmStmt, BranchStmt, CaseCla...
[d180746]25#include "Common/SemanticError.h" // for SemanticError
26#include "Common/utility.h" // for maybeMoveBuild, maybeBuild
[c468150]27#include "DeclarationNode.h" // for DeclarationNode
28#include "ExpressionNode.h" // for ExpressionNode
[d180746]29#include "parserutility.h" // for notZeroExpr
30
31class Declaration;
[51b73452]32
33using namespace std;
34
[61fc4f6]35StatementNode::StatementNode( DeclarationNode * decl ) {
[c0aa336]36 assert( decl );
[61fc4f6]37 DeclarationNode * agg = decl->extractAggregate();
[c0aa336]38 if ( agg ) {
[bb7422a]39 StatementNode * nextStmt = new StatementNode(
40 new ast::DeclStmt( decl->location, maybeBuild( decl ) ) );
[c0aa336]41 set_next( nextStmt );
42 if ( decl->get_next() ) {
43 get_next()->set_next( new StatementNode( dynamic_cast< DeclarationNode * >(decl->get_next()) ) );
44 decl->set_next( 0 );
[1d4580a]45 } // if
46 } else {
[c0aa336]47 if ( decl->get_next() ) {
48 set_next( new StatementNode( dynamic_cast< DeclarationNode * >( decl->get_next() ) ) );
49 decl->set_next( 0 );
50 } // if
51 agg = decl;
[1d4580a]52 } // if
[bb7422a]53 // Local copy to avoid accessing the pointer after it is moved from.
54 CodeLocation declLocation = agg->location;
55 stmt.reset( new ast::DeclStmt( declLocation, maybeMoveBuild( agg ) ) );
[c0aa336]56} // StatementNode::StatementNode
[1d4580a]57
[c468150]58StatementNode * StatementNode::add_label(
59 const CodeLocation & location,
60 const std::string * name,
61 DeclarationNode * attr ) {
62 stmt->labels.emplace_back( location,
63 *name,
64 attr ? std::move( attr->attributes )
65 : std::vector<ast::ptr<ast::Attribute>>{} );
66 delete attr;
67 delete name;
68 return this;
69}
70
[61fc4f6]71StatementNode * StatementNode::append_last_case( StatementNode * stmt ) {
72 StatementNode * prev = this;
[1d4580a]73 // find end of list and maintain previous pointer
74 for ( StatementNode * curr = prev; curr != nullptr; curr = (StatementNode *)curr->get_next() ) {
[61fc4f6]75 StatementNode * node = strict_dynamic_cast< StatementNode * >(curr);
[bb7422a]76 assert( nullptr == node->stmt.get() );
77 assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) );
[8cc5cb0]78 prev = curr;
79 } // for
[e82aa9df]80 // convert from StatementNode list to Statement list
[61fc4f6]81 StatementNode * node = dynamic_cast< StatementNode * >(prev);
[bb7422a]82 std::vector<ast::ptr<ast::Stmt>> stmts;
[7ecbb7e]83 buildMoveList( stmt, stmts );
[e82aa9df]84 // splice any new Statements to end of current Statements
[bb7422a]85 auto caseStmt = strict_dynamic_cast<ast::CaseClause *>( node->clause.get() );
86 for ( auto const & newStmt : stmts ) {
87 caseStmt->stmts.emplace_back( newStmt );
88 }
89 stmts.clear();
[8cc5cb0]90 return this;
[401e61f]91} // StatementNode::append_last_case
[8cc5cb0]92
[bb7422a]93ast::Stmt * build_expr( CodeLocation const & location, ExpressionNode * ctl ) {
94 if ( ast::Expr * e = maybeMoveBuild( ctl ) ) {
95 return new ast::ExprStmt( location, e );
96 } else {
97 return new ast::NullStmt( location );
98 }
[401e61f]99} // build_expr
[8cc5cb0]100
[bb7422a]101static ast::Expr * build_if_control( CondCtl * ctl,
102 std::vector<ast::ptr<ast::Stmt>> & inits ) {
103 assert( inits.empty() );
104 if ( nullptr != ctl->init ) {
105 buildMoveList( ctl->init, inits );
[936e9f4]106 } // if
107
[bb7422a]108 ast::Expr * cond = nullptr;
[a01f7c94]109 if ( ctl->condition ) {
110 // compare the provided condition against 0
[702e826]111 cond = notZeroExpr( maybeMoveBuild( ctl->condition ) );
[a01f7c94]112 } else {
[bb7422a]113 for ( ast::ptr<ast::Stmt> & stmt : inits ) {
[a01f7c94]114 // build the && of all of the declared variables compared against 0
[bb7422a]115 //auto declStmt = strict_dynamic_cast<ast::DeclStmt *>( stmt );
116 auto declStmt = stmt.strict_as<ast::DeclStmt>();
117 //ast::DeclWithType * dwt = strict_dynamic_cast<ast::DeclWithType *>( declStmt->decl );
118 auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
119 ast::Expr * nze = notZeroExpr( new ast::VariableExpr( dwt->location, dwt ) );
120 cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
[a01f7c94]121 }
122 }
[6d49ea3]123 delete ctl;
[ee3c93d]124 return cond;
[401e61f]125} // build_if_control
[ee3c93d]126
[bb7422a]127ast::Stmt * build_if( const CodeLocation & location, CondCtl * ctl, StatementNode * then, StatementNode * else_ ) {
128 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
129 ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
[436bbe5]130
[bb7422a]131 std::vector<ast::ptr<ast::Stmt>> aststmt;
132 buildMoveList( then, aststmt );
[436bbe5]133 assert( aststmt.size() == 1 );
[bb7422a]134 ast::Stmt const * astthen = aststmt.front().release();
[436bbe5]135
[bb7422a]136 ast::Stmt const * astelse = nullptr;
[436bbe5]137 if ( else_ ) {
[bb7422a]138 std::vector<ast::ptr<ast::Stmt>> aststmt;
139 buildMoveList( else_, aststmt );
[436bbe5]140 assert( aststmt.size() == 1 );
[bb7422a]141 astelse = aststmt.front().release();
[ee3c93d]142 } // if
143
[bb7422a]144 return new ast::IfStmt( location, astcond, astthen, astelse,
145 std::move( astinit )
146 );
[401e61f]147} // build_if
[2f22cc4]148
[bb7422a]149// Temporary work around. Split StmtClause off from StatementNode.
150template<typename clause_t>
151static void buildMoveClauseList( StatementNode * firstNode,
152 std::vector<ast::ptr<clause_t>> & output ) {
153 SemanticErrorException errors;
154 std::back_insert_iterator<std::vector<ast::ptr<clause_t>>>
155 out( output );
156 StatementNode * cur = firstNode;
157
158 while ( cur ) {
159 try {
160 auto clause = cur->clause.release();
161 if ( auto result = dynamic_cast<clause_t *>( clause ) ) {
162 *out++ = result;
163 } else {
164 assertf(false, __PRETTY_FUNCTION__ );
165 SemanticError( cur->location, "type specifier declaration in forall clause is currently unimplemented." );
166 } // if
167 } catch( SemanticErrorException & e ) {
168 errors.append( e );
169 } // try
170 ParseNode * temp = cur->get_next();
171 // Should not return nullptr, then it is non-homogeneous:
172 cur = dynamic_cast<StatementNode *>( temp );
173 if ( !cur && temp ) {
174 SemanticError( temp->location, "internal error, non-homogeneous nodes founds in buildList processing." );
175 } // if
176 } // while
177 if ( ! errors.isEmpty() ) {
178 throw errors;
179 } // if
180 // Usually in the wrapper.
181 delete firstNode;
182}
183
184ast::Stmt * build_switch( const CodeLocation & location, bool isSwitch, ExpressionNode * ctl, StatementNode * stmt ) {
185 std::vector<ast::ptr<ast::CaseClause>> aststmt;
186 buildMoveClauseList( stmt, aststmt );
187 // If it is not a switch it is a choose statement.
188 if ( ! isSwitch ) {
189 for ( ast::ptr<ast::CaseClause> & stmt : aststmt ) {
190 // Code after "case" is the end of case list.
191 if ( !stmt->stmts.empty() ) {
192 auto mutStmt = ast::mutate( stmt.get() );
193 // I believe the stmts are actually always one block.
194 auto stmts = mutStmt->stmts.front().get_and_mutate();
195 auto block = strict_dynamic_cast<ast::CompoundStmt *>( stmts );
196 block->kids.push_back( new ast::BranchStmt( block->location,
197 ast::BranchStmt::Break,
198 ast::Label( block->location ) ) );
199 stmt = mutStmt;
[6a276a0]200 } // if
201 } // for
202 } // if
[436bbe5]203 // aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
[bb7422a]204 return new ast::SwitchStmt( location,
205 maybeMoveBuild( ctl ), std::move( aststmt ) );
[401e61f]206} // build_switch
207
[bb7422a]208ast::CaseClause * build_case( ExpressionNode * ctl ) {
209 // stmt starts empty and then added to
210 auto expr = maybeMoveBuild( ctl );
211 return new ast::CaseClause( expr->location, expr, {} );
[401e61f]212} // build_case
213
[bb7422a]214ast::CaseClause * build_default( const CodeLocation & location ) {
215 // stmt starts empty and then added to
216 return new ast::CaseClause( location, nullptr, {} );
[401e61f]217} // build_default
218
[bb7422a]219ast::Stmt * build_while( const CodeLocation & location, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) {
220 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
221 ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
[3b0bc16]222
[bb7422a]223 std::vector<ast::ptr<ast::Stmt>> aststmt; // loop body, compound created if empty
224 buildMoveList( stmt, aststmt );
[3b0bc16]225 assert( aststmt.size() == 1 );
226
[bb7422a]227 std::vector<ast::ptr<ast::Stmt>> astelse; // else clause, maybe empty
228 buildMoveList( else_, astelse );
229 assert( astelse.size() <= 1 );
230
231 return new ast::WhileDoStmt( location,
232 astcond,
233 aststmt.front(),
234 astelse.empty() ? nullptr : astelse.front().release(),
235 std::move( astinit ),
[3e94a23]236 ast::While
[bb7422a]237 );
[401e61f]238} // build_while
[2f22cc4]239
[bb7422a]240ast::Stmt * build_do_while( const CodeLocation & location, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) {
241 std::vector<ast::ptr<ast::Stmt>> aststmt; // loop body, compound created if empty
242 buildMoveList( stmt, aststmt );
[436bbe5]243 assert( aststmt.size() == 1 ); // compound created if empty
[3b0bc16]244
[bb7422a]245 std::vector<ast::ptr<ast::Stmt>> astelse; // else clause, maybe empty
246 buildMoveList( else_, astelse );
247 assert( astelse.size() <= 1 );
[ee3c93d]248
[4e7171f]249 // do-while cannot have declarations in the contitional, so init is always empty
[bb7422a]250 return new ast::WhileDoStmt( location,
251 notZeroExpr( maybeMoveBuild( ctl ) ),
252 aststmt.front(),
253 astelse.empty() ? nullptr : astelse.front().release(),
254 {},
[3e94a23]255 ast::DoWhile
[bb7422a]256 );
[401e61f]257} // build_do_while
[2f22cc4]258
[bb7422a]259ast::Stmt * build_for( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) {
260 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
[436bbe5]261 buildMoveList( forctl->init, astinit );
[2f22cc4]262
[bb7422a]263 ast::Expr * astcond = nullptr; // maybe empty
[702e826]264 astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) );
[2f22cc4]265
[bb7422a]266 ast::Expr * astincr = nullptr; // maybe empty
[702e826]267 astincr = maybeMoveBuild( forctl->change );
[436bbe5]268 delete forctl;
[2f22cc4]269
[bb7422a]270 std::vector<ast::ptr<ast::Stmt>> aststmt; // loop body, compound created if empty
271 buildMoveList( stmt, aststmt );
[3b0bc16]272 assert( aststmt.size() == 1 );
273
[bb7422a]274 std::vector<ast::ptr<ast::Stmt>> astelse; // else clause, maybe empty
275 buildMoveList( else_, astelse );
276 assert( astelse.size() <= 1 );
277
278 return new ast::ForStmt( location,
279 std::move( astinit ),
280 astcond,
281 astincr,
282 aststmt.front(),
283 astelse.empty() ? nullptr : astelse.front().release()
284 );
[401e61f]285} // build_for
[2f22cc4]286
[bb7422a]287ast::Stmt * build_branch( const CodeLocation & location, ast::BranchStmt::Kind kind ) {
288 return new ast::BranchStmt( location,
289 kind,
290 ast::Label( location )
291 );
[401e61f]292} // build_branch
293
[bb7422a]294ast::Stmt * build_branch( const CodeLocation & location, string * identifier, ast::BranchStmt::Kind kind ) {
295 ast::Stmt * ret = new ast::BranchStmt( location,
296 kind,
297 ast::Label( location, *identifier )
298 );
299 delete identifier; // allocated by lexer
[ab57786]300 return ret;
[401e61f]301} // build_branch
302
[bb7422a]303ast::Stmt * build_computedgoto( ExpressionNode * ctl ) {
304 ast::Expr * expr = maybeMoveBuild( ctl );
305 return new ast::BranchStmt( expr->location, expr );
[401e61f]306} // build_computedgoto
[8cc5cb0]307
[bb7422a]308ast::Stmt * build_return( const CodeLocation & location, ExpressionNode * ctl ) {
309 std::vector<ast::ptr<ast::Expr>> exps;
[7ecbb7e]310 buildMoveList( ctl, exps );
[bb7422a]311 return new ast::ReturnStmt( location,
312 exps.size() > 0 ? exps.back().release() : nullptr
313 );
[401e61f]314} // build_return
[daf1af8]315
[bb7422a]316static ast::Stmt * build_throw_stmt(
317 const CodeLocation & location,
318 ExpressionNode * ctl,
319 ast::ExceptionKind kind ) {
320 std::vector<ast::ptr<ast::Expr>> exps;
[7ecbb7e]321 buildMoveList( ctl, exps );
[436bbe5]322 assertf( exps.size() < 2, "CFA internal error: leaking memory" );
[bb7422a]323 return new ast::ThrowStmt( location,
324 kind,
325 !exps.empty() ? exps.back().release() : nullptr,
326 (ast::Expr *)nullptr
327 );
328}
329
330ast::Stmt * build_throw( const CodeLocation & loc, ExpressionNode * ctl ) {
331 return build_throw_stmt( loc, ctl, ast::Terminate );
[401e61f]332} // build_throw
[daf1af8]333
[bb7422a]334ast::Stmt * build_resume( const CodeLocation & loc, ExpressionNode * ctl ) {
335 return build_throw_stmt( loc, ctl, ast::Resume );
[401e61f]336} // build_resume
[daf1af8]337
[bb7422a]338ast::Stmt * build_resume_at( ExpressionNode * ctl, ExpressionNode * target ) {
[794c15b]339 (void)ctl;
340 (void)target;
341 assertf( false, "resume at (non-local throw) is not yet supported," );
[401e61f]342} // build_resume_at
[321f55d]343
[bb7422a]344ast::Stmt * build_try( const CodeLocation & location, StatementNode * try_, StatementNode * catch_, StatementNode * finally_ ) {
345 std::vector<ast::ptr<ast::CatchClause>> aststmt;
346 buildMoveClauseList( catch_, aststmt );
347 ast::CompoundStmt * tryBlock = strict_dynamic_cast<ast::CompoundStmt *>( maybeMoveBuild( try_ ) );
348 ast::FinallyClause * finallyBlock = nullptr;
349 if ( finally_ ) {
350 finallyBlock = dynamic_cast<ast::FinallyClause *>( finally_->clause.release() );
351 }
352 return new ast::TryStmt( location,
353 tryBlock,
354 std::move( aststmt ),
355 finallyBlock
356 );
[401e61f]357} // build_try
358
[bb7422a]359ast::CatchClause * build_catch( const CodeLocation & location, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
360 std::vector<ast::ptr<ast::Stmt>> aststmt;
361 buildMoveList( body, aststmt );
[436bbe5]362 assert( aststmt.size() == 1 );
[bb7422a]363 return new ast::CatchClause( location,
364 kind,
365 maybeMoveBuild( decl ),
366 maybeMoveBuild( cond ),
367 aststmt.front().release()
368 );
[401e61f]369} // build_catch
370
[bb7422a]371ast::FinallyClause * build_finally( const CodeLocation & location, StatementNode * stmt ) {
372 std::vector<ast::ptr<ast::Stmt>> aststmt;
373 buildMoveList( stmt, aststmt );
[436bbe5]374 assert( aststmt.size() == 1 );
[bb7422a]375 return new ast::FinallyClause( location,
376 aststmt.front().strict_as<ast::CompoundStmt>()
377 );
[401e61f]378} // build_finally
[1d4580a]379
[835d6e8]380ast::SuspendStmt * build_suspend( const CodeLocation & location, StatementNode * then, ast::SuspendStmt::Kind kind ) {
[bb7422a]381 std::vector<ast::ptr<ast::Stmt>> stmts;
382 buildMoveList( then, stmts );
383 ast::CompoundStmt const * then2 = nullptr;
[427854b]384 if(!stmts.empty()) {
385 assert( stmts.size() == 1 );
[bb7422a]386 then2 = stmts.front().strict_as<ast::CompoundStmt>();
[427854b]387 }
[835d6e8]388 return new ast::SuspendStmt( location, then2, kind );
[9fd9d015]389} // build_suspend
[427854b]390
[bb7422a]391ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
392 auto clause = new ast::WaitForClause( location );
393 clause->target_func = maybeBuild( targetExpr );
394 clause->stmt = maybeMoveBuild( stmt );
395 clause->cond = notZeroExpr( maybeMoveBuild( when ) );
[2065609]396
397 ExpressionNode * next = dynamic_cast<ExpressionNode *>( targetExpr->get_next() );
398 targetExpr->set_next( nullptr );
[bb7422a]399 buildMoveList( next, clause->target_args );
[2065609]400
[135b431]401 delete targetExpr;
402
[bb7422a]403 existing->clauses.insert( existing->clauses.begin(), clause );
[135b431]404
[9fd9d015]405 return existing;
[401e61f]406} // build_waitfor
[135b431]407
[bb7422a]408ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
409 existing->else_stmt = maybeMoveBuild( stmt );
410 existing->else_cond = notZeroExpr( maybeMoveBuild( when ) );
[135b431]411
[bb7422a]412 (void)location;
[9fd9d015]413 return existing;
414} // build_waitfor_else
[135b431]415
[bb7422a]416ast::WaitForStmt * build_waitfor_timeout( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
417 existing->timeout_time = maybeMoveBuild( timeout );
418 existing->timeout_stmt = maybeMoveBuild( stmt );
419 existing->timeout_cond = notZeroExpr( maybeMoveBuild( when ) );
[135b431]420
[bb7422a]421 (void)location;
[9fd9d015]422 return existing;
[401e61f]423} // build_waitfor_timeout
[135b431]424
[bb7422a]425ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
426 std::vector<ast::ptr<ast::Expr>> e;
[a378ca7]427 buildMoveList( exprs, e );
[bb7422a]428 ast::Stmt * s = maybeMoveBuild( stmt );
429 return new ast::DeclStmt( location, new ast::WithStmt( location, std::move( e ), s ) );
[401e61f]430} // build_with
[a378ca7]431
[bb7422a]432ast::Stmt * build_compound( const CodeLocation & location, StatementNode * first ) {
433 auto cs = new ast::CompoundStmt( location );
434 buildMoveList( first, cs->kids );
[b87a5ed]435 return cs;
[401e61f]436} // build_compound
[51b73452]437
[a025ea8]438// A single statement in a control structure is always converted to a compound statement so subsequent generated code
439// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
440// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
441// conical form for code generation.
[bb7422a]442StatementNode * maybe_build_compound( const CodeLocation & location, StatementNode * first ) {
[a025ea8]443 // Optimization: if the control-structure statement is a compound statement, do not wrap it.
444 // e.g., if (...) {...} do not wrap the existing compound statement.
[bb7422a]445 if ( !dynamic_cast<ast::CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
446 return new StatementNode( build_compound( location, first ) );
[a025ea8]447 } // if
448 return first;
449} // maybe_build_compound
450
[f135b50]451// Question
[bb7422a]452ast::Stmt * build_asm( const CodeLocation & location, bool voltile, ast::Expr * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
453 std::vector<ast::ptr<ast::Expr>> out, in;
454 std::vector<ast::ptr<ast::ConstantExpr>> clob;
[e82aa9df]455
[7ecbb7e]456 buildMoveList( output, out );
457 buildMoveList( input, in );
458 buildMoveList( clobber, clob );
[bb7422a]459 return new ast::AsmStmt( location,
460 voltile,
461 instruction,
462 std::move( out ),
463 std::move( in ),
464 std::move( clob ),
465 gotolabels ? gotolabels->labels : std::vector<ast::Label>()
466 );
[401e61f]467} // build_asm
[7f5566b]468
[bb7422a]469ast::Stmt * build_directive( const CodeLocation & location, string * directive ) {
470 auto stmt = new ast::DirectiveStmt( location, *directive );
471 delete directive;
472 return stmt;
[401e61f]473} // build_directive
[61fc4f6]474
[bb7422a]475ast::Stmt * build_mutex( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
476 std::vector<ast::ptr<ast::Expr>> expList;
[de52331]477 buildMoveList( exprs, expList );
[bb7422a]478 ast::Stmt * body = maybeMoveBuild( stmt );
479 return new ast::MutexStmt( location, body, std::move( expList ) );
[de52331]480} // build_mutex
481
[51b73452]482// Local Variables: //
[b87a5ed]483// tab-width: 4 //
484// mode: c++ //
485// compile-command: "make install" //
[51b73452]486// End: //
Note: See TracBrowser for help on using the repository browser.