source: src/ResolvExpr/CandidateFinder.cpp@ 502ff9e

Last change on this file since 502ff9e was 17fa94f, checked in by Andrew Beach <ajbeach@…>, 7 months ago

Reworked some nodes so they can be typed or untyped. This allowed me to remove TranslationDeps as the type information is only needed in the candidate finder, which can easily insert it.

  • Property mode set to 100644
File size: 81.0 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// CandidateFinder.cpp --
8//
9// Author : Aaron B. Moss
10// Created On : Wed Jun 5 14:30:00 2019
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Mon Sep 9 11:30:11 2024
13// Update Count : 5
14//
15
16#include "CandidateFinder.hpp"
17
18#include <deque>
19#include <iterator> // for back_inserter
20#include <sstream>
21#include <string>
22#include <unordered_map>
23#include <vector>
24
25#include "AdjustExprType.hpp"
26#include "Candidate.hpp"
27#include "CastCost.hpp" // for castCost
28#include "CompilationState.hpp"
29#include "ConversionCost.hpp" // for conversionCast
30#include "Cost.hpp"
31#include "ExplodedArg.hpp"
32#include "PolyCost.hpp"
33#include "RenameVars.hpp" // for renameTyVars
34#include "Resolver.hpp"
35#include "ResolveTypeof.hpp"
36#include "SatisfyAssertions.hpp"
37#include "SpecCost.hpp"
38#include "Typeops.hpp" // for combos
39#include "Unify.hpp"
40#include "WidenMode.hpp"
41#include "AST/Expr.hpp"
42#include "AST/Node.hpp"
43#include "AST/Pass.hpp"
44#include "AST/Print.hpp"
45#include "AST/SymbolTable.hpp"
46#include "AST/Type.hpp"
47#include "Common/Utility.hpp" // for move, copy
48#include "SymTab/Mangler.hpp"
49#include "Tuples/Tuples.hpp" // for handleTupleAssignment
50#include "InitTweak/InitTweak.hpp" // for getPointerBase
51
52#include "Common/Stats/Counter.hpp"
53
54#include "AST/Inspect.hpp" // for getFunctionName
55
56#define PRINT( text ) if ( resolvep ) { text }
57
58namespace ResolvExpr {
59
60/// Unique identifier for matching expression resolutions to their requesting expression
61ast::UniqueId globalResnSlot = 0;
62
63namespace {
64 /// First index is which argument, second is which alternative, third is which exploded element
65 using ExplodedArgs = std::deque< std::vector< ExplodedArg > >;
66
67 /// Returns a list of alternatives with the minimum cost in the given list
68 CandidateList findMinCost( const CandidateList & candidates ) {
69 CandidateList out;
70 Cost minCost = Cost::infinity;
71 for ( const CandidateRef & r : candidates ) {
72 if ( r->cost < minCost ) {
73 minCost = r->cost;
74 out.clear();
75 out.emplace_back( r );
76 } else if ( r->cost == minCost ) {
77 out.emplace_back( r );
78 }
79 }
80 return out;
81 }
82
83 /// Computes conversion cost for a given expression to a given type
84 const ast::Expr * computeExpressionConversionCost(
85 const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost
86 ) {
87 Cost convCost = computeConversionCost(
88 arg->result, paramType, arg->get_lvalue(), symtab, env );
89 outCost += convCost;
90
91 // If there is a non-zero conversion cost, ignoring poly cost, then the expression requires
92 // conversion. Ignore poly cost for now, since this requires resolution of the cast to
93 // infer parameters and this does not currently work for the reason stated below
94 Cost tmpCost = convCost;
95 tmpCost.incPoly( -tmpCost.get_polyCost() );
96 if ( tmpCost != Cost::zero ) {
97 ast::ptr< ast::Type > newType = paramType;
98 env.apply( newType );
99 return new ast::CastExpr{ arg, newType };
100
101 // xxx - *should* be able to resolve this cast, but at the moment pointers are not
102 // castable to zero_t, but are implicitly convertible. This is clearly inconsistent,
103 // once this is fixed it should be possible to resolve the cast.
104 // xxx - this isn't working, it appears because type1 (parameter) is seen as widenable,
105 // but it shouldn't be because this makes the conversion from DT* to DT* since
106 // commontype(zero_t, DT*) is DT*, rather than nothing
107
108 // CandidateFinder finder{ symtab, env };
109 // finder.find( arg, ResolveMode::withAdjustment() );
110 // assertf( finder.candidates.size() > 0,
111 // "Somehow castable expression failed to find alternatives." );
112 // assertf( finder.candidates.size() == 1,
113 // "Somehow got multiple alternatives for known cast expression." );
114 // return finder.candidates.front()->expr;
115 }
116
117 return arg;
118 }
119
120 /// Computes conversion cost for a given candidate
121 Cost computeApplicationConversionCost(
122 CandidateRef cand, const ast::SymbolTable & symtab
123 ) {
124 auto appExpr = cand->expr.strict_as< ast::ApplicationExpr >();
125 auto pointer = appExpr->func->result.strict_as< ast::PointerType >();
126 auto function = pointer->base.strict_as< ast::FunctionType >();
127
128 Cost convCost = Cost::zero;
129 const auto & params = function->params;
130 auto param = params.begin();
131 auto & args = appExpr->args;
132
133 for ( unsigned i = 0; i < args.size(); ++i ) {
134 const ast::Type * argType = args[i]->result;
135 PRINT(
136 std::cerr << "arg expression:" << std::endl;
137 ast::print( std::cerr, args[i], 2 );
138 std::cerr << "--- results are" << std::endl;
139 ast::print( std::cerr, argType, 2 );
140 )
141
142 if ( param == params.end() ) {
143 if ( function->isVarArgs ) {
144 convCost.incUnsafe();
145 PRINT( std::cerr << "end of params with varargs function: inc unsafe: "
146 << convCost << std::endl; ; )
147 // convert reference-typed expressions into value-typed expressions
148 cand->expr = ast::mutate_field_index(
149 appExpr, &ast::ApplicationExpr::args, i,
150 referenceToRvalueConversion( args[i], convCost ) );
151 continue;
152 } else return Cost::infinity;
153 }
154
155 if ( auto def = args[i].as< ast::DefaultArgExpr >() ) {
156 // Default arguments should be free - don't include conversion cost.
157 // Unwrap them here because they are not relevant to the rest of the system
158 cand->expr = ast::mutate_field_index(
159 appExpr, &ast::ApplicationExpr::args, i, def->expr );
160 ++param;
161 continue;
162 }
163
164 // mark conversion cost and also specialization cost of param type
165 // const ast::Type * paramType = (*param)->get_type();
166 cand->expr = ast::mutate_field_index(
167 appExpr, &ast::ApplicationExpr::args, i,
168 computeExpressionConversionCost(
169 args[i], *param, symtab, cand->env, convCost ) );
170 convCost.decSpec( specCost( *param ) );
171 ++param; // can't be in for-loop update because of the continue
172 }
173
174 if ( param != params.end() ) return Cost::infinity;
175
176 // specialization cost of return types can't be accounted for directly, it disables
177 // otherwise-identical calls, like this example based on auto-newline in the I/O lib:
178 //
179 // forall(otype OS) {
180 // void ?|?(OS&, int); // with newline
181 // OS& ?|?(OS&, int); // no newline, always chosen due to more specialization
182 // }
183
184 // mark type variable and specialization cost of forall clause
185 convCost.incVar( function->forall.size() );
186 convCost.decSpec( function->assertions.size() );
187
188 return convCost;
189 }
190
191 void makeUnifiableVars(
192 const ast::FunctionType * type, ast::OpenVarSet & unifiableVars,
193 ast::AssertionSet & need
194 ) {
195 for ( auto & tyvar : type->forall ) {
196 unifiableVars[ *tyvar ] = ast::TypeData{ tyvar->base };
197 }
198 for ( auto & assn : type->assertions ) {
199 need[ assn ].isUsed = true;
200 }
201 }
202
203 /// Gets a default value from an initializer, nullptr if not present
204 const ast::ConstantExpr * getDefaultValue( const ast::Init * init ) {
205 if ( auto si = dynamic_cast< const ast::SingleInit * >( init ) ) {
206 if ( auto ce = si->value.as< ast::CastExpr >() ) {
207 return ce->arg.as< ast::ConstantExpr >();
208 } else {
209 return si->value.as< ast::ConstantExpr >();
210 }
211 }
212 return nullptr;
213 }
214
215 /// State to iteratively build a match of parameter expressions to arguments
216 struct ArgPack {
217 std::size_t parent; ///< Index of parent pack
218 ast::ptr< ast::Expr > expr; ///< The argument stored here
219 Cost cost; ///< The cost of this argument
220 ast::TypeEnvironment env; ///< Environment for this pack
221 ast::AssertionSet need; ///< Assertions outstanding for this pack
222 ast::AssertionSet have; ///< Assertions found for this pack
223 ast::OpenVarSet open; ///< Open variables for this pack
224 unsigned nextArg; ///< Index of next argument in arguments list
225 unsigned tupleStart; ///< Number of tuples that start at this index
226 unsigned nextExpl; ///< Index of next exploded element
227 unsigned explAlt; ///< Index of alternative for nextExpl > 0
228
229 ArgPack()
230 : parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ),
231 tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
232
233 ArgPack(
234 const ast::TypeEnvironment & env, const ast::AssertionSet & need,
235 const ast::AssertionSet & have, const ast::OpenVarSet & open )
236 : parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ),
237 open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
238
239 ArgPack(
240 std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env,
241 ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open,
242 unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero,
243 unsigned nextExpl = 0, unsigned explAlt = 0 )
244 : parent(parent), expr( expr ), cost( cost ), env( std::move( env ) ), need( std::move( need ) ),
245 have( std::move( have ) ), open( std::move( open ) ), nextArg( nextArg ), tupleStart( tupleStart ),
246 nextExpl( nextExpl ), explAlt( explAlt ) {}
247
248 ArgPack(
249 const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need,
250 ast::AssertionSet && have, ast::OpenVarSet && open, unsigned nextArg, Cost added )
251 : parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( std::move( env ) ),
252 need( std::move( need ) ), have( std::move( have ) ), open( std::move( open ) ), nextArg( nextArg ),
253 tupleStart( o.tupleStart ), nextExpl( 0 ), explAlt( 0 ) {}
254
255 /// true if this pack is in the middle of an exploded argument
256 bool hasExpl() const { return nextExpl > 0; }
257
258 /// Gets the list of exploded candidates for this pack
259 const ExplodedArg & getExpl( const ExplodedArgs & args ) const {
260 return args[ nextArg-1 ][ explAlt ];
261 }
262
263 /// Ends a tuple expression, consolidating the appropriate args
264 void endTuple( const std::vector< ArgPack > & packs ) {
265 // add all expressions in tuple to list, summing cost
266 std::deque< const ast::Expr * > exprs;
267 const ArgPack * pack = this;
268 if ( expr ) { exprs.emplace_front( expr ); }
269 while ( pack->tupleStart == 0 ) {
270 pack = &packs[pack->parent];
271 exprs.emplace_front( pack->expr );
272 cost += pack->cost;
273 }
274 // reset pack to appropriate tuple
275 std::vector< ast::ptr< ast::Expr > > exprv( exprs.begin(), exprs.end() );
276 expr = new ast::TupleExpr{ expr->location, std::move( exprv ) };
277 tupleStart = pack->tupleStart - 1;
278 parent = pack->parent;
279 }
280 };
281
282 /// Instantiates an argument to match a parameter, returns false if no matching results left
283 bool instantiateArgument(
284 const CodeLocation & location,
285 const ast::Type * paramType, const ast::Init * init, const ExplodedArgs & args,
286 std::vector< ArgPack > & results, std::size_t & genStart, const ResolveContext & context,
287 unsigned nTuples = 0
288 ) {
289 if ( auto tupleType = dynamic_cast< const ast::TupleType * >( paramType ) ) {
290 // paramType is a TupleType -- group args into a TupleExpr
291 ++nTuples;
292 for ( const ast::Type * type : *tupleType ) {
293 // xxx - dropping initializer changes behaviour from previous, but seems correct
294 // ^^^ need to handle the case where a tuple has a default argument
295 if ( ! instantiateArgument( location,
296 type, nullptr, args, results, genStart, context, nTuples ) ) return false;
297 nTuples = 0;
298 }
299 // re-constitute tuples for final generation
300 for ( auto i = genStart; i < results.size(); ++i ) {
301 results[i].endTuple( results );
302 }
303 return true;
304 } else if ( const ast::TypeInstType * ttype = Tuples::isTtype( paramType ) ) {
305 // paramType is a ttype, consumes all remaining arguments
306
307 // completed tuples; will be spliced to end of results to finish
308 std::vector< ArgPack > finalResults{};
309
310 // iterate until all results completed
311 std::size_t genEnd;
312 ++nTuples;
313 do {
314 genEnd = results.size();
315
316 // add another argument to results
317 for ( std::size_t i = genStart; i < genEnd; ++i ) {
318 unsigned nextArg = results[i].nextArg;
319
320 // use next element of exploded tuple if present
321 if ( results[i].hasExpl() ) {
322 const ExplodedArg & expl = results[i].getExpl( args );
323
324 unsigned nextExpl = results[i].nextExpl + 1;
325 if ( nextExpl == expl.exprs.size() ) { nextExpl = 0; }
326
327 results.emplace_back(
328 i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ),
329 copy( results[i].need ), copy( results[i].have ),
330 copy( results[i].open ), nextArg, nTuples, Cost::zero, nextExpl,
331 results[i].explAlt );
332
333 continue;
334 }
335
336 // finish result when out of arguments
337 if ( nextArg >= args.size() ) {
338 ArgPack newResult{
339 results[i].env, results[i].need, results[i].have, results[i].open };
340 newResult.nextArg = nextArg;
341 const ast::Type * argType = nullptr;
342
343 if ( nTuples > 0 || ! results[i].expr ) {
344 // first iteration or no expression to clone,
345 // push empty tuple expression
346 newResult.parent = i;
347 newResult.expr = new ast::TupleExpr( location, {} );
348 argType = newResult.expr->result;
349 } else {
350 // clone result to collect tuple
351 newResult.parent = results[i].parent;
352 newResult.cost = results[i].cost;
353 newResult.tupleStart = results[i].tupleStart;
354 newResult.expr = results[i].expr;
355 argType = newResult.expr->result;
356
357 if ( results[i].tupleStart > 0 && Tuples::isTtype( argType ) ) {
358 // the case where a ttype value is passed directly is special,
359 // e.g. for argument forwarding purposes
360 // xxx - what if passing multiple arguments, last of which is
361 // ttype?
362 // xxx - what would happen if unify was changed so that unifying
363 // tuple
364 // types flattened both before unifying lists? then pass in
365 // TupleType (ttype) below.
366 --newResult.tupleStart;
367 } else {
368 // collapse leftover arguments into tuple
369 newResult.endTuple( results );
370 argType = newResult.expr->result;
371 }
372 }
373
374 // check unification for ttype before adding to final
375 if (
376 unify(
377 ttype, argType, newResult.env, newResult.need, newResult.have,
378 newResult.open )
379 ) {
380 finalResults.emplace_back( std::move( newResult ) );
381 }
382
383 continue;
384 }
385
386 // add each possible next argument
387 for ( std::size_t j = 0; j < args[nextArg].size(); ++j ) {
388 const ExplodedArg & expl = args[nextArg][j];
389
390 // fresh copies of parent parameters for this iteration
391 ast::TypeEnvironment env = results[i].env;
392 ast::OpenVarSet open = results[i].open;
393
394 env.addActual( expl.env, open );
395
396 // skip empty tuple arguments by (nearly) cloning parent into next gen
397 if ( expl.exprs.empty() ) {
398 results.emplace_back(
399 results[i], std::move( env ), copy( results[i].need ),
400 copy( results[i].have ), std::move( open ), nextArg + 1, expl.cost );
401
402 continue;
403 }
404
405 // add new result
406 results.emplace_back(
407 i, expl.exprs.front(), std::move( env ), copy( results[i].need ),
408 copy( results[i].have ), std::move( open ), nextArg + 1, nTuples,
409 expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
410 }
411 }
412
413 // reset for next round
414 genStart = genEnd;
415 nTuples = 0;
416 } while ( genEnd != results.size() );
417
418 // splice final results onto results
419 for ( std::size_t i = 0; i < finalResults.size(); ++i ) {
420 results.emplace_back( std::move( finalResults[i] ) );
421 }
422 return ! finalResults.empty();
423 }
424
425 // iterate each current subresult
426 std::size_t genEnd = results.size();
427 for ( std::size_t i = genStart; i < genEnd; ++i ) {
428 unsigned nextArg = results[i].nextArg;
429
430 // use remainder of exploded tuple if present
431 if ( results[i].hasExpl() ) {
432 const ExplodedArg & expl = results[i].getExpl( args );
433 const ast::Expr * expr = expl.exprs[ results[i].nextExpl ];
434
435 ast::TypeEnvironment env = results[i].env;
436 ast::AssertionSet need = results[i].need, have = results[i].have;
437 ast::OpenVarSet open = results[i].open;
438
439 const ast::Type * argType = expr->result;
440
441 PRINT(
442 std::cerr << "param type is ";
443 ast::print( std::cerr, paramType );
444 std::cerr << std::endl << "arg type is ";
445 ast::print( std::cerr, argType );
446 std::cerr << std::endl;
447 )
448
449 if ( unify( paramType, argType, env, need, have, open ) ) {
450 unsigned nextExpl = results[i].nextExpl + 1;
451 if ( nextExpl == expl.exprs.size() ) { nextExpl = 0; }
452
453 results.emplace_back(
454 i, expr, std::move( env ), std::move( need ), std::move( have ), std::move( open ), nextArg,
455 nTuples, Cost::zero, nextExpl, results[i].explAlt );
456 }
457
458 continue;
459 }
460
461 // use default initializers if out of arguments
462 if ( nextArg >= args.size() ) {
463 if ( const ast::ConstantExpr * cnst = getDefaultValue( init ) ) {
464 ast::TypeEnvironment env = results[i].env;
465 ast::AssertionSet need = results[i].need, have = results[i].have;
466 ast::OpenVarSet open = results[i].open;
467
468 if ( unify( paramType, cnst->result, env, need, have, open ) ) {
469 results.emplace_back(
470 i, new ast::DefaultArgExpr{ cnst->location, cnst }, std::move( env ),
471 std::move( need ), std::move( have ), std::move( open ), nextArg, nTuples );
472 }
473 }
474
475 continue;
476 }
477
478 // Check each possible next argument
479 for ( std::size_t j = 0; j < args[nextArg].size(); ++j ) {
480 const ExplodedArg & expl = args[nextArg][j];
481
482 // fresh copies of parent parameters for this iteration
483 ast::TypeEnvironment env = results[i].env;
484 ast::AssertionSet need = results[i].need, have = results[i].have;
485 ast::OpenVarSet open = results[i].open;
486
487 env.addActual( expl.env, open );
488
489 // skip empty tuple arguments by (nearly) cloning parent into next gen
490 if ( expl.exprs.empty() ) {
491 results.emplace_back(
492 results[i], std::move( env ), std::move( need ), std::move( have ), std::move( open ),
493 nextArg + 1, expl.cost );
494
495 continue;
496 }
497
498 // consider only first exploded arg
499 const ast::Expr * expr = expl.exprs.front();
500 const ast::Type * argType = expr->result;
501
502 PRINT(
503 std::cerr << "param type is ";
504 ast::print( std::cerr, paramType );
505 std::cerr << std::endl << "arg type is ";
506 ast::print( std::cerr, argType );
507 std::cerr << std::endl;
508 )
509
510 // attempt to unify types
511 ast::ptr<ast::Type> common;
512 if ( unify( paramType, argType, env, need, have, open, common ) ) {
513 // add new result
514 assert( common );
515
516 auto paramAsEnum = dynamic_cast<const ast::EnumInstType *>(paramType);
517 auto argAsEnum =dynamic_cast<const ast::EnumInstType *>(argType);
518 if (paramAsEnum && argAsEnum) {
519 if (paramAsEnum->base->name != argAsEnum->base->name) {
520 Cost c = castCost(argType, paramType, expr, context.symtab, env);
521 if (c < Cost::infinity) {
522 CandidateFinder subFinder( context, env );
523 expr = subFinder.makeEnumOffsetCast(argAsEnum, paramAsEnum, expr, c);
524 if ( expr )
525 results.emplace_back(
526 i, expr, std::move( env ), std::move( need ), std::move( have ), std::move( open ),
527 nextArg + 1, nTuples, expl.cost + c, expl.exprs.size() == 1 ? 0 : 1, j );
528 continue;
529 } else {
530 std::cerr << "Cannot instantiate " << paramAsEnum->base->name << " with " << argAsEnum->base->name << std::endl;
531 }
532 }
533 }
534 results.emplace_back(
535 i, expr, std::move( env ), std::move( need ), std::move( have ), std::move( open ),
536 nextArg + 1, nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
537 }
538 }
539 }
540
541 // reset for next parameter
542 genStart = genEnd;
543
544 return genEnd != results.size(); // were any new results added?
545 }
546
547 /// Generate a cast expression from `arg` to `toType`
548 const ast::Expr * restructureCast(
549 ast::ptr< ast::Expr > & arg, const ast::Type * toType, ast::GeneratedFlag isGenerated = ast::GeneratedCast
550 ) {
551 if (
552 arg->result->size() > 1
553 && ! toType->isVoid()
554 && ! dynamic_cast< const ast::ReferenceType * >( toType )
555 ) {
556 // Argument is a tuple and the target type is neither void nor a reference. Cast each
557 // member of the tuple to its corresponding target type, producing the tuple of those
558 // cast expressions. If there are more components of the tuple than components in the
559 // target type, then excess components do not come out in the result expression (but
560 // UniqueExpr ensures that the side effects will still be produced)
561 if ( Tuples::maybeImpureIgnoreUnique( arg ) ) {
562 // expressions which may contain side effects require a single unique instance of
563 // the expression
564 arg = new ast::UniqueExpr{ arg->location, arg };
565 }
566 std::vector< ast::ptr< ast::Expr > > components;
567 for ( unsigned i = 0; i < toType->size(); ++i ) {
568 // cast each component
569 ast::ptr< ast::Expr > idx = new ast::TupleIndexExpr{ arg->location, arg, i };
570 components.emplace_back(
571 restructureCast( idx, toType->getComponent( i ), isGenerated ) );
572 }
573 return new ast::TupleExpr{ arg->location, std::move( components ) };
574 } else {
575 // handle normally
576 return new ast::CastExpr{ arg->location, arg, toType, isGenerated };
577 }
578 }
579
580 /// Gets the name from an untyped member expression (must be NameExpr)
581 const std::string & getMemberName( const ast::UntypedMemberExpr * memberExpr ) {
582 if ( memberExpr->member.as< ast::ConstantExpr >() ) {
583 SemanticError( memberExpr, "Indexed access to struct fields unsupported: " );
584 }
585
586 return memberExpr->member.strict_as< ast::NameExpr >()->name;
587 }
588
589 /// Actually visits expressions to find their candidate interpretations
590 class Finder final : public ast::WithShortCircuiting {
591 const ResolveContext & context;
592 const ast::SymbolTable & symtab;
593 public:
594 // static size_t traceId;
595 CandidateFinder & selfFinder;
596 CandidateList & candidates;
597 const ast::TypeEnvironment & tenv;
598 ast::ptr< ast::Type > & targetType;
599
600 enum Errors {
601 NotFound,
602 NoMatch,
603 ArgsToFew,
604 ArgsToMany,
605 RetsToFew,
606 RetsToMany,
607 NoReason
608 };
609
610 struct {
611 Errors code = NotFound;
612 } reason;
613
614 Finder( CandidateFinder & f )
615 : context( f.context ), symtab( context.symtab ), selfFinder( f ),
616 candidates( f.candidates ), tenv( f.env ), targetType( f.targetType ) {}
617
618 void previsit( const ast::Node * ) { visit_children = false; }
619
620 /// Convenience to add candidate to list
621 template<typename... Args>
622 void addCandidate( Args &&... args ) {
623 candidates.emplace_back( new Candidate{ std::forward<Args>( args )... } );
624 reason.code = NoReason;
625 }
626
627 void postvisit( const ast::ApplicationExpr * applicationExpr ) {
628 addCandidate( applicationExpr, tenv );
629 }
630
631 /// Set up candidate assertions for inference
632 void inferParameters( CandidateRef & newCand, CandidateList & out );
633
634 /// Completes a function candidate with arguments located
635 void validateFunctionCandidate(
636 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results,
637 CandidateList & out );
638
639 /// Builds a list of candidates for a function, storing them in out
640 void makeFunctionCandidates(
641 const CodeLocation & location,
642 const CandidateRef & func, const ast::FunctionType * funcType,
643 const ExplodedArgs & args, CandidateList & out );
644
645 /// Adds implicit struct-conversions to the alternative list
646 void addAnonConversions( const CandidateRef & cand );
647
648 /// Adds aggregate member interpretations
649 void addAggMembers(
650 const ast::BaseInstType * aggrInst, const ast::Expr * expr,
651 const Candidate & cand, const Cost & addedCost, const std::string & name
652 );
653
654 void addEnumValueAsCandidate(const ast::EnumInstType * instType, const ast::Expr * expr,
655 const Cost & addedCost
656 );
657
658 /// Adds tuple member interpretations
659 void addTupleMembers(
660 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand,
661 const Cost & addedCost, const ast::Expr * member
662 );
663
664 /// true if expression is an lvalue
665 static bool isLvalue( const ast::Expr * x ) {
666 return x->result && ( x->get_lvalue() || x->result.as< ast::ReferenceType >() );
667 }
668
669 void postvisit( const ast::UntypedExpr * untypedExpr );
670 void postvisit( const ast::VariableExpr * variableExpr );
671 void postvisit( const ast::ConstantExpr * constantExpr );
672 void postvisit( const ast::SizeofExpr * sizeofExpr );
673 void postvisit( const ast::AlignofExpr * alignofExpr );
674 void postvisit( const ast::CountofExpr * countExpr );
675 void postvisit( const ast::AddressExpr * addressExpr );
676 void postvisit( const ast::LabelAddressExpr * labelExpr );
677 void postvisit( const ast::CastExpr * castExpr );
678 void postvisit( const ast::VirtualCastExpr * castExpr );
679 void postvisit( const ast::KeywordCastExpr * castExpr );
680 void postvisit( const ast::UntypedMemberExpr * memberExpr );
681 void postvisit( const ast::MemberExpr * memberExpr );
682 void postvisit( const ast::NameExpr * nameExpr );
683 void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr );
684 void postvisit( const ast::OffsetofExpr * offsetofExpr );
685 void postvisit( const ast::OffsetPackExpr * offsetPackExpr );
686 void postvisit( const ast::LogicalExpr * logicalExpr );
687 void postvisit( const ast::ConditionalExpr * conditionalExpr );
688 void postvisit( const ast::CommaExpr * commaExpr );
689 void postvisit( const ast::ImplicitCopyCtorExpr * ctorExpr );
690 void postvisit( const ast::ConstructorExpr * ctorExpr );
691 void postvisit( const ast::RangeExpr * rangeExpr );
692 void postvisit( const ast::UntypedTupleExpr * tupleExpr );
693 void postvisit( const ast::TupleExpr * tupleExpr );
694 void postvisit( const ast::TupleIndexExpr * tupleExpr );
695 void postvisit( const ast::TupleAssignExpr * tupleExpr );
696 void postvisit( const ast::UniqueExpr * unqExpr );
697 void postvisit( const ast::StmtExpr * stmtExpr );
698 void postvisit( const ast::UntypedInitExpr * initExpr );
699 void postvisit( const ast::QualifiedNameExpr * qualifiedExpr );
700
701 void postvisit( const ast::InitExpr * ) {
702 assertf( false, "CandidateFinder should never see a resolved InitExpr." );
703 }
704
705 void postvisit( const ast::DeletedExpr * ) {
706 assertf( false, "CandidateFinder should never see a DeletedExpr." );
707 }
708
709 void postvisit( const ast::GenericExpr * ) {
710 assertf( false, "_Generic is not yet supported." );
711 }
712 };
713
714 /// Set up candidate assertions for inference
715 void Finder::inferParameters( CandidateRef & newCand, CandidateList & out ) {
716 // Set need bindings for any unbound assertions
717 ast::UniqueId crntResnSlot = 0; // matching ID for this expression's assertions
718 for ( auto & assn : newCand->need ) {
719 // skip already-matched assertions
720 if ( assn.second.resnSlot != 0 ) continue;
721 // assign slot for expression if needed
722 if ( crntResnSlot == 0 ) { crntResnSlot = ++globalResnSlot; }
723 // fix slot to assertion
724 assn.second.resnSlot = crntResnSlot;
725 }
726 // pair slot to expression
727 if ( crntResnSlot != 0 ) {
728 newCand->expr.get_and_mutate()->inferred.resnSlots().emplace_back( crntResnSlot );
729 }
730
731 // add to output list; assertion satisfaction will occur later
732 out.emplace_back( newCand );
733 }
734
735 /// Completes a function candidate with arguments located
736 void Finder::validateFunctionCandidate(
737 const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results,
738 CandidateList & out
739 ) {
740 ast::ApplicationExpr * appExpr =
741 new ast::ApplicationExpr{ func->expr->location, func->expr };
742 // sum cost and accumulate arguments
743 std::deque< const ast::Expr * > args;
744 Cost cost = func->cost;
745 const ArgPack * pack = &result;
746 while ( pack->expr ) {
747 args.emplace_front( pack->expr );
748 cost += pack->cost;
749 pack = &results[pack->parent];
750 }
751 std::vector< ast::ptr< ast::Expr > > vargs( args.begin(), args.end() );
752 appExpr->args = std::move( vargs );
753 // build and validate new candidate
754 auto newCand =
755 std::make_shared<Candidate>( appExpr, result.env, result.open, result.need, cost );
756 PRINT(
757 std::cerr << "instantiate function success: " << appExpr << std::endl;
758 std::cerr << "need assertions:" << std::endl;
759 ast::print( std::cerr, result.need, 2 );
760 )
761 inferParameters( newCand, out );
762 }
763
764 /// Builds a list of candidates for a function, storing them in out
765 void Finder::makeFunctionCandidates(
766 const CodeLocation & location,
767 const CandidateRef & func, const ast::FunctionType * funcType,
768 const ExplodedArgs & args, CandidateList & out
769 ) {
770 ast::OpenVarSet funcOpen;
771 ast::AssertionSet funcNeed, funcHave;
772 ast::TypeEnvironment funcEnv{ func->env };
773 makeUnifiableVars( funcType, funcOpen, funcNeed );
774 // add all type variables as open variables now so that those not used in the
775 // parameter list are still considered open
776 funcEnv.add( funcType->forall );
777
778 if ( targetType && ! targetType->isVoid() && ! funcType->returns.empty() ) {
779 // attempt to narrow based on expected target type
780 const ast::Type * returnType = funcType->returns.front();
781 if ( selfFinder.strictMode ) {
782 if ( !unifyExact(
783 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, noWiden() ) // xxx - is no widening correct?
784 ) {
785 // unification failed, do not pursue this candidate
786 return;
787 }
788 } else {
789 if ( !unify(
790 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen )
791 ) {
792 // unification failed, do not pursue this candidate
793 return;
794 }
795 }
796 }
797
798 // iteratively build matches, one parameter at a time
799 std::vector< ArgPack > results;
800 results.emplace_back( funcEnv, funcNeed, funcHave, funcOpen );
801 std::size_t genStart = 0;
802
803 // xxx - how to handle default arg after change to ftype representation?
804 if (const ast::VariableExpr * varExpr = func->expr.as<ast::VariableExpr>()) {
805 if (const ast::FunctionDecl * funcDecl = varExpr->var.as<ast::FunctionDecl>()) {
806 // function may have default args only if directly calling by name
807 // must use types on candidate however, due to RenameVars substitution
808 auto nParams = funcType->params.size();
809
810 for (size_t i=0; i<nParams; ++i) {
811 auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>();
812 if ( !instantiateArgument( location,
813 funcType->params[i], obj->init, args, results, genStart, context)) return;
814 }
815 goto endMatch;
816 }
817 }
818 for ( const auto & param : funcType->params ) {
819 // Try adding the arguments corresponding to the current parameter to the existing
820 // matches
821 // no default args for indirect calls
822 if ( !instantiateArgument( location,
823 param, nullptr, args, results, genStart, context ) ) return;
824 }
825
826 endMatch:
827 if ( funcType->isVarArgs ) {
828 // append any unused arguments to vararg pack
829 std::size_t genEnd;
830 do {
831 genEnd = results.size();
832
833 // iterate results
834 for ( std::size_t i = genStart; i < genEnd; ++i ) {
835 unsigned nextArg = results[i].nextArg;
836
837 // use remainder of exploded tuple if present
838 if ( results[i].hasExpl() ) {
839 const ExplodedArg & expl = results[i].getExpl( args );
840
841 unsigned nextExpl = results[i].nextExpl + 1;
842 if ( nextExpl == expl.exprs.size() ) { nextExpl = 0; }
843
844 results.emplace_back(
845 i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ),
846 copy( results[i].need ), copy( results[i].have ),
847 copy( results[i].open ), nextArg, 0, Cost::zero, nextExpl,
848 results[i].explAlt );
849
850 continue;
851 }
852
853 // finish result when out of arguments
854 if ( nextArg >= args.size() ) {
855 validateFunctionCandidate( func, results[i], results, out );
856
857 continue;
858 }
859
860 // add each possible next argument
861 for ( std::size_t j = 0; j < args[nextArg].size(); ++j ) {
862 const ExplodedArg & expl = args[nextArg][j];
863
864 // fresh copies of parent parameters for this iteration
865 ast::TypeEnvironment env = results[i].env;
866 ast::OpenVarSet open = results[i].open;
867
868 env.addActual( expl.env, open );
869
870 // skip empty tuple arguments by (nearly) cloning parent into next gen
871 if ( expl.exprs.empty() ) {
872 results.emplace_back(
873 results[i], std::move( env ), copy( results[i].need ),
874 copy( results[i].have ), std::move( open ), nextArg + 1,
875 expl.cost );
876
877 continue;
878 }
879
880 // add new result
881 results.emplace_back(
882 i, expl.exprs.front(), std::move( env ), copy( results[i].need ),
883 copy( results[i].have ), std::move( open ), nextArg + 1, 0, expl.cost,
884 expl.exprs.size() == 1 ? 0 : 1, j );
885 }
886 }
887
888 genStart = genEnd;
889 } while( genEnd != results.size() );
890 } else {
891 // filter out the results that don't use all the arguments
892 for ( std::size_t i = genStart; i < results.size(); ++i ) {
893 ArgPack & result = results[i];
894 if ( ! result.hasExpl() && result.nextArg >= args.size() ) {
895 validateFunctionCandidate( func, result, results, out );
896 }
897 }
898 }
899 }
900
901 void Finder::addEnumValueAsCandidate( const ast::EnumInstType * enumInst, const ast::Expr * expr,
902 const Cost & addedCost
903 ) {
904 if ( enumInst->base->base ) {
905 CandidateFinder finder( context, tenv );
906 auto location = expr->location;
907 auto callExpr = new ast::UntypedExpr(
908 location, new ast::NameExpr( location, "value" ), {expr}
909 );
910 finder.find( callExpr );
911 CandidateList winners = findMinCost( finder.candidates );
912 if (winners.size() != 1) {
913 SemanticError( callExpr, "Ambiguous expression in value..." );
914 }
915 CandidateRef & choice = winners.front();
916 choice->cost += addedCost;
917 addAnonConversions(choice);
918 candidates.emplace_back( std::move(choice) );
919 }
920 }
921
922 /// Adds implicit struct-conversions to the alternative list
923 void Finder::addAnonConversions( const CandidateRef & cand ) {
924 // adds anonymous member interpretations whenever an aggregate value type is seen.
925 // it's okay for the aggregate expression to have reference type -- cast it to the
926 // base type to treat the aggregate as the referenced value
927 ast::ptr< ast::Expr > aggrExpr( cand->expr );
928 ast::ptr< ast::Type > & aggrType = aggrExpr.get_and_mutate()->result;
929 cand->env.apply( aggrType );
930
931 if ( aggrType.as< ast::ReferenceType >() ) {
932 aggrExpr = new ast::CastExpr{ aggrExpr, aggrType->stripReferences() };
933 }
934
935 if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) {
936 addAggMembers( structInst, aggrExpr, *cand, Cost::unsafe, "" );
937 } else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) {
938 addAggMembers( unionInst, aggrExpr, *cand, Cost::unsafe, "" );
939 } else if ( auto enumInst = aggrExpr->result.as< ast::EnumInstType >() ) {
940 addEnumValueAsCandidate( enumInst, aggrExpr, Cost::implicit );
941 }
942 }
943
944 /// Adds aggregate member interpretations
945 void Finder::addAggMembers(
946 const ast::BaseInstType * aggrInst, const ast::Expr * expr,
947 const Candidate & cand, const Cost & addedCost, const std::string & name
948 ) {
949 for ( const ast::Decl * decl : aggrInst->lookup( name ) ) {
950 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl );
951 CandidateRef newCand = std::make_shared<Candidate>(
952 cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
953 // add anonymous member interpretations whenever an aggregate value type is seen
954 // as a member expression
955 addAnonConversions( newCand );
956 candidates.emplace_back( std::move( newCand ) );
957 }
958 }
959
960 /// Adds tuple member interpretations
961 void Finder::addTupleMembers(
962 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand,
963 const Cost & addedCost, const ast::Expr * member
964 ) {
965 if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) {
966 // get the value of the constant expression as an int, must be between 0 and the
967 // length of the tuple to have meaning
968 long long val = constantExpr->intValue();
969 if ( val >= 0 && (unsigned long long)val < tupleType->size() ) {
970 addCandidate(
971 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val },
972 addedCost );
973 }
974 }
975 }
976
977 void Finder::postvisit( const ast::UntypedExpr * untypedExpr ) {
978 std::vector< CandidateFinder > argCandidates =
979 selfFinder.findSubExprs( untypedExpr->args );
980
981 // take care of possible tuple assignments
982 // if not tuple assignment, handled as normal function call
983 Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates );
984
985 CandidateFinder funcFinder( context, tenv );
986 std::string funcName;
987 if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {
988 funcName = nameExpr->name;
989 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
990 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
991 assertf(!argCandidates.empty(), "special function call without argument");
992 for (auto & firstArgCand: argCandidates[0]) {
993 ast::ptr<ast::Type> argType = firstArgCand->expr->result;
994 firstArgCand->env.apply(argType);
995 // strip references
996 // xxx - is this correct?
997 while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base;
998
999 // convert 1-tuple to plain type
1000 if (auto tuple = argType.as<ast::TupleType>()) {
1001 if (tuple->size() == 1) {
1002 argType = tuple->types[0];
1003 }
1004 }
1005
1006 // if argType is an unbound type parameter, all special functions need to be searched.
1007 if (isUnboundType(argType)) {
1008 funcFinder.otypeKeys.clear();
1009 break;
1010 }
1011
1012 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);
1013 else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));
1014 }
1015 }
1016 }
1017 // if candidates are already produced, do not fail
1018 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates?
1019 // this means there exists ctor/assign functions with a tuple as first parameter.
1020 ResolveMode mode = {
1021 true, // adjust
1022 !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name
1023 selfFinder.candidates.empty() // failfast if other options are not found
1024 };
1025 funcFinder.find( untypedExpr->func, mode );
1026 // short-circuit if no candidates
1027 // if ( funcFinder.candidates.empty() ) return;
1028
1029 reason.code = NoMatch;
1030
1031 // find function operators
1032 ast::ptr< ast::Expr > opExpr = new ast::NameExpr{ untypedExpr->location, "?()" }; // ??? why not ?{}
1033 CandidateFinder opFinder( context, tenv );
1034 // okay if there aren't any function operations
1035 opFinder.find( opExpr, ResolveMode::withoutFailFast() );
1036 PRINT(
1037 std::cerr << "known function ops:" << std::endl;
1038 print( std::cerr, opFinder.candidates, 1 );
1039 )
1040
1041 // pre-explode arguments
1042 ExplodedArgs argExpansions;
1043 for ( const CandidateFinder & args : argCandidates ) {
1044 argExpansions.emplace_back();
1045 auto & argE = argExpansions.back();
1046 for ( const CandidateRef & arg : args ) { argE.emplace_back( *arg, symtab ); }
1047 }
1048
1049 // Find function matches
1050 CandidateList found;
1051 SemanticErrorException errors;
1052
1053 for ( CandidateRef & func : funcFinder ) {
1054 try {
1055 PRINT(
1056 std::cerr << "working on alternative:" << std::endl;
1057 print( std::cerr, *func, 2 );
1058 )
1059
1060 // check if the type is a pointer to function
1061 const ast::Type * funcResult = func->expr->result->stripReferences();
1062 if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) {
1063 if ( auto function = pointer->base.as< ast::FunctionType >() ) {
1064 // if (!selfFinder.allowVoid && function->returns.empty()) continue;
1065 CandidateRef newFunc{ new Candidate{ *func } };
1066 newFunc->expr =
1067 referenceToRvalueConversion( newFunc->expr, newFunc->cost );
1068 makeFunctionCandidates( untypedExpr->location,
1069 newFunc, function, argExpansions, found );
1070 }
1071 } else if (
1072 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
1073 ) {
1074 if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
1075 if ( auto function = clz->bound.as< ast::FunctionType >() ) {
1076 CandidateRef newFunc( new Candidate( *func ) );
1077 newFunc->expr =
1078 referenceToRvalueConversion( newFunc->expr, newFunc->cost );
1079 makeFunctionCandidates( untypedExpr->location,
1080 newFunc, function, argExpansions, found );
1081 }
1082 }
1083 }
1084 } catch ( SemanticErrorException & e ) { errors.append( e ); }
1085 }
1086
1087 // Find matches on function operators `?()`
1088 if ( ! opFinder.candidates.empty() ) {
1089 // add exploded function alternatives to front of argument list
1090 std::vector< ExplodedArg > funcE;
1091 funcE.reserve( funcFinder.candidates.size() );
1092 for ( const CandidateRef & func : funcFinder ) {
1093 funcE.emplace_back( *func, symtab );
1094 }
1095 argExpansions.emplace_front( std::move( funcE ) );
1096
1097 for ( const CandidateRef & op : opFinder ) {
1098 try {
1099 // check if type is pointer-to-function
1100 const ast::Type * opResult = op->expr->result->stripReferences();
1101 if ( auto pointer = dynamic_cast< const ast::PointerType * >( opResult ) ) {
1102 if ( auto function = pointer->base.as< ast::FunctionType >() ) {
1103 CandidateRef newOp{ new Candidate{ *op} };
1104 newOp->expr =
1105 referenceToRvalueConversion( newOp->expr, newOp->cost );
1106 makeFunctionCandidates( untypedExpr->location,
1107 newOp, function, argExpansions, found );
1108 }
1109 }
1110 } catch ( SemanticErrorException & e ) { errors.append( e ); }
1111 }
1112 }
1113
1114 // Implement SFINAE; resolution errors are only errors if there aren't any non-error
1115 // candidates
1116 if ( found.empty() ) errors.throwIfNonEmpty();
1117
1118 // only keep the best matching intrinsic result to match C semantics (no unexpected narrowing/widening)
1119 // TODO: keep one for each set of argument candidates?
1120 Cost intrinsicCost = Cost::infinity;
1121 CandidateList intrinsicResult;
1122
1123 // Compute conversion costs
1124 for ( CandidateRef & withFunc : found ) {
1125 Cost cvtCost = computeApplicationConversionCost( withFunc, symtab );
1126
1127 if (funcName == "?|?") {
1128 PRINT(
1129 auto appExpr = withFunc->expr.strict_as< ast::ApplicationExpr >();
1130 auto pointer = appExpr->func->result.strict_as< ast::PointerType >();
1131 auto function = pointer->base.strict_as< ast::FunctionType >();
1132
1133 std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl;
1134 std::cerr << "parameters are:" << std::endl;
1135 ast::printAll( std::cerr, function->params, 2 );
1136 std::cerr << "arguments are:" << std::endl;
1137 ast::printAll( std::cerr, appExpr->args, 2 );
1138 std::cerr << "bindings are:" << std::endl;
1139 ast::print( std::cerr, withFunc->env, 2 );
1140 std::cerr << "cost is: " << withFunc->cost << std::endl;
1141 std::cerr << "cost of conversion is:" << cvtCost << std::endl;
1142 )
1143 }
1144 if ( cvtCost != Cost::infinity ) {
1145 withFunc->cvtCost = cvtCost;
1146 withFunc->cost += cvtCost;
1147 auto func = withFunc->expr.strict_as<ast::ApplicationExpr>()->func.as<ast::VariableExpr>();
1148 if (func && func->var->linkage == ast::Linkage::Intrinsic) {
1149 if (withFunc->cost < intrinsicCost) {
1150 intrinsicResult.clear();
1151 intrinsicCost = withFunc->cost;
1152 }
1153 if (withFunc->cost == intrinsicCost) {
1154 intrinsicResult.emplace_back(std::move(withFunc));
1155 }
1156 } else {
1157 candidates.emplace_back( std::move( withFunc ) );
1158 }
1159 }
1160 }
1161 spliceBegin( candidates, intrinsicResult );
1162 found = std::move( candidates );
1163
1164 // use a new list so that candidates are not examined by addAnonConversions twice
1165 // CandidateList winners = findMinCost( found );
1166 // promoteCvtCost( winners );
1167
1168 // function may return a struct/union value, in which case we need to add candidates
1169 // for implicit conversions to each of the anonymous members, which must happen after
1170 // `findMinCost`, since anon conversions are never the cheapest
1171 for ( const CandidateRef & c : found ) {
1172 addAnonConversions( c );
1173 }
1174 // would this be too slow when we don't check cost anymore?
1175 spliceBegin( candidates, found );
1176
1177 if ( candidates.empty() && targetType && ! targetType->isVoid() && !selfFinder.strictMode ) {
1178 // If resolution is unsuccessful with a target type, try again without, since it
1179 // will sometimes succeed when it wouldn't with a target type binding.
1180 // For example:
1181 // forall( otype T ) T & ?[]( T *, ptrdiff_t );
1182 // const char * x = "hello world";
1183 // unsigned char ch = x[0];
1184 // Fails with simple return type binding (xxx -- check this!) as follows:
1185 // * T is bound to unsigned char
1186 // * (x: const char *) is unified with unsigned char *, which fails
1187 // xxx -- fix this better
1188 targetType = nullptr;
1189 postvisit( untypedExpr );
1190 }
1191 }
1192
1193 void Finder::postvisit( const ast::AddressExpr * addressExpr ) {
1194 CandidateFinder finder( context, tenv );
1195 finder.find( addressExpr->arg );
1196
1197 if ( finder.candidates.empty() ) return;
1198
1199 reason.code = NoMatch;
1200
1201 for ( CandidateRef & r : finder.candidates ) {
1202 if ( !isLvalue( r->expr ) ) continue;
1203 addCandidate( *r, new ast::AddressExpr{ addressExpr->location, r->expr } );
1204 }
1205 }
1206
1207 void Finder::postvisit( const ast::LabelAddressExpr * labelExpr ) {
1208 addCandidate( labelExpr, tenv );
1209 }
1210
1211 void Finder::postvisit( const ast::CastExpr * castExpr ) {
1212 ast::ptr< ast::Type > toType = castExpr->result;
1213 assert( toType );
1214 toType = resolveTypeof( toType, context );
1215 toType = adjustExprType( toType, tenv, symtab );
1216
1217 CandidateFinder finder( context, tenv, toType );
1218 if (toType->isVoid()) {
1219 finder.allowVoid = true;
1220 }
1221 if ( ast::ReturnCast == castExpr->kind ) {
1222 finder.strictMode = true;
1223 finder.find( castExpr->arg, ResolveMode::withAdjustment() );
1224
1225 // return casts are eliminated (merely selecting an overload, no actual operation)
1226 for (auto & cand : finder.candidates) {
1227 if (typesCompatibleIgnoreQualifiers(toType, cand->expr->result, cand->env)) {
1228 candidates.push_back (cand);
1229 }
1230 }
1231 // candidates = std::move(finder.candidates);
1232 return;
1233 }
1234 else if (toType->isVoid()) {
1235 finder.find( castExpr->arg ); // no adjust
1236 }
1237 else {
1238 finder.find( castExpr->arg, ResolveMode::withAdjustment() );
1239 }
1240
1241 if ( !finder.candidates.empty() ) reason.code = NoMatch;
1242
1243 CandidateList matches;
1244 Cost minExprCost = Cost::infinity;
1245 // Cost minCastCost = Cost::infinity;
1246 for ( CandidateRef & cand : finder.candidates ) {
1247 ast::ptr< ast::Type > fromType = cand->expr->result;
1248 assert( fromType );
1249 fromType = resolveTypeof( fromType, context );
1250 fromType = adjustExprType( fromType, tenv, symtab );
1251
1252 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
1253 ast::OpenVarSet open( cand->open );
1254
1255 cand->env.extractOpenVars( open );
1256
1257 // It is possible that a cast can throw away some values in a multiply-valued
1258 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the
1259 // subexpression results that are cast directly. The candidate is invalid if it
1260 // has fewer results than there are types to cast to.
1261 int discardedValues = fromType->size() - toType->size();
1262 if ( discardedValues < 0 ) continue;
1263
1264 // unification run for side-effects
1265 unify( toType, fromType, cand->env, need, have, open );
1266 Cost thisCost =
1267 (castExpr->isGenerated == ast::GeneratedFlag::GeneratedCast)
1268 ? conversionCost( fromType, toType, cand->expr->get_lvalue(), symtab, cand->env )
1269 : castCost( fromType, toType, cand->expr->get_lvalue(), symtab, cand->env );
1270
1271 // Redefine enum cast
1272 auto argAsEnum = fromType.as<ast::EnumInstType>();
1273 auto toAsEnum = toType.as<ast::EnumInstType>();
1274 if ( argAsEnum && toAsEnum && argAsEnum->name != toAsEnum->name ) {
1275 CandidateFinder subFinder(context, tenv);
1276 ast::ptr<ast::Expr> offsetExpr = subFinder.makeEnumOffsetCast(argAsEnum, toAsEnum, cand->expr, thisCost);
1277 if ( offsetExpr )
1278 cand->expr = offsetExpr;
1279 }
1280
1281 PRINT(
1282 std::cerr << "working on cast with result: " << toType << std::endl;
1283 std::cerr << "and expr type: " << fromType << std::endl;
1284 std::cerr << "env: " << cand->env << std::endl;
1285 )
1286 if ( thisCost != Cost::infinity ) {
1287 PRINT(
1288 std::cerr << "has finite cost." << std::endl;
1289 )
1290 // count one safe conversion for each value that is thrown away
1291 thisCost.incSafe( discardedValues );
1292
1293 // See Aaron Moss, page 47; this reasoning does not hold since implicit conversions
1294 // can create the same resolution issue. The C intrinsic interpretations are pruned
1295 // immediately for the lowest cost option regardless of result type. Related code in
1296 // postvisit (UntypedExpr).
1297 // Cast expression costs are updated now to use the general rules.
1298 /*
1299 // select first on argument cost, then conversion cost
1300 if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
1301 minExprCost = cand->cost;
1302 minCastCost = thisCost;
1303 matches.clear();
1304 }
1305 // ambigious case, still output candidates to print in error message
1306 if ( cand->cost == minExprCost && thisCost == minCastCost ) {
1307 */
1308 cand->cost += thisCost;
1309 if (cand->cost < minExprCost) {
1310 minExprCost = cand->cost;
1311 matches.clear();
1312 }
1313 if (cand->cost == minExprCost) {
1314 CandidateRef newCand = std::make_shared<Candidate>(
1315 restructureCast( cand->expr, toType, castExpr->isGenerated ),
1316 copy( cand->env ), std::move( open ), std::move( need ), cand->cost);
1317 // currently assertions are always resolved immediately so this should have no effect.
1318 // if this somehow changes in the future (e.g. delayed by indeterminate return type)
1319 // we may need to revisit the logic.
1320 inferParameters( newCand, matches );
1321 }
1322 // else skip, better alternatives found
1323
1324 }
1325 }
1326 candidates = std::move(matches);
1327 //CandidateList minArgCost = findMinCost( matches );
1328 //promoteCvtCost( minArgCost );
1329 //candidates = findMinCost( minArgCost );
1330 }
1331
1332 void Finder::postvisit( const ast::VirtualCastExpr * castExpr ) {
1333 assertf( castExpr->result, "Implicit virtual cast targets not yet supported." );
1334 CandidateFinder finder( context, tenv );
1335 // don't prune here, all alternatives guaranteed to have same type
1336 finder.find( castExpr->arg, ResolveMode::withoutPrune() );
1337 for ( CandidateRef & r : finder.candidates ) {
1338 addCandidate(
1339 *r,
1340 new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } );
1341 }
1342 }
1343
1344 void Finder::postvisit( const ast::KeywordCastExpr * castExpr ) {
1345 const auto & loc = castExpr->location;
1346 assertf( castExpr->result, "Cast target should have been set in Validate." );
1347 auto ref = castExpr->result.strict_as<ast::ReferenceType>();
1348 auto inst = ref->base.strict_as<ast::StructInstType>();
1349 auto target = inst->base.get();
1350
1351 CandidateFinder finder( context, tenv );
1352
1353 auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) {
1354 for (auto & cand : found) {
1355 const ast::Type * expr = cand->expr->result.get();
1356 if (expect_ref) {
1357 auto res = dynamic_cast<const ast::ReferenceType*>(expr);
1358 if (!res) { continue; }
1359 expr = res->base.get();
1360 }
1361
1362 if (auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {
1363 auto td = cand->env.lookup(*insttype);
1364 if (!td) { continue; }
1365 expr = td->bound.get();
1366 }
1367
1368 if (auto base = dynamic_cast<const ast::StructInstType*>(expr)) {
1369 if (base->base == target) {
1370 candidates.push_back( std::move(cand) );
1371 reason.code = NoReason;
1372 }
1373 }
1374 }
1375 };
1376
1377 try {
1378 // Attempt 1 : turn (thread&)X into (thread$&)X.__thrd
1379 // Clone is purely for memory management
1380 std::unique_ptr<const ast::Expr> tech1 { new ast::UntypedMemberExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.field), castExpr->arg) };
1381
1382 // don't prune here, since it's guaranteed all alternatives will have the same type
1383 finder.find( tech1.get(), ResolveMode::withoutPrune() );
1384 pick_alternatives(finder.candidates, false);
1385
1386 return;
1387 } catch(SemanticErrorException & ) {}
1388
1389 // Fallback : turn (thread&)X into (thread$&)get_thread(X)
1390 std::unique_ptr<const ast::Expr> fallback { ast::UntypedExpr::createDeref(loc, new ast::UntypedExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.getter), { castExpr->arg })) };
1391 // don't prune here, since it's guaranteed all alternatives will have the same type
1392 finder.find( fallback.get(), ResolveMode::withoutPrune() );
1393
1394 pick_alternatives(finder.candidates, true);
1395
1396 // Whatever happens here, we have no more fallbacks
1397 }
1398
1399 void Finder::postvisit( const ast::UntypedMemberExpr * memberExpr ) {
1400 CandidateFinder aggFinder( context, tenv );
1401 aggFinder.find( memberExpr->aggregate, ResolveMode::withAdjustment() );
1402 for ( CandidateRef & agg : aggFinder.candidates ) {
1403 // it's okay for the aggregate expression to have reference type -- cast it to the
1404 // base type to treat the aggregate as the referenced value
1405 Cost addedCost = Cost::zero;
1406 agg->expr = referenceToRvalueConversion( agg->expr, addedCost );
1407
1408 // find member of the given type
1409 if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) {
1410 addAggMembers(
1411 structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
1412 } else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) {
1413 addAggMembers(
1414 unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
1415 } else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) {
1416 addTupleMembers( tupleType, agg->expr, *agg, addedCost, memberExpr->member );
1417 }
1418 }
1419 }
1420
1421 void Finder::postvisit( const ast::MemberExpr * memberExpr ) {
1422 addCandidate( memberExpr, tenv );
1423 }
1424
1425 void Finder::postvisit( const ast::NameExpr * nameExpr ) {
1426 std::vector< ast::SymbolTable::IdData > declList;
1427 if (!selfFinder.otypeKeys.empty()) {
1428 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
1429 assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str());
1430
1431 for (auto & otypeKey: selfFinder.otypeKeys) {
1432 auto result = symtab.specialLookupId(kind, otypeKey);
1433 declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end()));
1434 }
1435 } else {
1436 declList = symtab.lookupIdIgnoreHidden( nameExpr->name );
1437 }
1438 PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
1439
1440 if ( declList.empty() ) return;
1441
1442 reason.code = NoMatch;
1443
1444 for ( auto & data : declList ) {
1445 Cost cost = Cost::zero;
1446 ast::Expr * newExpr = data.combine( nameExpr->location, cost );
1447
1448 CandidateRef newCand = std::make_shared<Candidate>(
1449 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero,
1450 cost );
1451 if (newCand->expr->env) {
1452 newCand->env.add(*newCand->expr->env);
1453 auto mutExpr = newCand->expr.get_and_mutate();
1454 mutExpr->env = nullptr;
1455 newCand->expr = mutExpr;
1456 }
1457
1458 PRINT(
1459 std::cerr << "decl is ";
1460 ast::print( std::cerr, data.id );
1461 std::cerr << std::endl;
1462 std::cerr << "newExpr is ";
1463 ast::print( std::cerr, newExpr );
1464 std::cerr << std::endl;
1465 )
1466 newCand->expr = ast::mutate_field(
1467 newCand->expr.get(), &ast::Expr::result,
1468 renameTyVars( newCand->expr->result ) );
1469 // add anonymous member interpretations whenever an aggregate value type is seen
1470 // as a name expression
1471 addAnonConversions( newCand );
1472 candidates.emplace_back( std::move( newCand ) );
1473 }
1474 }
1475
1476 void Finder::postvisit(const ast::VariableExpr *variableExpr) {
1477 // not sufficient to just pass `variableExpr` here, type might have changed
1478
1479 auto cand = new Candidate(variableExpr, tenv);
1480 candidates.emplace_back(cand);
1481 }
1482
1483 void Finder::postvisit( const ast::ConstantExpr * constantExpr ) {
1484 addCandidate( constantExpr, tenv );
1485 }
1486
1487 void Finder::postvisit( const ast::SizeofExpr * sizeofExpr ) {
1488 const ast::Type * type = resolveTypeof( sizeofExpr->type, context );
1489 const ast::Type * sizeType = context.global.sizeType.get();
1490 addCandidate(
1491 new ast::SizeofExpr( sizeofExpr->location, type, sizeType ),
1492 tenv );
1493 }
1494
1495 void Finder::postvisit( const ast::AlignofExpr * alignofExpr ) {
1496 const ast::Type * type = resolveTypeof( alignofExpr->type, context );
1497 const ast::Type * sizeType = context.global.sizeType.get();
1498 addCandidate(
1499 new ast::AlignofExpr( alignofExpr->location, type, sizeType ),
1500 tenv );
1501 }
1502
1503 void Finder::postvisit( const ast::CountofExpr * countExpr ) {
1504 if ( auto enumInst = countExpr->type.as<ast::EnumInstType>() ) {
1505 addCandidate( ast::ConstantExpr::from_ulong( countExpr->location, enumInst->base->members.size()), tenv );
1506 return;
1507 }
1508 auto untypedFirst = ast::UntypedExpr::createCall( countExpr->location, "lowerBound", {} );
1509 auto castFirst = new ast::CastExpr( countExpr->location, untypedFirst , countExpr->type );
1510 const ast::UntypedExpr * untyped = ast::UntypedExpr::createCall(
1511 countExpr->location, "Countof", { castFirst }
1512 );
1513 CandidateFinder finder( context, tenv );
1514 finder.find( untyped );
1515 CandidateList winners = findMinCost( finder.candidates );
1516 if ( winners.size() == 0 ) {
1517 SemanticError( countExpr, "Countof is not implemented: " );
1518 }
1519 if ( winners.size() != 1 ) {
1520 SemanticError( countExpr, "Ambiguous expression in countof: " );
1521 }
1522 CandidateRef & choice = winners.front();
1523 choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
1524 addCandidate( *choice, choice->expr );
1525 }
1526
1527 void Finder::postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) {
1528 const ast::BaseInstType * aggInst;
1529 if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ;
1530 else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ;
1531 else return;
1532
1533 const ast::Type * sizeType = context.global.sizeType.get();
1534 for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) {
1535 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member );
1536 addCandidate(
1537 new ast::OffsetofExpr( offsetofExpr->location, aggInst, dwt, sizeType ), tenv );
1538 }
1539 }
1540
1541 void Finder::postvisit( const ast::OffsetofExpr * offsetofExpr ) {
1542 addCandidate( offsetofExpr, tenv );
1543 }
1544
1545 void Finder::postvisit( const ast::OffsetPackExpr * offsetPackExpr ) {
1546 addCandidate( offsetPackExpr, tenv );
1547 }
1548
1549 void Finder::postvisit( const ast::LogicalExpr * logicalExpr ) {
1550 CandidateFinder finder1( context, tenv );
1551 ast::ptr<ast::Expr> arg1 = createCondExpr( logicalExpr->arg1 );
1552 finder1.find( arg1, ResolveMode::withAdjustment() );
1553 if ( finder1.candidates.empty() ) return;
1554
1555 CandidateFinder finder2( context, tenv );
1556 ast::ptr<ast::Expr> arg2 = createCondExpr( logicalExpr->arg2 );
1557 finder2.find( arg2, ResolveMode::withAdjustment() );
1558 if ( finder2.candidates.empty() ) return;
1559
1560 reason.code = NoMatch;
1561
1562 for ( const CandidateRef & r1 : finder1.candidates ) {
1563 for ( const CandidateRef & r2 : finder2.candidates ) {
1564 ast::TypeEnvironment env{ r1->env };
1565 env.simpleCombine( r2->env );
1566 ast::OpenVarSet open{ r1->open };
1567 mergeOpenVars( open, r2->open );
1568 ast::AssertionSet need;
1569 mergeAssertionSet( need, r1->need );
1570 mergeAssertionSet( need, r2->need );
1571
1572 addCandidate(
1573 new ast::LogicalExpr{
1574 logicalExpr->location, r1->expr, r2->expr, logicalExpr->isAnd },
1575 std::move( env ), std::move( open ), std::move( need ), r1->cost + r2->cost );
1576 }
1577 }
1578 }
1579
1580 void Finder::postvisit( const ast::ConditionalExpr * conditionalExpr ) {
1581 // candidates for condition
1582 ast::ptr<ast::Expr> arg1 = createCondExpr( conditionalExpr->arg1 );
1583 CandidateFinder finder1( context, tenv );
1584 finder1.find( arg1, ResolveMode::withAdjustment() );
1585 if ( finder1.candidates.empty() ) return;
1586
1587 // candidates for true result
1588 // FIX ME: resolves and runs arg1 twice when arg2 is missing.
1589 ast::Expr const * arg2 = conditionalExpr->arg2;
1590 arg2 = arg2 ? arg2 : conditionalExpr->arg1.get();
1591 CandidateFinder finder2( context, tenv );
1592 finder2.allowVoid = true;
1593 finder2.find( arg2, ResolveMode::withAdjustment() );
1594 if ( finder2.candidates.empty() ) return;
1595
1596 // candidates for false result
1597 CandidateFinder finder3( context, tenv );
1598 finder3.allowVoid = true;
1599 finder3.find( conditionalExpr->arg3, ResolveMode::withAdjustment() );
1600 if ( finder3.candidates.empty() ) return;
1601
1602 reason.code = NoMatch;
1603
1604 for ( const CandidateRef & r1 : finder1.candidates ) {
1605 for ( const CandidateRef & r2 : finder2.candidates ) {
1606 for ( const CandidateRef & r3 : finder3.candidates ) {
1607 ast::TypeEnvironment env{ r1->env };
1608 env.simpleCombine( r2->env );
1609 env.simpleCombine( r3->env );
1610 ast::OpenVarSet open{ r1->open };
1611 mergeOpenVars( open, r2->open );
1612 mergeOpenVars( open, r3->open );
1613 ast::AssertionSet need;
1614 mergeAssertionSet( need, r1->need );
1615 mergeAssertionSet( need, r2->need );
1616 mergeAssertionSet( need, r3->need );
1617 ast::AssertionSet have;
1618
1619 // unify true and false results, then infer parameters to produce new
1620 // candidates
1621 ast::ptr< ast::Type > common;
1622 if (
1623 unify(
1624 r2->expr->result, r3->expr->result, env, need, have, open,
1625 common )
1626 ) {
1627 // generate typed expression
1628 ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{
1629 conditionalExpr->location, r1->expr, r2->expr, r3->expr };
1630 newExpr->result = common ? common : r2->expr->result;
1631 // convert both options to result type
1632 Cost cost = r1->cost + r2->cost + r3->cost;
1633 newExpr->arg2 = computeExpressionConversionCost(
1634 newExpr->arg2, newExpr->result, symtab, env, cost );
1635 newExpr->arg3 = computeExpressionConversionCost(
1636 newExpr->arg3, newExpr->result, symtab, env, cost );
1637 // output candidate
1638 CandidateRef newCand = std::make_shared<Candidate>(
1639 newExpr, std::move( env ), std::move( open ), std::move( need ), cost );
1640 inferParameters( newCand, candidates );
1641 }
1642 }
1643 }
1644 }
1645 }
1646
1647 void Finder::postvisit( const ast::CommaExpr * commaExpr ) {
1648 ast::TypeEnvironment env{ tenv };
1649 ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, context, env );
1650
1651 CandidateFinder finder2( context, env );
1652 finder2.find( commaExpr->arg2, ResolveMode::withAdjustment() );
1653
1654 for ( const CandidateRef & r2 : finder2.candidates ) {
1655 addCandidate( *r2, new ast::CommaExpr{ commaExpr->location, arg1, r2->expr } );
1656 }
1657 }
1658
1659 void Finder::postvisit( const ast::ImplicitCopyCtorExpr * ctorExpr ) {
1660 addCandidate( ctorExpr, tenv );
1661 }
1662
1663 void Finder::postvisit( const ast::ConstructorExpr * ctorExpr ) {
1664 CandidateFinder finder( context, tenv );
1665 finder.allowVoid = true;
1666 finder.find( ctorExpr->callExpr, ResolveMode::withoutPrune() );
1667 for ( CandidateRef & r : finder.candidates ) {
1668 addCandidate( *r, new ast::ConstructorExpr{ ctorExpr->location, r->expr } );
1669 }
1670 }
1671
1672 void Finder::postvisit( const ast::RangeExpr * rangeExpr ) {
1673 // resolve low and high, accept candidates where low and high types unify
1674 CandidateFinder finder1( context, tenv );
1675 finder1.find( rangeExpr->low, ResolveMode::withAdjustment() );
1676 if ( finder1.candidates.empty() ) return;
1677
1678 CandidateFinder finder2( context, tenv );
1679 finder2.find( rangeExpr->high, ResolveMode::withAdjustment() );
1680 if ( finder2.candidates.empty() ) return;
1681
1682 reason.code = NoMatch;
1683
1684 for ( const CandidateRef & r1 : finder1.candidates ) {
1685 for ( const CandidateRef & r2 : finder2.candidates ) {
1686 ast::TypeEnvironment env{ r1->env };
1687 env.simpleCombine( r2->env );
1688 ast::OpenVarSet open{ r1->open };
1689 mergeOpenVars( open, r2->open );
1690 ast::AssertionSet need;
1691 mergeAssertionSet( need, r1->need );
1692 mergeAssertionSet( need, r2->need );
1693 ast::AssertionSet have;
1694
1695 ast::ptr< ast::Type > common;
1696 if (
1697 unify(
1698 r1->expr->result, r2->expr->result, env, need, have, open,
1699 common )
1700 ) {
1701 // generate new expression
1702 ast::RangeExpr * newExpr =
1703 new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr };
1704 newExpr->result = common ? common : r1->expr->result;
1705 // add candidate
1706 CandidateRef newCand = std::make_shared<Candidate>(
1707 newExpr, std::move( env ), std::move( open ), std::move( need ),
1708 r1->cost + r2->cost );
1709 inferParameters( newCand, candidates );
1710 }
1711 }
1712 }
1713 }
1714
1715 void Finder::postvisit( const ast::UntypedTupleExpr * tupleExpr ) {
1716 std::vector< CandidateFinder > subCandidates =
1717 selfFinder.findSubExprs( tupleExpr->exprs );
1718 std::vector< CandidateList > possibilities;
1719 combos( subCandidates.begin(), subCandidates.end(), back_inserter( possibilities ) );
1720
1721 for ( const CandidateList & subs : possibilities ) {
1722 std::vector< ast::ptr< ast::Expr > > exprs;
1723 exprs.reserve( subs.size() );
1724 for ( const CandidateRef & sub : subs ) { exprs.emplace_back( sub->expr ); }
1725
1726 ast::TypeEnvironment env;
1727 ast::OpenVarSet open;
1728 ast::AssertionSet need;
1729 for ( const CandidateRef & sub : subs ) {
1730 env.simpleCombine( sub->env );
1731 mergeOpenVars( open, sub->open );
1732 mergeAssertionSet( need, sub->need );
1733 }
1734
1735 addCandidate(
1736 new ast::TupleExpr{ tupleExpr->location, std::move( exprs ) },
1737 std::move( env ), std::move( open ), std::move( need ), sumCost( subs ) );
1738 }
1739 }
1740
1741 void Finder::postvisit( const ast::TupleExpr * tupleExpr ) {
1742 addCandidate( tupleExpr, tenv );
1743 }
1744
1745 void Finder::postvisit( const ast::TupleIndexExpr * tupleExpr ) {
1746 addCandidate( tupleExpr, tenv );
1747 }
1748
1749 void Finder::postvisit( const ast::TupleAssignExpr * tupleExpr ) {
1750 addCandidate( tupleExpr, tenv );
1751 }
1752
1753 void Finder::postvisit( const ast::UniqueExpr * unqExpr ) {
1754 CandidateFinder finder( context, tenv );
1755 finder.find( unqExpr->expr, ResolveMode::withAdjustment() );
1756 for ( CandidateRef & r : finder.candidates ) {
1757 // ensure that the the id is passed on so that the expressions are "linked"
1758 addCandidate( *r, new ast::UniqueExpr{ unqExpr->location, r->expr, unqExpr->id } );
1759 }
1760 }
1761
1762 void Finder::postvisit( const ast::StmtExpr * stmtExpr ) {
1763 addCandidate( resolveStmtExpr( stmtExpr, context ), tenv );
1764 }
1765
1766 void Finder::postvisit( const ast::UntypedInitExpr * initExpr ) {
1767 // handle each option like a cast
1768 CandidateList matches;
1769 PRINT(
1770 std::cerr << "untyped init expr: " << initExpr << std::endl;
1771 )
1772 // O(n^2) checks of d-types with e-types
1773 for ( const ast::InitAlternative & initAlt : initExpr->initAlts ) {
1774 // calculate target type
1775 const ast::Type * toType = resolveTypeof( initAlt.type, context );
1776 toType = adjustExprType( toType, tenv, symtab );
1777 // The call to find must occur inside this loop, otherwise polymorphic return
1778 // types are not bound to the initialization type, since return type variables are
1779 // only open for the duration of resolving the UntypedExpr.
1780 CandidateFinder finder( context, tenv, toType );
1781 finder.find( initExpr->expr, ResolveMode::withAdjustment() );
1782
1783 Cost minExprCost = Cost::infinity;
1784 Cost minCastCost = Cost::infinity;
1785 for ( CandidateRef & cand : finder.candidates ) {
1786 if (reason.code == NotFound) reason.code = NoMatch;
1787
1788 ast::TypeEnvironment env{ cand->env };
1789 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
1790 ast::OpenVarSet open{ cand->open };
1791
1792 PRINT(
1793 std::cerr << " @ " << toType << " " << initAlt.designation << std::endl;
1794 )
1795
1796 // It is possible that a cast can throw away some values in a multiply-valued
1797 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of
1798 // the subexpression results that are cast directly. The candidate is invalid
1799 // if it has fewer results than there are types to cast to.
1800 int discardedValues = cand->expr->result->size() - toType->size();
1801 if ( discardedValues < 0 ) continue;
1802
1803 // unification run for side-effects
1804 ast::ptr<ast::Type> common;
1805 bool canUnify = unify( toType, cand->expr->result, env, need, have, open, common );
1806 (void) canUnify;
1807 Cost thisCost = computeConversionCost( cand->expr->result, toType, cand->expr->get_lvalue(),
1808 symtab, env );
1809 PRINT(
1810 Cost legacyCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(),
1811 symtab, env );
1812 std::cerr << "Considering initialization:";
1813 std::cerr << std::endl << " FROM: " << cand->expr->result << std::endl;
1814 std::cerr << std::endl << " TO: " << toType << std::endl;
1815 std::cerr << std::endl << " Unification " << (canUnify ? "succeeded" : "failed");
1816 std::cerr << std::endl << " Legacy cost " << legacyCost;
1817 std::cerr << std::endl << " New cost " << thisCost;
1818 std::cerr << std::endl;
1819 )
1820 if ( thisCost != Cost::infinity ) {
1821 // count one safe conversion for each value that is thrown away
1822 thisCost.incSafe( discardedValues );
1823 if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
1824 minExprCost = cand->cost;
1825 minCastCost = thisCost;
1826 matches.clear();
1827 }
1828 CandidateRef newCand = std::make_shared<Candidate>(
1829 new ast::InitExpr{
1830 initExpr->location,
1831 restructureCast( cand->expr, toType ),
1832 initAlt.designation },
1833 std::move(env), std::move( open ), std::move( need ), cand->cost + thisCost );
1834 // currently assertions are always resolved immediately so this should have no effect.
1835 // if this somehow changes in the future (e.g. delayed by indeterminate return type)
1836 // we may need to revisit the logic.
1837 inferParameters( newCand, matches );
1838 }
1839 }
1840 }
1841
1842 // select first on argument cost, then conversion cost
1843 // CandidateList minArgCost = findMinCost( matches );
1844 // promoteCvtCost( minArgCost );
1845 // candidates = findMinCost( minArgCost );
1846 candidates = std::move(matches);
1847 }
1848
1849 void Finder::postvisit( const ast::QualifiedNameExpr * expr ) {
1850 std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( expr->name );
1851 if ( declList.empty() ) return;
1852
1853 for ( ast::SymbolTable::IdData & data: declList ) {
1854 const ast::Type * t = data.id->get_type()->stripReferences();
1855 if ( const ast::EnumInstType * enumInstType =
1856 dynamic_cast<const ast::EnumInstType *>( t ) ) {
1857 if ( (enumInstType->base->name == expr->type_name)
1858 || (expr->type_decl && enumInstType->base->name == expr->type_decl->name) ) {
1859 Cost cost = Cost::zero;
1860 ast::Expr * newExpr = data.combine( expr->location, cost );
1861 CandidateRef newCand =
1862 std::make_shared<Candidate>(
1863 newExpr, copy( tenv ), ast::OpenVarSet{},
1864 ast::AssertionSet{}, Cost::zero, cost
1865 );
1866 if (newCand->expr->env) {
1867 newCand->env.add(*newCand->expr->env);
1868 auto mutExpr = newCand->expr.get_and_mutate();
1869 mutExpr->env = nullptr;
1870 newCand->expr = mutExpr;
1871 }
1872
1873 newCand->expr = ast::mutate_field(
1874 newCand->expr.get(), &ast::Expr::result,
1875 renameTyVars( newCand->expr->result ) );
1876 addAnonConversions( newCand );
1877 candidates.emplace_back( std::move( newCand ) );
1878 }
1879 }
1880 }
1881 }
1882 // size_t Finder::traceId = Stats::Heap::new_stacktrace_id("Finder");
1883 /// Prunes a list of candidates down to those that have the minimum conversion cost for a given
1884 /// return type. Skips ambiguous candidates.
1885
1886} // anonymous namespace
1887
1888bool CandidateFinder::pruneCandidates( CandidateList & candidates, CandidateList & out, std::vector<std::string> & errors ) {
1889 struct PruneStruct {
1890 CandidateRef candidate;
1891 bool ambiguous;
1892
1893 PruneStruct() = default;
1894 PruneStruct( const CandidateRef & c ) : candidate( c ), ambiguous( false ) {}
1895 };
1896
1897 // find lowest-cost candidate for each type
1898 std::unordered_map< std::string, PruneStruct > selected;
1899 // attempt to skip satisfyAssertions on more expensive alternatives if better options have been found
1900 std::sort(candidates.begin(), candidates.end(), [](const CandidateRef & x, const CandidateRef & y){return x->cost < y->cost;});
1901 for ( CandidateRef & candidate : candidates ) {
1902 std::string mangleName;
1903 {
1904 ast::ptr< ast::Type > newType = candidate->expr->result;
1905 assertf(candidate->expr->result, "Result of expression %p for candidate is null", candidate->expr.get());
1906 candidate->env.apply( newType );
1907 mangleName = Mangle::mangle( newType );
1908 }
1909
1910 auto found = selected.find( mangleName );
1911 if (found != selected.end() && found->second.candidate->cost < candidate->cost) {
1912 PRINT(
1913 std::cerr << "cost " << candidate->cost << " loses to "
1914 << found->second.candidate->cost << std::endl;
1915 )
1916 continue;
1917 }
1918
1919 // xxx - when do satisfyAssertions produce more than 1 result?
1920 // this should only happen when initial result type contains
1921 // unbound type parameters, then it should never be pruned by
1922 // the previous step, since renameTyVars guarantees the mangled name
1923 // is unique.
1924 CandidateList satisfied;
1925 bool needRecomputeKey = false;
1926 if (candidate->need.empty()) {
1927 satisfied.emplace_back(candidate);
1928 }
1929 else {
1930 satisfyAssertions(candidate, context.symtab, satisfied, errors);
1931 needRecomputeKey = true;
1932 }
1933
1934 for (auto & newCand : satisfied) {
1935 // recomputes type key, if satisfyAssertions changed it
1936 if (needRecomputeKey)
1937 {
1938 ast::ptr< ast::Type > newType = newCand->expr->result;
1939 assertf(newCand->expr->result, "Result of expression %p for candidate is null", newCand->expr.get());
1940 newCand->env.apply( newType );
1941 mangleName = Mangle::mangle( newType );
1942 }
1943 auto found = selected.find( mangleName );
1944 if ( found != selected.end() ) {
1945 // tiebreaking by picking the lower cost on CURRENT expression
1946 // NOTE: this behavior is different from C semantics.
1947 // Specific remediations are performed for C operators at postvisit(UntypedExpr).
1948 // Further investigations may take place.
1949 if ( newCand->cost < found->second.candidate->cost
1950 || (newCand->cost == found->second.candidate->cost && newCand->cvtCost < found->second.candidate->cvtCost) ) {
1951 PRINT(
1952 std::cerr << "cost " << newCand->cost << " beats "
1953 << found->second.candidate->cost << std::endl;
1954 )
1955
1956 found->second = PruneStruct{ newCand };
1957 } else if ( newCand->cost == found->second.candidate->cost && newCand->cvtCost == found->second.candidate->cvtCost ) {
1958 // if one of the candidates contains a deleted identifier, can pick the other,
1959 // since deleted expressions should not be ambiguous if there is another option
1960 // that is at least as good
1961 if ( findDeletedExpr( newCand->expr ) ) {
1962 // do nothing
1963 PRINT( std::cerr << "candidate is deleted" << std::endl; )
1964 } else if ( findDeletedExpr( found->second.candidate->expr ) ) {
1965 PRINT( std::cerr << "current is deleted" << std::endl; )
1966 found->second = PruneStruct{ newCand };
1967 } else {
1968 PRINT( std::cerr << "marking ambiguous" << std::endl; )
1969 found->second.ambiguous = true;
1970 }
1971 } else {
1972 // xxx - can satisfyAssertions increase the cost?
1973 PRINT(
1974 std::cerr << "cost " << newCand->cost << " loses to "
1975 << found->second.candidate->cost << std::endl;
1976 )
1977 }
1978 } else {
1979 selected.emplace_hint( found, mangleName, newCand );
1980 }
1981 }
1982 }
1983
1984 // report unambiguous min-cost candidates
1985 // CandidateList out;
1986 for ( auto & target : selected ) {
1987 if ( target.second.ambiguous ) continue;
1988
1989 CandidateRef cand = target.second.candidate;
1990
1991 ast::ptr< ast::Type > newResult = cand->expr->result;
1992 cand->env.applyFree( newResult );
1993 cand->expr = ast::mutate_field(
1994 cand->expr.get(), &ast::Expr::result, std::move( newResult ) );
1995
1996 out.emplace_back( cand );
1997 }
1998 // if everything is lost in satisfyAssertions, report the error
1999 return !selected.empty();
2000}
2001
2002void CandidateFinder::find( const ast::Expr * expr, ResolveMode mode ) {
2003 // Find alternatives for expression
2004 ast::Pass<Finder> finder{ *this };
2005 expr->accept( finder );
2006
2007 if ( mode.failFast && candidates.empty() ) {
2008 switch(finder.core.reason.code) {
2009 case Finder::NotFound:
2010 { SemanticError( expr, "No alternatives for expression " ); break; }
2011 case Finder::NoMatch:
2012 { SemanticError( expr, "Invalid application of existing declaration(s) in expression " ); break; }
2013 case Finder::ArgsToFew:
2014 case Finder::ArgsToMany:
2015 case Finder::RetsToFew:
2016 case Finder::RetsToMany:
2017 case Finder::NoReason:
2018 default:
2019 { SemanticError( expr->location, "No reasonable alternatives for expression : reasons unkown" ); }
2020 }
2021 }
2022
2023 /*
2024 if ( mode.satisfyAssns || mode.prune ) {
2025 // trim candidates to just those where the assertions are satisfiable
2026 // - necessary pre-requisite to pruning
2027 CandidateList satisfied;
2028 std::vector< std::string > errors;
2029 for ( CandidateRef & candidate : candidates ) {
2030 satisfyAssertions( candidate, localSyms, satisfied, errors );
2031 }
2032
2033 // fail early if none such
2034 if ( mode.failFast && satisfied.empty() ) {
2035 std::ostringstream stream;
2036 stream << "No alternatives with satisfiable assertions for " << expr << "\n";
2037 for ( const auto& err : errors ) {
2038 stream << err;
2039 }
2040 SemanticError( expr->location, stream.str() );
2041 }
2042
2043 // reset candidates
2044 candidates = move( satisfied );
2045 }
2046 */
2047
2048 // optimization: don't prune for NameExpr since it never has cost
2049 if ( mode.prune && !dynamic_cast<const ast::NameExpr *>(expr) ) {
2050 // trim candidates to single best one
2051 PRINT(
2052 std::cerr << "alternatives before prune:" << std::endl;
2053 print( std::cerr, candidates );
2054 )
2055
2056 CandidateList pruned;
2057 std::vector<std::string> errors;
2058 bool found = pruneCandidates( candidates, pruned, errors );
2059
2060 if ( mode.failFast && pruned.empty() ) {
2061 std::ostringstream stream;
2062 if (found) {
2063 CandidateList winners = findMinCost( candidates );
2064 stream << "Cannot choose between " << winners.size() << " alternatives for "
2065 "expression\n";
2066 ast::print( stream, expr );
2067 stream << " Alternatives are:\n";
2068 print( stream, winners, 1 );
2069 SemanticError( expr->location, stream.str() );
2070 }
2071 else {
2072 stream << "No alternatives with satisfiable assertions for " << expr << "\n";
2073 for ( const auto& err : errors ) {
2074 stream << err;
2075 }
2076 SemanticError( expr->location, stream.str() );
2077 }
2078 }
2079
2080 auto oldsize = candidates.size();
2081 candidates = std::move( pruned );
2082
2083 PRINT(
2084 std::cerr << "there are " << oldsize << " alternatives before elimination" << std::endl;
2085 )
2086 PRINT(
2087 std::cerr << "there are " << candidates.size() << " alternatives after elimination"
2088 << std::endl;
2089 )
2090 }
2091
2092 // adjust types after pruning so that types substituted by pruneAlternatives are correctly
2093 // adjusted
2094 if ( mode.adjust ) {
2095 for ( CandidateRef & r : candidates ) {
2096 r->expr = ast::mutate_field(
2097 r->expr.get(), &ast::Expr::result,
2098 adjustExprType( r->expr->result, r->env, context.symtab ) );
2099 }
2100 }
2101
2102 // Central location to handle gcc extension keyword, etc. for all expressions
2103 for ( CandidateRef & r : candidates ) {
2104 if ( r->expr->extension != expr->extension ) {
2105 r->expr.get_and_mutate()->extension = expr->extension;
2106 }
2107 }
2108}
2109
2110std::vector< CandidateFinder > CandidateFinder::findSubExprs(
2111 const std::vector< ast::ptr< ast::Expr > > & xs
2112) {
2113 std::vector< CandidateFinder > out;
2114
2115 for ( const auto & x : xs ) {
2116 out.emplace_back( context, env );
2117 out.back().find( x, ResolveMode::withAdjustment() );
2118
2119 PRINT(
2120 std::cerr << "findSubExprs" << std::endl;
2121 print( std::cerr, out.back().candidates );
2122 )
2123 }
2124
2125 return out;
2126}
2127
2128const ast::Expr * referenceToRvalueConversion( const ast::Expr * expr, Cost & cost ) {
2129 if ( expr->result.as< ast::ReferenceType >() ) {
2130 // cast away reference from expr
2131 cost.incReference();
2132 return new ast::CastExpr{ expr, expr->result->stripReferences() };
2133 }
2134
2135 return expr;
2136}
2137
2138/// If the target enum is a child, get the offset from the base to the target.
2139static unsigned findChildOffset(
2140 const ast::EnumDecl * decl, const ast::EnumDecl * target ) {
2141 unsigned offset = 0;
2142 for ( auto inlined : decl->inlinedDecl ) {
2143 auto childDecl = inlined->base;
2144 if ( childDecl == target ) {
2145 return offset;
2146 }
2147 offset += childDecl->members.size();
2148 }
2149 SemanticError( decl, "Cannot find the target enum." );
2150}
2151
2152const ast::Expr * CandidateFinder::makeEnumOffsetCast( const ast::EnumInstType * src,
2153 const ast::EnumInstType * dst, const ast::Expr * expr, Cost minCost ) {
2154 auto srcDecl = src->base;
2155 auto dstDecl = dst->base;
2156
2157 if (srcDecl->name == dstDecl->name) return expr;
2158
2159 for (auto& dstChild: dstDecl->inlinedDecl) {
2160 Cost c = castCost(src, dstChild, false, context.symtab, env);
2161 ast::CastExpr * castToDst;
2162 if (c<minCost) {
2163 unsigned offset = findChildOffset( dstDecl, dstChild.get()->base );
2164 if (offset > 0) {
2165 auto untyped = ast::UntypedExpr::createCall(
2166 expr->location,
2167 "?+?",
2168 { new ast::CastExpr( expr->location,
2169 expr,
2170 new ast::BasicType(ast::BasicKind::SignedInt),
2171 ast::GeneratedFlag::ExplicitCast ),
2172 ast::ConstantExpr::from_int(expr->location, offset)} );
2173 CandidateFinder finder(context, env);
2174 finder.find( untyped );
2175 CandidateList winners = findMinCost( finder.candidates );
2176 CandidateRef & choice = winners.front();
2177 choice->expr = new ast::CastExpr(expr->location, choice->expr, dstChild, ast::GeneratedFlag::ExplicitCast);
2178 auto destExpr = makeEnumOffsetCast( src, dstChild, choice->expr, minCost );
2179 if ( !destExpr ) continue;
2180 castToDst = new ast::CastExpr( destExpr, dst );
2181 } else {
2182 castToDst = new ast::CastExpr( expr, dst );
2183 }
2184 return castToDst;
2185 }
2186 }
2187 return nullptr;
2188}
2189
2190Cost computeConversionCost(
2191 const ast::Type * argType, const ast::Type * paramType, bool argIsLvalue,
2192 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
2193) {
2194 PRINT(
2195 std::cerr << std::endl << "converting ";
2196 ast::print( std::cerr, argType, 2 );
2197 std::cerr << std::endl << " to ";
2198 ast::print( std::cerr, paramType, 2 );
2199 std::cerr << std::endl << "environment is: ";
2200 ast::print( std::cerr, env, 2 );
2201 std::cerr << std::endl;
2202 )
2203 Cost convCost = conversionCost( argType, paramType, argIsLvalue, symtab, env );
2204 PRINT(
2205 std::cerr << std::endl << "cost is " << convCost << std::endl;
2206 )
2207 if ( convCost == Cost::infinity ) return convCost;
2208 convCost.incPoly( polyCost( paramType, symtab, env ) + polyCost( argType, symtab, env ) );
2209 PRINT(
2210 std::cerr << "cost with polycost is " << convCost << std::endl;
2211 )
2212 return convCost;
2213}
2214
2215const ast::Expr * createCondExpr( const ast::Expr * expr ) {
2216 assert( expr );
2217 return new ast::CastExpr( expr->location,
2218 ast::UntypedExpr::createCall( expr->location,
2219 "?!=?",
2220 {
2221 expr,
2222 new ast::ConstantExpr( expr->location,
2223 new ast::ZeroType(), "0", std::make_optional( 0ull )
2224 ),
2225 }
2226 ),
2227 new ast::BasicType( ast::BasicKind::SignedInt )
2228 );
2229}
2230
2231} // namespace ResolvExpr
2232
2233// Local Variables: //
2234// tab-width: 4 //
2235// mode: c++ //
2236// compile-command: "make install" //
2237// End: //
Note: See TracBrowser for help on using the repository browser.