source: src/ResolvExpr/AlternativeFinder.cc@ 4b6ef70

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors deferred_resn demangler enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr new-env no_list persistent-indexer pthread-emulation qualifiedEnum resolv-new with_gc
Last change on this file since 4b6ef70 was 4b6ef70, checked in by Aaron Moss <a3moss@…>, 8 years ago

Fix one tuple bug in resolver refactor

  • Property mode set to 100644
File size: 65.4 KB
Line 
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//
7// AlternativeFinder.cc --
8//
9// Author : Richard C. Bilson
10// Created On : Sat May 16 23:52:08 2015
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Mon Aug 28 13:47:24 2017
13// Update Count : 32
14//
15
16#include <algorithm> // for copy
17#include <cassert> // for strict_dynamic_cast, assert, assertf
18#include <iostream> // for operator<<, cerr, ostream, endl
19#include <iterator> // for back_insert_iterator, back_inserter
20#include <list> // for _List_iterator, list, _List_const_...
21#include <map> // for _Rb_tree_iterator, map, _Rb_tree_c...
22#include <memory> // for allocator_traits<>::value_type
23#include <utility> // for pair
24#include <vector> // for vector
25
26#include "Alternative.h" // for AltList, Alternative
27#include "AlternativeFinder.h"
28#include "Common/SemanticError.h" // for SemanticError
29#include "Common/utility.h" // for deleteAll, printAll, CodeLocation
30#include "Cost.h" // for Cost, Cost::zero, operator<<, Cost...
31#include "InitTweak/InitTweak.h" // for getFunctionName
32#include "RenameVars.h" // for RenameVars, global_renamer
33#include "ResolveTypeof.h" // for resolveTypeof
34#include "Resolver.h" // for resolveStmtExpr
35#include "SymTab/Indexer.h" // for Indexer
36#include "SymTab/Mangler.h" // for Mangler
37#include "SymTab/Validate.h" // for validateType
38#include "SynTree/Constant.h" // for Constant
39#include "SynTree/Declaration.h" // for DeclarationWithType, TypeDecl, Dec...
40#include "SynTree/Expression.h" // for Expression, CastExpr, NameExpr
41#include "SynTree/Initializer.h" // for SingleInit, operator<<, Designation
42#include "SynTree/SynTree.h" // for UniqueId
43#include "SynTree/Type.h" // for Type, FunctionType, PointerType
44#include "Tuples/Explode.h" // for explode
45#include "Tuples/Tuples.h" // for isTtype, handleTupleAssignment
46#include "Unify.h" // for unify
47#include "typeops.h" // for adjustExprType, polyCost, castCost
48
49extern bool resolvep;
50#define PRINT( text ) if ( resolvep ) { text }
51//#define DEBUG_COST
52
53namespace ResolvExpr {
54 Expression *resolveInVoidContext( Expression *expr, const SymTab::Indexer &indexer, TypeEnvironment &env ) {
55 CastExpr *castToVoid = new CastExpr( expr );
56
57 AlternativeFinder finder( indexer, env );
58 finder.findWithAdjustment( castToVoid );
59
60 // it's a property of the language that a cast expression has either 1 or 0 interpretations; if it has 0
61 // interpretations, an exception has already been thrown.
62 assert( finder.get_alternatives().size() == 1 );
63 CastExpr *newExpr = dynamic_cast< CastExpr* >( finder.get_alternatives().front().expr );
64 assert( newExpr );
65 env = finder.get_alternatives().front().env;
66 return newExpr->get_arg()->clone();
67 }
68
69 Cost sumCost( const AltList &in ) {
70 Cost total = Cost::zero;
71 for ( AltList::const_iterator i = in.begin(); i != in.end(); ++i ) {
72 total += i->cost;
73 }
74 return total;
75 }
76
77 namespace {
78 void printAlts( const AltList &list, std::ostream &os, int indent = 0 ) {
79 for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
80 i->print( os, indent );
81 os << std::endl;
82 }
83 }
84
85 void makeExprList( const AltList &in, std::list< Expression* > &out ) {
86 for ( AltList::const_iterator i = in.begin(); i != in.end(); ++i ) {
87 out.push_back( i->expr->clone() );
88 }
89 }
90
91 struct PruneStruct {
92 bool isAmbiguous;
93 AltList::iterator candidate;
94 PruneStruct() {}
95 PruneStruct( AltList::iterator candidate ): isAmbiguous( false ), candidate( candidate ) {}
96 };
97
98 /// Prunes a list of alternatives down to those that have the minimum conversion cost for a given return type; skips ambiguous interpretations
99 template< typename InputIterator, typename OutputIterator >
100 void pruneAlternatives( InputIterator begin, InputIterator end, OutputIterator out ) {
101 // select the alternatives that have the minimum conversion cost for a particular set of result types
102 std::map< std::string, PruneStruct > selected;
103 for ( AltList::iterator candidate = begin; candidate != end; ++candidate ) {
104 PruneStruct current( candidate );
105 std::string mangleName;
106 {
107 Type * newType = candidate->expr->get_result()->clone();
108 candidate->env.apply( newType );
109 mangleName = SymTab::Mangler::mangle( newType );
110 delete newType;
111 }
112 std::map< std::string, PruneStruct >::iterator mapPlace = selected.find( mangleName );
113 if ( mapPlace != selected.end() ) {
114 if ( candidate->cost < mapPlace->second.candidate->cost ) {
115 PRINT(
116 std::cerr << "cost " << candidate->cost << " beats " << mapPlace->second.candidate->cost << std::endl;
117 )
118 selected[ mangleName ] = current;
119 } else if ( candidate->cost == mapPlace->second.candidate->cost ) {
120 PRINT(
121 std::cerr << "marking ambiguous" << std::endl;
122 )
123 mapPlace->second.isAmbiguous = true;
124 }
125 } else {
126 selected[ mangleName ] = current;
127 }
128 }
129
130 PRINT(
131 std::cerr << "there are " << selected.size() << " alternatives before elimination" << std::endl;
132 )
133
134 // accept the alternatives that were unambiguous
135 for ( std::map< std::string, PruneStruct >::iterator target = selected.begin(); target != selected.end(); ++target ) {
136 if ( ! target->second.isAmbiguous ) {
137 Alternative &alt = *target->second.candidate;
138 alt.env.applyFree( alt.expr->get_result() );
139 *out++ = alt;
140 }
141 }
142 }
143
144 void renameTypes( Expression *expr ) {
145 expr->get_result()->accept( global_renamer );
146 }
147
148 void referenceToRvalueConversion( Expression *& expr ) {
149 if ( dynamic_cast< ReferenceType * >( expr->get_result() ) ) {
150 // cast away reference from expr
151 expr = new CastExpr( expr, expr->get_result()->stripReferences()->clone() );
152 }
153 }
154 } // namespace
155
156 template< typename InputIterator, typename OutputIterator >
157 void AlternativeFinder::findSubExprs( InputIterator begin, InputIterator end, OutputIterator out ) {
158 while ( begin != end ) {
159 AlternativeFinder finder( indexer, env );
160 finder.findWithAdjustment( *begin );
161 // XXX either this
162 //Designators::fixDesignations( finder, (*begin++)->get_argName() );
163 // or XXX this
164 begin++;
165 PRINT(
166 std::cerr << "findSubExprs" << std::endl;
167 printAlts( finder.alternatives, std::cerr );
168 )
169 *out++ = finder;
170 }
171 }
172
173 AlternativeFinder::AlternativeFinder( const SymTab::Indexer &indexer, const TypeEnvironment &env )
174 : indexer( indexer ), env( env ) {
175 }
176
177 void AlternativeFinder::find( Expression *expr, bool adjust, bool prune ) {
178 expr->accept( *this );
179 if ( alternatives.empty() ) {
180 throw SemanticError( "No reasonable alternatives for expression ", expr );
181 }
182 for ( AltList::iterator i = alternatives.begin(); i != alternatives.end(); ++i ) {
183 if ( adjust ) {
184 adjustExprType( i->expr->get_result(), i->env, indexer );
185 }
186 }
187 if ( prune ) {
188 PRINT(
189 std::cerr << "alternatives before prune:" << std::endl;
190 printAlts( alternatives, std::cerr );
191 )
192 AltList::iterator oldBegin = alternatives.begin();
193 pruneAlternatives( alternatives.begin(), alternatives.end(), front_inserter( alternatives ) );
194 if ( alternatives.begin() == oldBegin ) {
195 std::ostringstream stream;
196 AltList winners;
197 findMinCost( alternatives.begin(), alternatives.end(), back_inserter( winners ) );
198 stream << "Cannot choose between " << winners.size() << " alternatives for expression ";
199 expr->print( stream );
200 stream << "Alternatives are:";
201 printAlts( winners, stream, 8 );
202 throw SemanticError( stream.str() );
203 }
204 alternatives.erase( oldBegin, alternatives.end() );
205 PRINT(
206 std::cerr << "there are " << alternatives.size() << " alternatives after elimination" << std::endl;
207 )
208 }
209
210 // Central location to handle gcc extension keyword, etc. for all expression types.
211 for ( Alternative &iter: alternatives ) {
212 iter.expr->set_extension( expr->get_extension() );
213 iter.expr->location = expr->location;
214 } // for
215 }
216
217 void AlternativeFinder::findWithAdjustment( Expression *expr, bool prune ) {
218 find( expr, true, prune );
219 }
220
221 void AlternativeFinder::addAnonConversions( const Alternative & alt ) {
222 // adds anonymous member interpretations whenever an aggregate value type is seen.
223 // it's okay for the aggregate expression to have reference type -- cast it to the base type to treat the aggregate as the referenced value
224 std::unique_ptr<Expression> aggrExpr( alt.expr->clone() );
225 alt.env.apply( aggrExpr->get_result() );
226 Type * aggrType = aggrExpr->get_result();
227 if ( dynamic_cast< ReferenceType * >( aggrType ) ) {
228 aggrType = aggrType->stripReferences();
229 aggrExpr.reset( new CastExpr( aggrExpr.release(), aggrType->clone() ) );
230 }
231
232 if ( StructInstType *structInst = dynamic_cast< StructInstType* >( aggrExpr->get_result() ) ) {
233 NameExpr nameExpr( "" );
234 addAggMembers( structInst, aggrExpr.get(), alt.cost+Cost::safe, alt.env, &nameExpr );
235 } else if ( UnionInstType *unionInst = dynamic_cast< UnionInstType* >( aggrExpr->get_result() ) ) {
236 NameExpr nameExpr( "" );
237 addAggMembers( unionInst, aggrExpr.get(), alt.cost+Cost::safe, alt.env, &nameExpr );
238 } // if
239 }
240
241 template< typename StructOrUnionType >
242 void AlternativeFinder::addAggMembers( StructOrUnionType *aggInst, Expression *expr, const Cost &newCost, const TypeEnvironment & env, Expression * member ) {
243 // by this point, member must be a name expr
244 NameExpr * nameExpr = dynamic_cast< NameExpr * >( member );
245 if ( ! nameExpr ) return;
246 const std::string & name = nameExpr->get_name();
247 std::list< Declaration* > members;
248 aggInst->lookup( name, members );
249
250 for ( std::list< Declaration* >::const_iterator i = members.begin(); i != members.end(); ++i ) {
251 if ( DeclarationWithType *dwt = dynamic_cast< DeclarationWithType* >( *i ) ) {
252 alternatives.push_back( Alternative( new MemberExpr( dwt, expr->clone() ), env, newCost ) );
253 renameTypes( alternatives.back().expr );
254 addAnonConversions( alternatives.back() ); // add anonymous member interpretations whenever an aggregate value type is seen as a member expression.
255 } else {
256 assert( false );
257 }
258 }
259 }
260
261 void AlternativeFinder::addTupleMembers( TupleType * tupleType, Expression *expr, const Cost &newCost, const TypeEnvironment & env, Expression * member ) {
262 if ( ConstantExpr * constantExpr = dynamic_cast< ConstantExpr * >( member ) ) {
263 // get the value of the constant expression as an int, must be between 0 and the length of the tuple type to have meaning
264 // xxx - this should be improved by memoizing the value of constant exprs
265 // during parsing and reusing that information here.
266 std::stringstream ss( constantExpr->get_constant()->get_value() );
267 int val = 0;
268 std::string tmp;
269 if ( ss >> val && ! (ss >> tmp) ) {
270 if ( val >= 0 && (unsigned int)val < tupleType->size() ) {
271 alternatives.push_back( Alternative( new TupleIndexExpr( expr->clone(), val ), env, newCost ) );
272 } // if
273 } // if
274 } else if ( NameExpr * nameExpr = dynamic_cast< NameExpr * >( member ) ) {
275 // xxx - temporary hack until 0/1 are int constants
276 if ( nameExpr->get_name() == "0" || nameExpr->get_name() == "1" ) {
277 std::stringstream ss( nameExpr->get_name() );
278 int val;
279 ss >> val;
280 alternatives.push_back( Alternative( new TupleIndexExpr( expr->clone(), val ), env, newCost ) );
281 }
282 } // if
283 }
284
285 void AlternativeFinder::visit( ApplicationExpr *applicationExpr ) {
286 alternatives.push_back( Alternative( applicationExpr->clone(), env, Cost::zero ) );
287 }
288
289 Cost computeConversionCost( Type * actualType, Type * formalType, const SymTab::Indexer &indexer, const TypeEnvironment & env ) {
290 PRINT(
291 std::cerr << std::endl << "converting ";
292 actualType->print( std::cerr, 8 );
293 std::cerr << std::endl << " to ";
294 formalType->print( std::cerr, 8 );
295 std::cerr << std::endl << "environment is: ";
296 env.print( std::cerr, 8 );
297 std::cerr << std::endl;
298 )
299 Cost convCost = conversionCost( actualType, formalType, indexer, env );
300 PRINT(
301 std::cerr << std::endl << "cost is" << convCost << std::endl;
302 )
303 if ( convCost == Cost::infinity ) {
304 return convCost;
305 }
306 convCost.incPoly( polyCost( formalType, env, indexer ) + polyCost( actualType, env, indexer ) );
307 return convCost;
308 }
309
310 Cost computeExpressionConversionCost( Expression *& actualExpr, Type * formalType, const SymTab::Indexer &indexer, const TypeEnvironment & env ) {
311 Cost convCost = computeConversionCost( actualExpr->result, formalType, indexer, env );
312 // if ( convCost != Cost::zero ) {
313
314 // xxx - temporary -- ignore poly cost, since this causes some polymorphic functions to be cast, which causes the specialize
315 // pass to try to specialize them, which currently does not work. Once that is fixed, remove the next 3 lines and uncomment the
316 // previous line.
317 Cost tmpCost = convCost;
318 tmpCost.incPoly( -tmpCost.get_polyCost() );
319 if ( tmpCost != Cost::zero ) {
320 Type *newType = formalType->clone();
321 env.apply( newType );
322 actualExpr = new CastExpr( actualExpr, newType );
323 // xxx - SHOULD be able to resolve this cast, but at the moment pointers are not castable to zero_t, but are implicitly convertible. This is clearly
324 // inconsistent, once this is fixed it should be possible to resolve the cast.
325 // xxx - this isn't working, it appears because type1 (the formal type) is seen as widenable, but it shouldn't be, because this makes the conversion from DT* to DT* since commontype(zero_t, DT*) is DT*, rather than just nothing.
326
327 // AlternativeFinder finder( indexer, env );
328 // finder.findWithAdjustment( actualExpr );
329 // assertf( finder.get_alternatives().size() > 0, "Somehow castable expression failed to find alternatives." );
330 // assertf( finder.get_alternatives().size() == 1, "Somehow got multiple alternatives for known cast expression." );
331 // Alternative & alt = finder.get_alternatives().front();
332 // delete actualExpr;
333 // actualExpr = alt.expr->clone();
334 }
335 return convCost;
336 }
337
338 Cost computeApplicationConversionCost( Alternative &alt, const SymTab::Indexer &indexer ) {
339 ApplicationExpr *appExpr = strict_dynamic_cast< ApplicationExpr* >( alt.expr );
340 PointerType *pointer = strict_dynamic_cast< PointerType* >( appExpr->get_function()->get_result() );
341 FunctionType *function = strict_dynamic_cast< FunctionType* >( pointer->get_base() );
342
343 Cost convCost = Cost::zero;
344 std::list< DeclarationWithType* >& formals = function->get_parameters();
345 std::list< DeclarationWithType* >::iterator formal = formals.begin();
346 std::list< Expression* >& actuals = appExpr->get_args();
347
348 for ( std::list< Expression* >::iterator actualExpr = actuals.begin(); actualExpr != actuals.end(); ++actualExpr ) {
349 Type * actualType = (*actualExpr)->get_result();
350 PRINT(
351 std::cerr << "actual expression:" << std::endl;
352 (*actualExpr)->print( std::cerr, 8 );
353 std::cerr << "--- results are" << std::endl;
354 actualType->print( std::cerr, 8 );
355 )
356 if ( formal == formals.end() ) {
357 if ( function->get_isVarArgs() ) {
358 convCost.incUnsafe();
359 // convert reference-typed expressions to value-typed expressions
360 referenceToRvalueConversion( *actualExpr );
361 continue;
362 } else {
363 return Cost::infinity;
364 }
365 }
366 Type * formalType = (*formal)->get_type();
367 PRINT(
368 std::cerr << std::endl << "converting ";
369 actualType->print( std::cerr, 8 );
370 std::cerr << std::endl << " to ";
371 formalType->print( std::cerr, 8 );
372 std::cerr << std::endl << "environment is: ";
373 alt.env.print( std::cerr, 8 );
374 std::cerr << std::endl;
375 )
376 convCost += computeExpressionConversionCost( *actualExpr, formalType, indexer, alt.env );
377 ++formal; // can't be in for-loop update because of the continue
378 }
379 if ( formal != formals.end() ) {
380 return Cost::infinity;
381 }
382
383 for ( InferredParams::const_iterator assert = appExpr->get_inferParams().begin(); assert != appExpr->get_inferParams().end(); ++assert ) {
384 convCost += computeConversionCost( assert->second.actualType, assert->second.formalType, indexer, alt.env );
385 }
386
387 return convCost;
388 }
389
390 /// Adds type variables to the open variable set and marks their assertions
391 void makeUnifiableVars( Type *type, OpenVarSet &unifiableVars, AssertionSet &needAssertions ) {
392 for ( Type::ForallList::const_iterator tyvar = type->get_forall().begin(); tyvar != type->get_forall().end(); ++tyvar ) {
393 unifiableVars[ (*tyvar)->get_name() ] = TypeDecl::Data{ *tyvar };
394 for ( std::list< DeclarationWithType* >::iterator assert = (*tyvar)->get_assertions().begin(); assert != (*tyvar)->get_assertions().end(); ++assert ) {
395 needAssertions[ *assert ].isUsed = true;
396 }
397/// needAssertions.insert( needAssertions.end(), (*tyvar)->get_assertions().begin(), (*tyvar)->get_assertions().end() );
398 }
399 }
400
401 // /// Map of declaration uniqueIds (intended to be the assertions in an AssertionSet) to their parents and the number of times they've been included
402 //typedef std::unordered_map< UniqueId, std::unordered_map< UniqueId, unsigned > > AssertionParentSet;
403
404 static const int recursionLimit = /*10*/ 4; ///< Limit to depth of recursion satisfaction
405 //static const unsigned recursionParentLimit = 1; ///< Limit to the number of times an assertion can recursively use itself
406
407 void addToIndexer( AssertionSet &assertSet, SymTab::Indexer &indexer ) {
408 for ( AssertionSet::iterator i = assertSet.begin(); i != assertSet.end(); ++i ) {
409 if ( i->second.isUsed ) {
410 indexer.addId( i->first );
411 }
412 }
413 }
414
415 template< typename ForwardIterator, typename OutputIterator >
416 void inferRecursive( ForwardIterator begin, ForwardIterator end, const Alternative &newAlt, OpenVarSet &openVars, const SymTab::Indexer &decls, const AssertionSet &newNeed, /*const AssertionParentSet &needParents,*/
417 int level, const SymTab::Indexer &indexer, OutputIterator out ) {
418 if ( begin == end ) {
419 if ( newNeed.empty() ) {
420 PRINT(
421 std::cerr << "all assertions satisfied, output alternative: ";
422 newAlt.print( std::cerr );
423 std::cerr << std::endl;
424 );
425 *out++ = newAlt;
426 return;
427 } else if ( level >= recursionLimit ) {
428 throw SemanticError( "Too many recursive assertions" );
429 } else {
430 AssertionSet newerNeed;
431 PRINT(
432 std::cerr << "recursing with new set:" << std::endl;
433 printAssertionSet( newNeed, std::cerr, 8 );
434 )
435 inferRecursive( newNeed.begin(), newNeed.end(), newAlt, openVars, decls, newerNeed, /*needParents,*/ level+1, indexer, out );
436 return;
437 }
438 }
439
440 ForwardIterator cur = begin++;
441 if ( ! cur->second.isUsed ) {
442 inferRecursive( begin, end, newAlt, openVars, decls, newNeed, /*needParents,*/ level, indexer, out );
443 return; // xxx - should this continue? previously this wasn't here, and it looks like it should be
444 }
445 DeclarationWithType *curDecl = cur->first;
446
447 PRINT(
448 std::cerr << "inferRecursive: assertion is ";
449 curDecl->print( std::cerr );
450 std::cerr << std::endl;
451 )
452 std::list< DeclarationWithType* > candidates;
453 decls.lookupId( curDecl->get_name(), candidates );
454/// if ( candidates.empty() ) { std::cerr << "no candidates!" << std::endl; }
455 for ( std::list< DeclarationWithType* >::const_iterator candidate = candidates.begin(); candidate != candidates.end(); ++candidate ) {
456 PRINT(
457 std::cerr << "inferRecursive: candidate is ";
458 (*candidate)->print( std::cerr );
459 std::cerr << std::endl;
460 )
461
462 AssertionSet newHave, newerNeed( newNeed );
463 TypeEnvironment newEnv( newAlt.env );
464 OpenVarSet newOpenVars( openVars );
465 Type *adjType = (*candidate)->get_type()->clone();
466 adjustExprType( adjType, newEnv, indexer );
467 adjType->accept( global_renamer );
468 PRINT(
469 std::cerr << "unifying ";
470 curDecl->get_type()->print( std::cerr );
471 std::cerr << " with ";
472 adjType->print( std::cerr );
473 std::cerr << std::endl;
474 )
475 if ( unify( curDecl->get_type(), adjType, newEnv, newerNeed, newHave, newOpenVars, indexer ) ) {
476 PRINT(
477 std::cerr << "success!" << std::endl;
478 )
479 SymTab::Indexer newDecls( decls );
480 addToIndexer( newHave, newDecls );
481 Alternative newerAlt( newAlt );
482 newerAlt.env = newEnv;
483 assert( (*candidate)->get_uniqueId() );
484 DeclarationWithType *candDecl = static_cast< DeclarationWithType* >( Declaration::declFromId( (*candidate)->get_uniqueId() ) );
485
486 // everything with an empty idChain was pulled in by the current assertion.
487 // add current assertion's idChain + current assertion's ID so that the correct inferParameters can be found.
488 for ( auto & a : newerNeed ) {
489 if ( a.second.idChain.empty() ) {
490 a.second.idChain = cur->second.idChain;
491 a.second.idChain.push_back( curDecl->get_uniqueId() );
492 }
493 }
494
495 //AssertionParentSet newNeedParents( needParents );
496 // skip repeatingly-self-recursive assertion satisfaction
497 // DOESN'T WORK: grandchild nodes conflict with their cousins
498 //if ( newNeedParents[ curDecl->get_uniqueId() ][ candDecl->get_uniqueId() ]++ > recursionParentLimit ) continue;
499 Expression *varExpr = new VariableExpr( candDecl );
500 delete varExpr->get_result();
501 varExpr->set_result( adjType->clone() );
502 PRINT(
503 std::cerr << "satisfying assertion " << curDecl->get_uniqueId() << " ";
504 curDecl->print( std::cerr );
505 std::cerr << " with declaration " << (*candidate)->get_uniqueId() << " ";
506 (*candidate)->print( std::cerr );
507 std::cerr << std::endl;
508 )
509 ApplicationExpr *appExpr = static_cast< ApplicationExpr* >( newerAlt.expr );
510 // follow the current assertion's ID chain to find the correct set of inferred parameters to add the candidate to (i.e. the set of inferred parameters belonging to the entity which requested the assertion parameter).
511 InferredParams * inferParameters = &appExpr->get_inferParams();
512 for ( UniqueId id : cur->second.idChain ) {
513 inferParameters = (*inferParameters)[ id ].inferParams.get();
514 }
515 // XXX: this is a memory leak, but adjType can't be deleted because it might contain assertions
516 (*inferParameters)[ curDecl->get_uniqueId() ] = ParamEntry( (*candidate)->get_uniqueId(), adjType->clone(), curDecl->get_type()->clone(), varExpr );
517 inferRecursive( begin, end, newerAlt, newOpenVars, newDecls, newerNeed, /*newNeedParents,*/ level, indexer, out );
518 } else {
519 delete adjType;
520 }
521 }
522 }
523
524 template< typename OutputIterator >
525 void AlternativeFinder::inferParameters( const AssertionSet &need, AssertionSet &have, const Alternative &newAlt, OpenVarSet &openVars, OutputIterator out ) {
526// PRINT(
527// std::cerr << "inferParameters: assertions needed are" << std::endl;
528// printAll( need, std::cerr, 8 );
529// )
530 SymTab::Indexer decls( indexer );
531 // PRINT(
532 // std::cerr << "============= original indexer" << std::endl;
533 // indexer.print( std::cerr );
534 // std::cerr << "============= new indexer" << std::endl;
535 // decls.print( std::cerr );
536 // )
537 addToIndexer( have, decls );
538 AssertionSet newNeed;
539 //AssertionParentSet needParents;
540 PRINT(
541 std::cerr << "env is: " << std::endl;
542 newAlt.env.print( std::cerr, 0 );
543 std::cerr << std::endl;
544 )
545
546 inferRecursive( need.begin(), need.end(), newAlt, openVars, decls, newNeed, /*needParents,*/ 0, indexer, out );
547// PRINT(
548// std::cerr << "declaration 14 is ";
549// Declaration::declFromId
550// *out++ = newAlt;
551// )
552 }
553
554 /// Gets a default value from an initializer, nullptr if not present
555 ConstantExpr* getDefaultValue( Initializer* init ) {
556 if ( SingleInit* si = dynamic_cast<SingleInit*>( init ) ) {
557 if ( CastExpr* ce = dynamic_cast<CastExpr*>( si->get_value() ) ) {
558 return dynamic_cast<ConstantExpr*>( ce->get_arg() );
559 }
560 }
561 return nullptr;
562 }
563
564 /// State to iteratively build a match of parameter expressions to arguments
565 struct ArgPack {
566 AltList actuals; ///< Arguments included in this pack
567 TypeEnvironment env; ///< Environment for this pack
568 AssertionSet need; ///< Assertions outstanding for this pack
569 AssertionSet have; ///< Assertions found for this pack
570 OpenVarSet openVars; ///< Open variables for this pack
571 unsigned nextArg; ///< Index of next argument in arguments list
572 std::vector<Alternative> expls; ///< Exploded actuals left over from last match
573 unsigned nextExpl; ///< Index of next exploded alternative to use
574 std::vector<unsigned> tupleEls; /// Number of elements in current tuple element(s)
575
576 ArgPack(const TypeEnvironment& env, const AssertionSet& need, const AssertionSet& have,
577 const OpenVarSet& openVars)
578 : actuals(), env(env), need(need), have(have), openVars(openVars), nextArg(0),
579 expls(), nextExpl(0), tupleEls() {}
580
581 ArgPack(const ArgPack& old, Expression* actual, TypeEnvironment&& env,
582 OpenVarSet&& openVars, const Cost& cost = Cost::zero)
583 : actuals(old.actuals), env(std::move(env)), need(old.need), have(old.have),
584 openVars(std::move(openVars)), nextArg(old.nextArg + 1), tupleEls(old.tupleEls) {
585 // add new actual
586 actuals.emplace_back( actual, this->env, cost );
587 // count tuple elements, if applicable
588 if ( ! tupleEls.empty() ) ++tupleEls.back();
589 }
590
591 ArgPack(const ArgPack& old, Expression* actual, TypeEnvironment&& env, AssertionSet&& need,
592 AssertionSet&& have, OpenVarSet&& openVars, const Cost& cost = Cost::zero)
593 : actuals(old.actuals), env(std::move(env)), need(std::move(need)),
594 have(std::move(have)), openVars(std::move(openVars)), nextArg(old.nextArg + 1),
595 tupleEls(old.tupleEls) {
596 // add new actual
597 actuals.emplace_back( actual, this->env, cost );
598 // count tuple elements, if applicable
599 if ( ! tupleEls.empty() ) ++tupleEls.back();
600 }
601
602 /// Starts a new tuple expression
603 void beginTuple() {
604 if ( ! tupleEls.empty() ) ++tupleEls.back();
605 tupleEls.push_back(0);
606 }
607
608 /// Ends a tuple expression, consolidating the appropriate actuals
609 void endTuple() {
610 // set up new Tuple alternative
611 std::list<Expression*> exprs;
612 Cost cost = Cost::zero;
613
614 // transfer elements into alternative
615 for (unsigned i = 0; i < tupleEls.back(); ++i) {
616 exprs.push_front( actuals.back().expr );
617 cost += actuals.back().cost;
618 actuals.pop_back();
619 }
620 tupleEls.pop_back();
621
622 // build new alternative
623 actuals.emplace_back( new TupleExpr( exprs ), this->env, cost );
624 }
625
626 /// Clones and adds an actual, returns this
627 ArgPack& withArg( Expression* expr ) {
628 actuals.emplace_back( expr->clone(), this->env, Cost::zero );
629 if ( ! tupleEls.empty() ) ++tupleEls.back();
630 return *this;
631 }
632 };
633
634 /// Instantiates an argument to match a formal, returns false if no results left
635 bool instantiateArgument( Type* formalType, Initializer* initializer,
636 const std::vector< AlternativeFinder >& args,
637 std::vector<ArgPack>& results, std::vector<ArgPack>& nextResults,
638 const SymTab::Indexer& indexer ) {
639 if ( TupleType* tupleType = dynamic_cast<TupleType*>( formalType ) ) {
640 // formalType is a TupleType - group actuals into a TupleExpr
641 for ( ArgPack& result : results ) { result.beginTuple(); }
642 for ( Type* type : *tupleType ) {
643 // xxx - dropping initializer changes behaviour from previous, but seems correct
644 if ( ! instantiateArgument( type, nullptr, args, results, nextResults, indexer ) )
645 return false;
646 }
647 for ( ArgPack& result : results ) { result.endTuple(); }
648 return true;
649 } else if ( TypeInstType* ttype = Tuples::isTtype( formalType ) ) {
650 // formalType is a ttype, consumes all remaining arguments
651 // xxx - mixing default arguments with variadic??
652 std::vector<ArgPack> finalResults{}; /// list of completed tuples
653 // start tuples
654 for ( ArgPack& result : results ) { result.beginTuple(); }
655 // iterate until all results completed
656 while ( ! results.empty() ) {
657 // add another argument to results
658 for ( ArgPack& result : results ) {
659 // finish result when out of arguments
660 if ( result.nextArg >= args.size() ) {
661 Type* argType = result.actuals.back().expr->get_result();
662 if ( result.tupleEls.back() == 1 && Tuples::isTtype( argType ) ) {
663 // the case where a ttype value is passed directly is special, e.g. for
664 // argument forwarding purposes
665 // xxx - what if passing multiple arguments, last of which is ttype?
666 // xxx - what would happen if unify was changed so that unifying tuple
667 // types flattened both before unifying lists? then pass in TupleType
668 // (ttype) below.
669 result.tupleEls.pop_back();
670 } else {
671 // collapse leftover arguments into tuple
672 result.endTuple();
673 argType = result.actuals.back().expr->get_result();
674 }
675 // check unification for ttype before adding to final
676 if ( unify( ttype, argType, result.env, result.need, result.have,
677 result.openVars, indexer ) ) {
678 finalResults.emplace_back( std::move(result) );
679 }
680 continue;
681 }
682
683 // add each possible next argument
684 for ( const Alternative& actual : args[result.nextArg] ) {
685 ArgPack aResult = result; // copy to clone everything
686 // add details of actual to result
687 aResult.env.addActual( actual.env, aResult.openVars );
688
689 // explode argument
690 std::vector<Alternative> exploded;
691 Tuples::explode( actual, indexer, back_inserter( exploded ) );
692
693 // add exploded argument to tuple
694 for ( Alternative& aActual : exploded ) {
695 aResult.withArg( aActual.expr );
696 }
697 ++aResult.nextArg;
698 nextResults.push_back( std::move(aResult) );
699 }
700 }
701
702 // reset for next round
703 results.swap( nextResults );
704 nextResults.clear();
705 }
706 results.swap( finalResults );
707 return ! results.empty();
708 }
709
710 // iterate each current subresult
711 for ( unsigned iResult = 0; iResult < results.size(); ++iResult ) {
712 ArgPack& result = results[iResult];
713
714 if ( result.nextExpl < result.expls.size() ) {
715 // use remainder of exploded tuple if present
716 const Alternative& actual = result.expls[result.nextExpl];
717 result.env.addActual( actual.env, result.openVars );
718 Type* actualType = actual.expr->get_result();
719
720 PRINT(
721 std::cerr << "formal type is ";
722 formalType->print( std::cerr );
723 std::cerr << std::endl << "actual type is ";
724 actualType->print( std::cerr );
725 std::cerr << std::endl;
726 )
727
728 if ( unify( formalType, actualType, result.env, result.need, result.have,
729 result.openVars, indexer ) ) {
730 ++result.nextExpl;
731 nextResults.push_back( std::move(result.withArg( actual.expr )) );
732 }
733
734 continue;
735 } else if ( result.nextArg >= args.size() ) {
736 // use default initializers if out of arguments
737 if ( ConstantExpr* cnstExpr = getDefaultValue( initializer ) ) {
738 if ( Constant* cnst = dynamic_cast<Constant*>( cnstExpr->get_constant() ) ) {
739 if ( unify( formalType, cnst->get_type(), result.env, result.need,
740 result.have, result.openVars, indexer ) ) {
741 nextResults.push_back( std::move(result.withArg( cnstExpr )) );
742 }
743 }
744 }
745 continue;
746 }
747
748 // Check each possible next argument
749 for ( const Alternative& actual : args[result.nextArg] ) {
750 ArgPack aResult = result; // copy to clone everything
751 // add details of actual to result
752 aResult.env.addActual( actual.env, aResult.openVars );
753
754 // explode argument
755 std::vector<Alternative> exploded;
756 Tuples::explode( actual, indexer, back_inserter( exploded ) );
757 if ( exploded.empty() ) {
758 // skip empty tuple arguments
759 ++aResult.nextArg;
760 results.push_back( std::move(aResult) );
761 continue;
762 }
763
764 // consider only first exploded actual
765 const Alternative& aActual = exploded.front();
766 Type* actualType = aActual.expr->get_result();
767
768 PRINT(
769 std::cerr << "formal type is ";
770 formalType->print( std::cerr );
771 std::cerr << std::endl << "actual type is ";
772 actualType->print( std::cerr );
773 std::cerr << std::endl;
774 )
775
776 // attempt to unify types
777 if ( unify( formalType, actualType, aResult.env, aResult.need, aResult.have, aResult.openVars, indexer ) ) {
778 // add argument
779 aResult.withArg( aActual.expr );
780 if ( exploded.size() == 1 ) {
781 // argument consumed
782 ++aResult.nextArg;
783 } else {
784 // other parts of tuple left over
785 aResult.expls = std::move( exploded );
786 aResult.nextExpl = 1;
787 }
788 nextResults.push_back( std::move(aResult) );
789 }
790 }
791 }
792
793 // reset for next parameter
794 results.swap( nextResults );
795 nextResults.clear();
796
797 return ! results.empty();
798 }
799
800 template<typename OutputIterator>
801 void AlternativeFinder::makeFunctionAlternatives( const Alternative& func,
802 FunctionType* funcType, const std::vector< AlternativeFinder >& args,
803 OutputIterator out ) {
804 OpenVarSet funcOpenVars;
805 AssertionSet funcNeed, funcHave;
806 TypeEnvironment funcEnv;
807 makeUnifiableVars( funcType, funcOpenVars, funcNeed );
808 // add all type variables as open variables now so that those not used in the parameter
809 // list are still considered open.
810 funcEnv.add( funcType->get_forall() );
811
812 if ( targetType && ! targetType->isVoid() && ! funcType->get_returnVals().empty() ) {
813 // attempt to narrow based on expected target type
814 Type* returnType = funcType->get_returnVals().front()->get_type();
815 if ( ! unify( returnType, targetType, funcEnv, funcNeed, funcHave, funcOpenVars,
816 indexer ) ) {
817 // unification failed, don't pursue this function alternative
818 return;
819 }
820 }
821
822 // iteratively build matches, one parameter at a time
823 std::vector<ArgPack> results{ ArgPack{ funcEnv, funcNeed, funcHave, funcOpenVars } };
824 std::vector<ArgPack> nextResults{};
825 for ( DeclarationWithType* formal : funcType->get_parameters() ) {
826 ObjectDecl* obj = strict_dynamic_cast< ObjectDecl* >( formal );
827 if ( ! instantiateArgument(
828 obj->get_type(), obj->get_init(), args, results, nextResults, indexer ) )
829 return;
830 }
831
832 // filter out results that don't use all the arguments, and aren't variadic
833 std::vector<ArgPack> finalResults{};
834 if ( funcType->get_isVarArgs() ) {
835 while ( ! results.empty() ) {
836 // build combinations for all remaining arguments
837 for ( ArgPack& result : results ) {
838 // keep if used all arguments
839 if ( result.nextArg >= args.size() ) {
840 finalResults.emplace_back( std::move(result) );
841 continue;
842 }
843
844 // add each possible next argument
845 for ( const Alternative& actual : args[result.nextArg] ) {
846 TypeEnvironment env{ result.env };
847 OpenVarSet openVars{ result.openVars };
848 env.addActual( actual.env, openVars );
849 nextResults.emplace_back( result, actual.expr->clone(), std::move(env),
850 std::move(openVars), actual.cost );
851 }
852 }
853
854 // reset for next round
855 results.swap( nextResults );
856 nextResults.clear();
857 }
858 } else {
859 // filter out results that don't use all the arguments
860 for ( ArgPack& result : results ) {
861 if ( result.nextArg >= args.size() ) {
862 finalResults.emplace_back( std::move(result) );
863 }
864 }
865 }
866
867 // validate matching combos, add to final result list
868 for ( ArgPack& result : finalResults ) {
869 ApplicationExpr *appExpr = new ApplicationExpr( func.expr->clone() );
870 Alternative newAlt( appExpr, result.env, sumCost( result.actuals ) );
871 makeExprList( result.actuals, appExpr->get_args() );
872 PRINT(
873 std::cerr << "instantiate function success: " << appExpr << std::endl;
874 std::cerr << "need assertions:" << std::endl;
875 printAssertionSet( result.need, std::cerr, 8 );
876 )
877 inferParameters( result.need, result.have, newAlt, result.openVars, out );
878 }
879 }
880
881 void AlternativeFinder::visit( UntypedExpr *untypedExpr ) {
882 AlternativeFinder funcFinder( indexer, env );
883 funcFinder.findWithAdjustment( untypedExpr->get_function() );
884 // if there are no function alternatives, then proceeding is a waste of time.
885 if ( funcFinder.alternatives.empty() ) return;
886
887 std::vector< AlternativeFinder > argAlternatives;
888 findSubExprs( untypedExpr->begin_args(), untypedExpr->end_args(),
889 back_inserter( argAlternatives ) );
890
891 // take care of possible tuple assignments
892 // if not tuple assignment, assignment is taken care of as a normal function call
893 Tuples::handleTupleAssignment( *this, untypedExpr, argAlternatives );
894
895 // find function operators
896 AlternativeFinder funcOpFinder( indexer, env );
897 NameExpr *opExpr = new NameExpr( "?()" );
898 try {
899 funcOpFinder.findWithAdjustment( opExpr );
900 } catch( SemanticError &e ) {
901 // it's ok if there aren't any defined function ops
902 }
903 PRINT(
904 std::cerr << "known function ops:" << std::endl;
905 printAlts( funcOpFinder.alternatives, std::cerr, 8 );
906 )
907
908 AltList candidates;
909 SemanticError errors;
910 for ( AltList::iterator func = funcFinder.alternatives.begin(); func != funcFinder.alternatives.end(); ++func ) {
911 try {
912 PRINT(
913 std::cerr << "working on alternative: " << std::endl;
914 func->print( std::cerr, 8 );
915 )
916 // check if the type is pointer to function
917 if ( PointerType *pointer = dynamic_cast< PointerType* >( func->expr->get_result()->stripReferences() ) ) {
918 if ( FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() ) ) {
919 Alternative newFunc( *func );
920 referenceToRvalueConversion( newFunc.expr );
921 makeFunctionAlternatives( newFunc, function, argAlternatives,
922 std::back_inserter( candidates ) );
923 }
924 } else if ( TypeInstType *typeInst = dynamic_cast< TypeInstType* >( func->expr->get_result()->stripReferences() ) ) { // handle ftype (e.g. *? on function pointer)
925 EqvClass eqvClass;
926 if ( func->env.lookup( typeInst->get_name(), eqvClass ) && eqvClass.type ) {
927 if ( FunctionType *function = dynamic_cast< FunctionType* >( eqvClass.type ) ) {
928 Alternative newFunc( *func );
929 referenceToRvalueConversion( newFunc.expr );
930 makeFunctionAlternatives( newFunc, function, argAlternatives,
931 std::back_inserter( candidates ) );
932 } // if
933 } // if
934 }
935 } catch ( SemanticError &e ) {
936 errors.append( e );
937 }
938 } // for
939
940 // try each function operator ?() with each function alternative
941 if ( ! funcOpFinder.alternatives.empty() ) {
942 // add function alternatives to front of argument list
943 argAlternatives.insert( argAlternatives.begin(), std::move(funcFinder) );
944
945 for ( AltList::iterator funcOp = funcOpFinder.alternatives.begin();
946 funcOp != funcOpFinder.alternatives.end(); ++funcOp ) {
947 try {
948 // check if type is a pointer to function
949 if ( PointerType* pointer = dynamic_cast<PointerType*>(
950 funcOp->expr->get_result()->stripReferences() ) ) {
951 if ( FunctionType* function =
952 dynamic_cast<FunctionType*>( pointer->get_base() ) ) {
953 Alternative newFunc( *funcOp );
954 referenceToRvalueConversion( newFunc.expr );
955 makeFunctionAlternatives( newFunc, function, argAlternatives,
956 std::back_inserter( candidates ) );
957 }
958 }
959 } catch ( SemanticError &e ) {
960 errors.append( e );
961 }
962 }
963 }
964
965 // Implement SFINAE; resolution errors are only errors if there aren't any non-erroneous resolutions
966 if ( candidates.empty() && ! errors.isEmpty() ) { throw errors; }
967
968 // compute conversionsion costs
969 for ( AltList::iterator withFunc = candidates.begin(); withFunc != candidates.end(); ++withFunc ) {
970 Cost cvtCost = computeApplicationConversionCost( *withFunc, indexer );
971
972 PRINT(
973 ApplicationExpr *appExpr = strict_dynamic_cast< ApplicationExpr* >( withFunc->expr );
974 PointerType *pointer = strict_dynamic_cast< PointerType* >( appExpr->get_function()->get_result() );
975 FunctionType *function = strict_dynamic_cast< FunctionType* >( pointer->get_base() );
976 std::cerr << "Case +++++++++++++ " << appExpr->get_function() << std::endl;
977 std::cerr << "formals are:" << std::endl;
978 printAll( function->get_parameters(), std::cerr, 8 );
979 std::cerr << "actuals are:" << std::endl;
980 printAll( appExpr->get_args(), std::cerr, 8 );
981 std::cerr << "bindings are:" << std::endl;
982 withFunc->env.print( std::cerr, 8 );
983 std::cerr << "cost of conversion is:" << cvtCost << std::endl;
984 )
985 if ( cvtCost != Cost::infinity ) {
986 withFunc->cvtCost = cvtCost;
987 alternatives.push_back( *withFunc );
988 } // if
989 } // for
990
991 candidates.clear();
992 candidates.splice( candidates.end(), alternatives );
993
994 findMinCost( candidates.begin(), candidates.end(), std::back_inserter( alternatives ) );
995
996 // function may return struct or union value, in which case we need to add alternatives for implicit
997 // conversions to each of the anonymous members, must happen after findMinCost since anon conversions
998 // are never the cheapest expression
999 for ( const Alternative & alt : alternatives ) {
1000 addAnonConversions( alt );
1001 }
1002
1003 if ( alternatives.empty() && targetType && ! targetType->isVoid() ) {
1004 // xxx - this is a temporary hack. If resolution is unsuccessful with a target type, try again without a
1005 // target type, since it will sometimes succeed when it wouldn't easily with target type binding. For example,
1006 // forall( otype T ) lvalue T ?[?]( T *, ptrdiff_t );
1007 // const char * x = "hello world";
1008 // unsigned char ch = x[0];
1009 // Fails with simple return type binding. First, T is bound to unsigned char, then (x: const char *) is unified
1010 // with unsigned char *, which fails because pointer base types must be unified exactly. The new resolver should
1011 // fix this issue in a more robust way.
1012 targetType = nullptr;
1013 visit( untypedExpr );
1014 }
1015 }
1016
1017 bool isLvalue( Expression *expr ) {
1018 // xxx - recurse into tuples?
1019 return expr->has_result() && ( expr->get_result()->get_lvalue() || dynamic_cast< ReferenceType * >( expr->get_result() ) );
1020 }
1021
1022 void AlternativeFinder::visit( AddressExpr *addressExpr ) {
1023 AlternativeFinder finder( indexer, env );
1024 finder.find( addressExpr->get_arg() );
1025 for ( std::list< Alternative >::iterator i = finder.alternatives.begin(); i != finder.alternatives.end(); ++i ) {
1026 if ( isLvalue( i->expr ) ) {
1027 alternatives.push_back( Alternative( new AddressExpr( i->expr->clone() ), i->env, i->cost ) );
1028 } // if
1029 } // for
1030 }
1031
1032 void AlternativeFinder::visit( LabelAddressExpr * expr ) {
1033 alternatives.push_back( Alternative( expr->clone(), env, Cost::zero) );
1034 }
1035
1036 Expression * restructureCast( Expression * argExpr, Type * toType ) {
1037 if ( argExpr->get_result()->size() > 1 && ! toType->isVoid() && ! dynamic_cast<ReferenceType *>( toType ) ) {
1038 // Argument expression is a tuple and the target type is not void and not a reference type.
1039 // Cast each member of the tuple to its corresponding target type, producing the tuple of those
1040 // cast expressions. If there are more components of the tuple than components in the target type,
1041 // then excess components do not come out in the result expression (but UniqueExprs ensure that
1042 // side effects will still be done).
1043 if ( Tuples::maybeImpureIgnoreUnique( argExpr ) ) {
1044 // expressions which may contain side effects require a single unique instance of the expression.
1045 argExpr = new UniqueExpr( argExpr );
1046 }
1047 std::list< Expression * > componentExprs;
1048 for ( unsigned int i = 0; i < toType->size(); i++ ) {
1049 // cast each component
1050 TupleIndexExpr * idx = new TupleIndexExpr( argExpr->clone(), i );
1051 componentExprs.push_back( restructureCast( idx, toType->getComponent( i ) ) );
1052 }
1053 delete argExpr;
1054 assert( componentExprs.size() > 0 );
1055 // produce the tuple of casts
1056 return new TupleExpr( componentExprs );
1057 } else {
1058 // handle normally
1059 return new CastExpr( argExpr, toType->clone() );
1060 }
1061 }
1062
1063 void AlternativeFinder::visit( CastExpr *castExpr ) {
1064 Type *& toType = castExpr->get_result();
1065 assert( toType );
1066 toType = resolveTypeof( toType, indexer );
1067 SymTab::validateType( toType, &indexer );
1068 adjustExprType( toType, env, indexer );
1069
1070 AlternativeFinder finder( indexer, env );
1071 finder.targetType = toType;
1072 finder.findWithAdjustment( castExpr->get_arg() );
1073
1074 AltList candidates;
1075 for ( std::list< Alternative >::iterator i = finder.alternatives.begin(); i != finder.alternatives.end(); ++i ) {
1076 AssertionSet needAssertions, haveAssertions;
1077 OpenVarSet openVars;
1078
1079 // It's possible that a cast can throw away some values in a multiply-valued expression. (An example is a
1080 // cast-to-void, which casts from one value to zero.) Figure out the prefix of the subexpression results
1081 // that are cast directly. The candidate is invalid if it has fewer results than there are types to cast
1082 // to.
1083 int discardedValues = i->expr->get_result()->size() - castExpr->get_result()->size();
1084 if ( discardedValues < 0 ) continue;
1085 // xxx - may need to go into tuple types and extract relevant types and use unifyList. Note that currently, this does not
1086 // allow casting a tuple to an atomic type (e.g. (int)([1, 2, 3]))
1087 // unification run for side-effects
1088 unify( castExpr->get_result(), i->expr->get_result(), i->env, needAssertions, haveAssertions, openVars, indexer );
1089 Cost thisCost = castCost( i->expr->get_result(), castExpr->get_result(), indexer, i->env );
1090 if ( thisCost != Cost::infinity ) {
1091 // count one safe conversion for each value that is thrown away
1092 thisCost.incSafe( discardedValues );
1093 Alternative newAlt( restructureCast( i->expr->clone(), toType ), i->env, i->cost, thisCost );
1094 // xxx - this doesn't work at the moment, since inferParameters requires an ApplicationExpr as the alternative.
1095 // Once this works, it should be possible to infer parameters on a cast expression and specialize any function.
1096
1097 // inferParameters( needAssertions, haveAssertions, newAlt, openVars, back_inserter( candidates ) );
1098 candidates.emplace_back( std::move( newAlt ) );
1099 } // if
1100 } // for
1101
1102 // findMinCost selects the alternatives with the lowest "cost" members, but has the side effect of copying the
1103 // cvtCost member to the cost member (since the old cost is now irrelevant). Thus, calling findMinCost twice
1104 // selects first based on argument cost, then on conversion cost.
1105 AltList minArgCost;
1106 findMinCost( candidates.begin(), candidates.end(), std::back_inserter( minArgCost ) );
1107 findMinCost( minArgCost.begin(), minArgCost.end(), std::back_inserter( alternatives ) );
1108 }
1109
1110 void AlternativeFinder::visit( VirtualCastExpr * castExpr ) {
1111 assertf( castExpr->get_result(), "Implicate virtual cast targets not yet supported." );
1112 AlternativeFinder finder( indexer, env );
1113 // don't prune here, since it's guaranteed all alternatives will have the same type
1114 // (giving the alternatives different types is half of the point of ConstructorExpr nodes)
1115 finder.findWithAdjustment( castExpr->get_arg(), false );
1116 for ( Alternative & alt : finder.alternatives ) {
1117 alternatives.push_back( Alternative(
1118 new VirtualCastExpr( alt.expr->clone(), castExpr->get_result()->clone() ),
1119 alt.env, alt.cost ) );
1120 }
1121 }
1122
1123 void AlternativeFinder::visit( UntypedMemberExpr *memberExpr ) {
1124 AlternativeFinder funcFinder( indexer, env );
1125 funcFinder.findWithAdjustment( memberExpr->get_aggregate() );
1126 for ( AltList::const_iterator agg = funcFinder.alternatives.begin(); agg != funcFinder.alternatives.end(); ++agg ) {
1127 // it's okay for the aggregate expression to have reference type -- cast it to the base type to treat the aggregate as the referenced value
1128 std::unique_ptr<Expression> aggrExpr( agg->expr->clone() );
1129 Type * aggrType = aggrExpr->get_result();
1130 if ( dynamic_cast< ReferenceType * >( aggrType ) ) {
1131 aggrType = aggrType->stripReferences();
1132 aggrExpr.reset( new CastExpr( aggrExpr.release(), aggrType->clone() ) );
1133 }
1134 // find member of the given type
1135 if ( StructInstType *structInst = dynamic_cast< StructInstType* >( aggrExpr->get_result() ) ) {
1136 addAggMembers( structInst, aggrExpr.get(), agg->cost, agg->env, memberExpr->get_member() );
1137 } else if ( UnionInstType *unionInst = dynamic_cast< UnionInstType* >( aggrExpr->get_result() ) ) {
1138 addAggMembers( unionInst, aggrExpr.get(), agg->cost, agg->env, memberExpr->get_member() );
1139 } else if ( TupleType * tupleType = dynamic_cast< TupleType * >( aggrExpr->get_result() ) ) {
1140 addTupleMembers( tupleType, aggrExpr.get(), agg->cost, agg->env, memberExpr->get_member() );
1141 } // if
1142 } // for
1143 }
1144
1145 void AlternativeFinder::visit( MemberExpr *memberExpr ) {
1146 alternatives.push_back( Alternative( memberExpr->clone(), env, Cost::zero ) );
1147 }
1148
1149 void AlternativeFinder::visit( NameExpr *nameExpr ) {
1150 std::list< DeclarationWithType* > declList;
1151 indexer.lookupId( nameExpr->get_name(), declList );
1152 PRINT( std::cerr << "nameExpr is " << nameExpr->get_name() << std::endl; )
1153 for ( std::list< DeclarationWithType* >::iterator i = declList.begin(); i != declList.end(); ++i ) {
1154 VariableExpr newExpr( *i, nameExpr->get_argName() );
1155 alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
1156 PRINT(
1157 std::cerr << "decl is ";
1158 (*i)->print( std::cerr );
1159 std::cerr << std::endl;
1160 std::cerr << "newExpr is ";
1161 newExpr.print( std::cerr );
1162 std::cerr << std::endl;
1163 )
1164 renameTypes( alternatives.back().expr );
1165 addAnonConversions( alternatives.back() ); // add anonymous member interpretations whenever an aggregate value type is seen as a name expression.
1166 } // for
1167 }
1168
1169 void AlternativeFinder::visit( VariableExpr *variableExpr ) {
1170 // not sufficient to clone here, because variable's type may have changed
1171 // since the VariableExpr was originally created.
1172 alternatives.push_back( Alternative( new VariableExpr( variableExpr->get_var() ), env, Cost::zero ) );
1173 }
1174
1175 void AlternativeFinder::visit( ConstantExpr *constantExpr ) {
1176 alternatives.push_back( Alternative( constantExpr->clone(), env, Cost::zero ) );
1177 }
1178
1179 void AlternativeFinder::visit( SizeofExpr *sizeofExpr ) {
1180 if ( sizeofExpr->get_isType() ) {
1181 Type * newType = sizeofExpr->get_type()->clone();
1182 alternatives.push_back( Alternative( new SizeofExpr( resolveTypeof( newType, indexer ) ), env, Cost::zero ) );
1183 } else {
1184 // find all alternatives for the argument to sizeof
1185 AlternativeFinder finder( indexer, env );
1186 finder.find( sizeofExpr->get_expr() );
1187 // find the lowest cost alternative among the alternatives, otherwise ambiguous
1188 AltList winners;
1189 findMinCost( finder.alternatives.begin(), finder.alternatives.end(), back_inserter( winners ) );
1190 if ( winners.size() != 1 ) {
1191 throw SemanticError( "Ambiguous expression in sizeof operand: ", sizeofExpr->get_expr() );
1192 } // if
1193 // return the lowest cost alternative for the argument
1194 Alternative &choice = winners.front();
1195 referenceToRvalueConversion( choice.expr );
1196 alternatives.push_back( Alternative( new SizeofExpr( choice.expr->clone() ), choice.env, Cost::zero ) );
1197 } // if
1198 }
1199
1200 void AlternativeFinder::visit( AlignofExpr *alignofExpr ) {
1201 if ( alignofExpr->get_isType() ) {
1202 Type * newType = alignofExpr->get_type()->clone();
1203 alternatives.push_back( Alternative( new AlignofExpr( resolveTypeof( newType, indexer ) ), env, Cost::zero ) );
1204 } else {
1205 // find all alternatives for the argument to sizeof
1206 AlternativeFinder finder( indexer, env );
1207 finder.find( alignofExpr->get_expr() );
1208 // find the lowest cost alternative among the alternatives, otherwise ambiguous
1209 AltList winners;
1210 findMinCost( finder.alternatives.begin(), finder.alternatives.end(), back_inserter( winners ) );
1211 if ( winners.size() != 1 ) {
1212 throw SemanticError( "Ambiguous expression in alignof operand: ", alignofExpr->get_expr() );
1213 } // if
1214 // return the lowest cost alternative for the argument
1215 Alternative &choice = winners.front();
1216 referenceToRvalueConversion( choice.expr );
1217 alternatives.push_back( Alternative( new AlignofExpr( choice.expr->clone() ), choice.env, Cost::zero ) );
1218 } // if
1219 }
1220
1221 template< typename StructOrUnionType >
1222 void AlternativeFinder::addOffsetof( StructOrUnionType *aggInst, const std::string &name ) {
1223 std::list< Declaration* > members;
1224 aggInst->lookup( name, members );
1225 for ( std::list< Declaration* >::const_iterator i = members.begin(); i != members.end(); ++i ) {
1226 if ( DeclarationWithType *dwt = dynamic_cast< DeclarationWithType* >( *i ) ) {
1227 alternatives.push_back( Alternative( new OffsetofExpr( aggInst->clone(), dwt ), env, Cost::zero ) );
1228 renameTypes( alternatives.back().expr );
1229 } else {
1230 assert( false );
1231 }
1232 }
1233 }
1234
1235 void AlternativeFinder::visit( UntypedOffsetofExpr *offsetofExpr ) {
1236 AlternativeFinder funcFinder( indexer, env );
1237 // xxx - resolveTypeof?
1238 if ( StructInstType *structInst = dynamic_cast< StructInstType* >( offsetofExpr->get_type() ) ) {
1239 addOffsetof( structInst, offsetofExpr->get_member() );
1240 } else if ( UnionInstType *unionInst = dynamic_cast< UnionInstType* >( offsetofExpr->get_type() ) ) {
1241 addOffsetof( unionInst, offsetofExpr->get_member() );
1242 }
1243 }
1244
1245 void AlternativeFinder::visit( OffsetofExpr *offsetofExpr ) {
1246 alternatives.push_back( Alternative( offsetofExpr->clone(), env, Cost::zero ) );
1247 }
1248
1249 void AlternativeFinder::visit( OffsetPackExpr *offsetPackExpr ) {
1250 alternatives.push_back( Alternative( offsetPackExpr->clone(), env, Cost::zero ) );
1251 }
1252
1253 void AlternativeFinder::resolveAttr( DeclarationWithType *funcDecl, FunctionType *function, Type *argType, const TypeEnvironment &env ) {
1254 // assume no polymorphism
1255 // assume no implicit conversions
1256 assert( function->get_parameters().size() == 1 );
1257 PRINT(
1258 std::cerr << "resolvAttr: funcDecl is ";
1259 funcDecl->print( std::cerr );
1260 std::cerr << " argType is ";
1261 argType->print( std::cerr );
1262 std::cerr << std::endl;
1263 )
1264 if ( typesCompatibleIgnoreQualifiers( argType, function->get_parameters().front()->get_type(), indexer, env ) ) {
1265 alternatives.push_back( Alternative( new AttrExpr( new VariableExpr( funcDecl ), argType->clone() ), env, Cost::zero ) );
1266 for ( std::list< DeclarationWithType* >::iterator i = function->get_returnVals().begin(); i != function->get_returnVals().end(); ++i ) {
1267 alternatives.back().expr->set_result( (*i)->get_type()->clone() );
1268 } // for
1269 } // if
1270 }
1271
1272 void AlternativeFinder::visit( AttrExpr *attrExpr ) {
1273 // assume no 'pointer-to-attribute'
1274 NameExpr *nameExpr = dynamic_cast< NameExpr* >( attrExpr->get_attr() );
1275 assert( nameExpr );
1276 std::list< DeclarationWithType* > attrList;
1277 indexer.lookupId( nameExpr->get_name(), attrList );
1278 if ( attrExpr->get_isType() || attrExpr->get_expr() ) {
1279 for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
1280 // check if the type is function
1281 if ( FunctionType *function = dynamic_cast< FunctionType* >( (*i)->get_type() ) ) {
1282 // assume exactly one parameter
1283 if ( function->get_parameters().size() == 1 ) {
1284 if ( attrExpr->get_isType() ) {
1285 resolveAttr( *i, function, attrExpr->get_type(), env );
1286 } else {
1287 AlternativeFinder finder( indexer, env );
1288 finder.find( attrExpr->get_expr() );
1289 for ( AltList::iterator choice = finder.alternatives.begin(); choice != finder.alternatives.end(); ++choice ) {
1290 if ( choice->expr->get_result()->size() == 1 ) {
1291 resolveAttr(*i, function, choice->expr->get_result(), choice->env );
1292 } // fi
1293 } // for
1294 } // if
1295 } // if
1296 } // if
1297 } // for
1298 } else {
1299 for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
1300 VariableExpr newExpr( *i );
1301 alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
1302 renameTypes( alternatives.back().expr );
1303 } // for
1304 } // if
1305 }
1306
1307 void AlternativeFinder::visit( LogicalExpr *logicalExpr ) {
1308 AlternativeFinder firstFinder( indexer, env );
1309 firstFinder.findWithAdjustment( logicalExpr->get_arg1() );
1310 for ( AltList::const_iterator first = firstFinder.alternatives.begin(); first != firstFinder.alternatives.end(); ++first ) {
1311 AlternativeFinder secondFinder( indexer, first->env );
1312 secondFinder.findWithAdjustment( logicalExpr->get_arg2() );
1313 for ( AltList::const_iterator second = secondFinder.alternatives.begin(); second != secondFinder.alternatives.end(); ++second ) {
1314 LogicalExpr *newExpr = new LogicalExpr( first->expr->clone(), second->expr->clone(), logicalExpr->get_isAnd() );
1315 alternatives.push_back( Alternative( newExpr, second->env, first->cost + second->cost ) );
1316 }
1317 }
1318 }
1319
1320 void AlternativeFinder::visit( ConditionalExpr *conditionalExpr ) {
1321 // find alternatives for condition
1322 AlternativeFinder firstFinder( indexer, env );
1323 firstFinder.findWithAdjustment( conditionalExpr->get_arg1() );
1324 for ( AltList::const_iterator first = firstFinder.alternatives.begin(); first != firstFinder.alternatives.end(); ++first ) {
1325 // find alternatives for true expression
1326 AlternativeFinder secondFinder( indexer, first->env );
1327 secondFinder.findWithAdjustment( conditionalExpr->get_arg2() );
1328 for ( AltList::const_iterator second = secondFinder.alternatives.begin(); second != secondFinder.alternatives.end(); ++second ) {
1329 // find alterantives for false expression
1330 AlternativeFinder thirdFinder( indexer, second->env );
1331 thirdFinder.findWithAdjustment( conditionalExpr->get_arg3() );
1332 for ( AltList::const_iterator third = thirdFinder.alternatives.begin(); third != thirdFinder.alternatives.end(); ++third ) {
1333 // unify true and false types, then infer parameters to produce new alternatives
1334 OpenVarSet openVars;
1335 AssertionSet needAssertions, haveAssertions;
1336 Alternative newAlt( 0, third->env, first->cost + second->cost + third->cost );
1337 Type* commonType = nullptr;
1338 if ( unify( second->expr->get_result(), third->expr->get_result(), newAlt.env, needAssertions, haveAssertions, openVars, indexer, commonType ) ) {
1339 ConditionalExpr *newExpr = new ConditionalExpr( first->expr->clone(), second->expr->clone(), third->expr->clone() );
1340 newExpr->set_result( commonType ? commonType : second->expr->get_result()->clone() );
1341 // convert both options to the conditional result type
1342 newAlt.cost += computeExpressionConversionCost( newExpr->arg2, newExpr->result, indexer, newAlt.env );
1343 newAlt.cost += computeExpressionConversionCost( newExpr->arg3, newExpr->result, indexer, newAlt.env );
1344 newAlt.expr = newExpr;
1345 inferParameters( needAssertions, haveAssertions, newAlt, openVars, back_inserter( alternatives ) );
1346 } // if
1347 } // for
1348 } // for
1349 } // for
1350 }
1351
1352 void AlternativeFinder::visit( CommaExpr *commaExpr ) {
1353 TypeEnvironment newEnv( env );
1354 Expression *newFirstArg = resolveInVoidContext( commaExpr->get_arg1(), indexer, newEnv );
1355 AlternativeFinder secondFinder( indexer, newEnv );
1356 secondFinder.findWithAdjustment( commaExpr->get_arg2() );
1357 for ( AltList::const_iterator alt = secondFinder.alternatives.begin(); alt != secondFinder.alternatives.end(); ++alt ) {
1358 alternatives.push_back( Alternative( new CommaExpr( newFirstArg->clone(), alt->expr->clone() ), alt->env, alt->cost ) );
1359 } // for
1360 delete newFirstArg;
1361 }
1362
1363 void AlternativeFinder::visit( RangeExpr * rangeExpr ) {
1364 // resolve low and high, accept alternatives whose low and high types unify
1365 AlternativeFinder firstFinder( indexer, env );
1366 firstFinder.findWithAdjustment( rangeExpr->get_low() );
1367 for ( AltList::const_iterator first = firstFinder.alternatives.begin(); first != firstFinder.alternatives.end(); ++first ) {
1368 AlternativeFinder secondFinder( indexer, first->env );
1369 secondFinder.findWithAdjustment( rangeExpr->get_high() );
1370 for ( AltList::const_iterator second = secondFinder.alternatives.begin(); second != secondFinder.alternatives.end(); ++second ) {
1371 OpenVarSet openVars;
1372 AssertionSet needAssertions, haveAssertions;
1373 Alternative newAlt( 0, second->env, first->cost + second->cost );
1374 Type* commonType = nullptr;
1375 if ( unify( first->expr->get_result(), second->expr->get_result(), newAlt.env, needAssertions, haveAssertions, openVars, indexer, commonType ) ) {
1376 RangeExpr *newExpr = new RangeExpr( first->expr->clone(), second->expr->clone() );
1377 newExpr->set_result( commonType ? commonType : first->expr->get_result()->clone() );
1378 newAlt.expr = newExpr;
1379 inferParameters( needAssertions, haveAssertions, newAlt, openVars, back_inserter( alternatives ) );
1380 } // if
1381 } // for
1382 } // for
1383 }
1384
1385 void AlternativeFinder::visit( UntypedTupleExpr *tupleExpr ) {
1386 std::list< AlternativeFinder > subExprAlternatives;
1387 findSubExprs( tupleExpr->get_exprs().begin(), tupleExpr->get_exprs().end(), back_inserter( subExprAlternatives ) );
1388 std::list< AltList > possibilities;
1389 // TODO re-write to use iterative method
1390 combos( subExprAlternatives.begin(), subExprAlternatives.end(), back_inserter( possibilities ) );
1391 for ( std::list< AltList >::const_iterator i = possibilities.begin(); i != possibilities.end(); ++i ) {
1392 std::list< Expression * > exprs;
1393 makeExprList( *i, exprs );
1394
1395 TypeEnvironment compositeEnv;
1396 simpleCombineEnvironments( i->begin(), i->end(), compositeEnv );
1397 alternatives.push_back( Alternative( new TupleExpr( exprs ) , compositeEnv, sumCost( *i ) ) );
1398 } // for
1399 }
1400
1401 void AlternativeFinder::visit( TupleExpr *tupleExpr ) {
1402 alternatives.push_back( Alternative( tupleExpr->clone(), env, Cost::zero ) );
1403 }
1404
1405 void AlternativeFinder::visit( ImplicitCopyCtorExpr * impCpCtorExpr ) {
1406 alternatives.push_back( Alternative( impCpCtorExpr->clone(), env, Cost::zero ) );
1407 }
1408
1409 void AlternativeFinder::visit( ConstructorExpr * ctorExpr ) {
1410 AlternativeFinder finder( indexer, env );
1411 // don't prune here, since it's guaranteed all alternatives will have the same type
1412 // (giving the alternatives different types is half of the point of ConstructorExpr nodes)
1413 finder.findWithAdjustment( ctorExpr->get_callExpr(), false );
1414 for ( Alternative & alt : finder.alternatives ) {
1415 alternatives.push_back( Alternative( new ConstructorExpr( alt.expr->clone() ), alt.env, alt.cost ) );
1416 }
1417 }
1418
1419 void AlternativeFinder::visit( TupleIndexExpr *tupleExpr ) {
1420 alternatives.push_back( Alternative( tupleExpr->clone(), env, Cost::zero ) );
1421 }
1422
1423 void AlternativeFinder::visit( TupleAssignExpr *tupleAssignExpr ) {
1424 alternatives.push_back( Alternative( tupleAssignExpr->clone(), env, Cost::zero ) );
1425 }
1426
1427 void AlternativeFinder::visit( UniqueExpr *unqExpr ) {
1428 AlternativeFinder finder( indexer, env );
1429 finder.findWithAdjustment( unqExpr->get_expr() );
1430 for ( Alternative & alt : finder.alternatives ) {
1431 // ensure that the id is passed on to the UniqueExpr alternative so that the expressions are "linked"
1432 UniqueExpr * newUnqExpr = new UniqueExpr( alt.expr->clone(), unqExpr->get_id() );
1433 alternatives.push_back( Alternative( newUnqExpr, alt.env, alt.cost ) );
1434 }
1435 }
1436
1437 void AlternativeFinder::visit( StmtExpr *stmtExpr ) {
1438 StmtExpr * newStmtExpr = stmtExpr->clone();
1439 ResolvExpr::resolveStmtExpr( newStmtExpr, indexer );
1440 // xxx - this env is almost certainly wrong, and needs to somehow contain the combined environments from all of the statements in the stmtExpr...
1441 alternatives.push_back( Alternative( newStmtExpr, env, Cost::zero ) );
1442 }
1443
1444 void AlternativeFinder::visit( UntypedInitExpr *initExpr ) {
1445 // handle each option like a cast
1446 AltList candidates;
1447 PRINT( std::cerr << "untyped init expr: " << initExpr << std::endl; )
1448 // O(N^2) checks of d-types with e-types
1449 for ( InitAlternative & initAlt : initExpr->get_initAlts() ) {
1450 Type * toType = resolveTypeof( initAlt.type, indexer );
1451 SymTab::validateType( toType, &indexer );
1452 adjustExprType( toType, env, indexer );
1453 // Ideally the call to findWithAdjustment could be moved out of the loop, but unfortunately it currently has to occur inside or else
1454 // polymorphic return types are not properly bound to the initialization type, since return type variables are only open for the duration of resolving
1455 // the UntypedExpr. This is only actually an issue in initialization contexts that allow more than one possible initialization type, but it is still suboptimal.
1456 AlternativeFinder finder( indexer, env );
1457 finder.targetType = toType;
1458 finder.findWithAdjustment( initExpr->get_expr() );
1459 for ( Alternative & alt : finder.get_alternatives() ) {
1460 TypeEnvironment newEnv( alt.env );
1461 AssertionSet needAssertions, haveAssertions;
1462 OpenVarSet openVars; // find things in env that don't have a "representative type" and claim those are open vars?
1463 PRINT( std::cerr << " @ " << toType << " " << initAlt.designation << std::endl; )
1464 // It's possible that a cast can throw away some values in a multiply-valued expression. (An example is a
1465 // cast-to-void, which casts from one value to zero.) Figure out the prefix of the subexpression results
1466 // that are cast directly. The candidate is invalid if it has fewer results than there are types to cast
1467 // to.
1468 int discardedValues = alt.expr->get_result()->size() - toType->size();
1469 if ( discardedValues < 0 ) continue;
1470 // xxx - may need to go into tuple types and extract relevant types and use unifyList. Note that currently, this does not
1471 // allow casting a tuple to an atomic type (e.g. (int)([1, 2, 3]))
1472 // unification run for side-effects
1473 unify( toType, alt.expr->get_result(), newEnv, needAssertions, haveAssertions, openVars, indexer ); // xxx - do some inspecting on this line... why isn't result bound to initAlt.type??
1474
1475 Cost thisCost = castCost( alt.expr->get_result(), toType, indexer, newEnv );
1476 if ( thisCost != Cost::infinity ) {
1477 // count one safe conversion for each value that is thrown away
1478 thisCost.incSafe( discardedValues );
1479 candidates.push_back( Alternative( new InitExpr( restructureCast( alt.expr->clone(), toType ), initAlt.designation->clone() ), newEnv, alt.cost, thisCost ) );
1480 }
1481 }
1482 }
1483
1484 // findMinCost selects the alternatives with the lowest "cost" members, but has the side effect of copying the
1485 // cvtCost member to the cost member (since the old cost is now irrelevant). Thus, calling findMinCost twice
1486 // selects first based on argument cost, then on conversion cost.
1487 AltList minArgCost;
1488 findMinCost( candidates.begin(), candidates.end(), std::back_inserter( minArgCost ) );
1489 findMinCost( minArgCost.begin(), minArgCost.end(), std::back_inserter( alternatives ) );
1490 }
1491} // namespace ResolvExpr
1492
1493// Local Variables: //
1494// tab-width: 4 //
1495// mode: c++ //
1496// compile-command: "make install" //
1497// End: //
Note: See TracBrowser for help on using the repository browser.