source: src/ResolvExpr/AlternativeFinder.cc@ c38ae92

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 c38ae92 was 11094d9, checked in by Rob Schluntz <rschlunt@…>, 8 years ago

Fix duplicate anonymous member alternative generation

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