source: src/ResolvExpr/CandidateFinder.cpp@ 7c80a86

Last change on this file since 7c80a86 was 90be0cf, checked in by Andrew Beach <ajbeach@…>, 11 months ago

Moved some methods out of EnumDecl. These were calculations and the ast types are supposed to be data focused so they should either go with utility functions or to their use site. I tried the use site.

  • Property mode set to 100644
File size: 80.8 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::AddressExpr * addressExpr );
675 void postvisit( const ast::LabelAddressExpr * labelExpr );
676 void postvisit( const ast::CastExpr * castExpr );
677 void postvisit( const ast::VirtualCastExpr * castExpr );
678 void postvisit( const ast::KeywordCastExpr * castExpr );
679 void postvisit( const ast::UntypedMemberExpr * memberExpr );
680 void postvisit( const ast::MemberExpr * memberExpr );
681 void postvisit( const ast::NameExpr * nameExpr );
682 void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr );
683 void postvisit( const ast::OffsetofExpr * offsetofExpr );
684 void postvisit( const ast::OffsetPackExpr * offsetPackExpr );
685 void postvisit( const ast::LogicalExpr * logicalExpr );
686 void postvisit( const ast::ConditionalExpr * conditionalExpr );
687 void postvisit( const ast::CommaExpr * commaExpr );
688 void postvisit( const ast::ImplicitCopyCtorExpr * ctorExpr );
689 void postvisit( const ast::ConstructorExpr * ctorExpr );
690 void postvisit( const ast::RangeExpr * rangeExpr );
691 void postvisit( const ast::UntypedTupleExpr * tupleExpr );
692 void postvisit( const ast::TupleExpr * tupleExpr );
693 void postvisit( const ast::TupleIndexExpr * tupleExpr );
694 void postvisit( const ast::TupleAssignExpr * tupleExpr );
695 void postvisit( const ast::UniqueExpr * unqExpr );
696 void postvisit( const ast::StmtExpr * stmtExpr );
697 void postvisit( const ast::UntypedInitExpr * initExpr );
698 void postvisit( const ast::QualifiedNameExpr * qualifiedExpr );
699 void postvisit( const ast::CountExpr * countExpr );
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
945 /// Adds aggregate member interpretations
946 void Finder::addAggMembers(
947 const ast::BaseInstType * aggrInst, const ast::Expr * expr,
948 const Candidate & cand, const Cost & addedCost, const std::string & name
949 ) {
950 for ( const ast::Decl * decl : aggrInst->lookup( name ) ) {
951 auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl );
952 CandidateRef newCand = std::make_shared<Candidate>(
953 cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
954 // add anonymous member interpretations whenever an aggregate value type is seen
955 // as a member expression
956 addAnonConversions( newCand );
957 candidates.emplace_back( std::move( newCand ) );
958 }
959 }
960
961 /// Adds tuple member interpretations
962 void Finder::addTupleMembers(
963 const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand,
964 const Cost & addedCost, const ast::Expr * member
965 ) {
966 if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) {
967 // get the value of the constant expression as an int, must be between 0 and the
968 // length of the tuple to have meaning
969 long long val = constantExpr->intValue();
970 if ( val >= 0 && (unsigned long long)val < tupleType->size() ) {
971 addCandidate(
972 cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val },
973 addedCost );
974 }
975 }
976 }
977
978 void Finder::postvisit( const ast::UntypedExpr * untypedExpr ) {
979 std::vector< CandidateFinder > argCandidates =
980 selfFinder.findSubExprs( untypedExpr->args );
981
982 // take care of possible tuple assignments
983 // if not tuple assignment, handled as normal function call
984 Tuples::handleTupleAssignment( selfFinder, untypedExpr, argCandidates );
985
986 CandidateFinder funcFinder( context, tenv );
987 std::string funcName;
988 if (auto nameExpr = untypedExpr->func.as<ast::NameExpr>()) {
989 funcName = nameExpr->name;
990 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
991 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
992 assertf(!argCandidates.empty(), "special function call without argument");
993 for (auto & firstArgCand: argCandidates[0]) {
994 ast::ptr<ast::Type> argType = firstArgCand->expr->result;
995 firstArgCand->env.apply(argType);
996 // strip references
997 // xxx - is this correct?
998 while (argType.as<ast::ReferenceType>()) argType = argType.as<ast::ReferenceType>()->base;
999
1000 // convert 1-tuple to plain type
1001 if (auto tuple = argType.as<ast::TupleType>()) {
1002 if (tuple->size() == 1) {
1003 argType = tuple->types[0];
1004 }
1005 }
1006
1007 // if argType is an unbound type parameter, all special functions need to be searched.
1008 if (isUnboundType(argType)) {
1009 funcFinder.otypeKeys.clear();
1010 break;
1011 }
1012
1013 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);
1014 else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));
1015 }
1016 }
1017 }
1018 // if candidates are already produced, do not fail
1019 // xxx - is it possible that handleTupleAssignment and main finder both produce candidates?
1020 // this means there exists ctor/assign functions with a tuple as first parameter.
1021 ResolveMode mode = {
1022 true, // adjust
1023 !untypedExpr->func.as<ast::NameExpr>(), // prune if not calling by name
1024 selfFinder.candidates.empty() // failfast if other options are not found
1025 };
1026 funcFinder.find( untypedExpr->func, mode );
1027 // short-circuit if no candidates
1028 // if ( funcFinder.candidates.empty() ) return;
1029
1030 reason.code = NoMatch;
1031
1032 // find function operators
1033 ast::ptr< ast::Expr > opExpr = new ast::NameExpr{ untypedExpr->location, "?()" }; // ??? why not ?{}
1034 CandidateFinder opFinder( context, tenv );
1035 // okay if there aren't any function operations
1036 opFinder.find( opExpr, ResolveMode::withoutFailFast() );
1037 PRINT(
1038 std::cerr << "known function ops:" << std::endl;
1039 print( std::cerr, opFinder.candidates, 1 );
1040 )
1041
1042 // pre-explode arguments
1043 ExplodedArgs argExpansions;
1044 for ( const CandidateFinder & args : argCandidates ) {
1045 argExpansions.emplace_back();
1046 auto & argE = argExpansions.back();
1047 for ( const CandidateRef & arg : args ) { argE.emplace_back( *arg, symtab ); }
1048 }
1049
1050 // Find function matches
1051 CandidateList found;
1052 SemanticErrorException errors;
1053
1054 for ( CandidateRef & func : funcFinder ) {
1055 try {
1056 PRINT(
1057 std::cerr << "working on alternative:" << std::endl;
1058 print( std::cerr, *func, 2 );
1059 )
1060
1061 // check if the type is a pointer to function
1062 const ast::Type * funcResult = func->expr->result->stripReferences();
1063 if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) {
1064 if ( auto function = pointer->base.as< ast::FunctionType >() ) {
1065 // if (!selfFinder.allowVoid && function->returns.empty()) continue;
1066 CandidateRef newFunc{ new Candidate{ *func } };
1067 newFunc->expr =
1068 referenceToRvalueConversion( newFunc->expr, newFunc->cost );
1069 makeFunctionCandidates( untypedExpr->location,
1070 newFunc, function, argExpansions, found );
1071 }
1072 } else if (
1073 auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
1074 ) {
1075 if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
1076 if ( auto function = clz->bound.as< ast::FunctionType >() ) {
1077 CandidateRef newFunc( new Candidate( *func ) );
1078 newFunc->expr =
1079 referenceToRvalueConversion( newFunc->expr, newFunc->cost );
1080 makeFunctionCandidates( untypedExpr->location,
1081 newFunc, function, argExpansions, found );
1082 }
1083 }
1084 }
1085 } catch ( SemanticErrorException & e ) { errors.append( e ); }
1086 }
1087
1088 // Find matches on function operators `?()`
1089 if ( ! opFinder.candidates.empty() ) {
1090 // add exploded function alternatives to front of argument list
1091 std::vector< ExplodedArg > funcE;
1092 funcE.reserve( funcFinder.candidates.size() );
1093 for ( const CandidateRef & func : funcFinder ) {
1094 funcE.emplace_back( *func, symtab );
1095 }
1096 argExpansions.emplace_front( std::move( funcE ) );
1097
1098 for ( const CandidateRef & op : opFinder ) {
1099 try {
1100 // check if type is pointer-to-function
1101 const ast::Type * opResult = op->expr->result->stripReferences();
1102 if ( auto pointer = dynamic_cast< const ast::PointerType * >( opResult ) ) {
1103 if ( auto function = pointer->base.as< ast::FunctionType >() ) {
1104 CandidateRef newOp{ new Candidate{ *op} };
1105 newOp->expr =
1106 referenceToRvalueConversion( newOp->expr, newOp->cost );
1107 makeFunctionCandidates( untypedExpr->location,
1108 newOp, function, argExpansions, found );
1109 }
1110 }
1111 } catch ( SemanticErrorException & e ) { errors.append( e ); }
1112 }
1113 }
1114
1115 // Implement SFINAE; resolution errors are only errors if there aren't any non-error
1116 // candidates
1117 if ( found.empty() ) errors.throwIfNonEmpty();
1118
1119 // only keep the best matching intrinsic result to match C semantics (no unexpected narrowing/widening)
1120 // TODO: keep one for each set of argument candidates?
1121 Cost intrinsicCost = Cost::infinity;
1122 CandidateList intrinsicResult;
1123
1124 // Compute conversion costs
1125 for ( CandidateRef & withFunc : found ) {
1126 Cost cvtCost = computeApplicationConversionCost( withFunc, symtab );
1127
1128 if (funcName == "?|?") {
1129 PRINT(
1130 auto appExpr = withFunc->expr.strict_as< ast::ApplicationExpr >();
1131 auto pointer = appExpr->func->result.strict_as< ast::PointerType >();
1132 auto function = pointer->base.strict_as< ast::FunctionType >();
1133
1134 std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl;
1135 std::cerr << "parameters are:" << std::endl;
1136 ast::printAll( std::cerr, function->params, 2 );
1137 std::cerr << "arguments are:" << std::endl;
1138 ast::printAll( std::cerr, appExpr->args, 2 );
1139 std::cerr << "bindings are:" << std::endl;
1140 ast::print( std::cerr, withFunc->env, 2 );
1141 std::cerr << "cost is: " << withFunc->cost << std::endl;
1142 std::cerr << "cost of conversion is:" << cvtCost << std::endl;
1143 )
1144 }
1145 if ( cvtCost != Cost::infinity ) {
1146 withFunc->cvtCost = cvtCost;
1147 withFunc->cost += cvtCost;
1148 auto func = withFunc->expr.strict_as<ast::ApplicationExpr>()->func.as<ast::VariableExpr>();
1149 if (func && func->var->linkage == ast::Linkage::Intrinsic) {
1150 if (withFunc->cost < intrinsicCost) {
1151 intrinsicResult.clear();
1152 intrinsicCost = withFunc->cost;
1153 }
1154 if (withFunc->cost == intrinsicCost) {
1155 intrinsicResult.emplace_back(std::move(withFunc));
1156 }
1157 } else {
1158 candidates.emplace_back( std::move( withFunc ) );
1159 }
1160 }
1161 }
1162 spliceBegin( candidates, intrinsicResult );
1163 found = std::move( candidates );
1164
1165 // use a new list so that candidates are not examined by addAnonConversions twice
1166 // CandidateList winners = findMinCost( found );
1167 // promoteCvtCost( winners );
1168
1169 // function may return a struct/union value, in which case we need to add candidates
1170 // for implicit conversions to each of the anonymous members, which must happen after
1171 // `findMinCost`, since anon conversions are never the cheapest
1172 for ( const CandidateRef & c : found ) {
1173 addAnonConversions( c );
1174 }
1175 // would this be too slow when we don't check cost anymore?
1176 spliceBegin( candidates, found );
1177
1178 if ( candidates.empty() && targetType && ! targetType->isVoid() && !selfFinder.strictMode ) {
1179 // If resolution is unsuccessful with a target type, try again without, since it
1180 // will sometimes succeed when it wouldn't with a target type binding.
1181 // For example:
1182 // forall( otype T ) T & ?[]( T *, ptrdiff_t );
1183 // const char * x = "hello world";
1184 // unsigned char ch = x[0];
1185 // Fails with simple return type binding (xxx -- check this!) as follows:
1186 // * T is bound to unsigned char
1187 // * (x: const char *) is unified with unsigned char *, which fails
1188 // xxx -- fix this better
1189 targetType = nullptr;
1190 postvisit( untypedExpr );
1191 }
1192 }
1193
1194 void Finder::postvisit( const ast::AddressExpr * addressExpr ) {
1195 CandidateFinder finder( context, tenv );
1196 finder.find( addressExpr->arg );
1197
1198 if ( finder.candidates.empty() ) return;
1199
1200 reason.code = NoMatch;
1201
1202 for ( CandidateRef & r : finder.candidates ) {
1203 if ( !isLvalue( r->expr ) ) continue;
1204 addCandidate( *r, new ast::AddressExpr{ addressExpr->location, r->expr } );
1205 }
1206 }
1207
1208 void Finder::postvisit( const ast::LabelAddressExpr * labelExpr ) {
1209 addCandidate( labelExpr, tenv );
1210 }
1211
1212 void Finder::postvisit( const ast::CastExpr * castExpr ) {
1213 ast::ptr< ast::Type > toType = castExpr->result;
1214 assert( toType );
1215 toType = resolveTypeof( toType, context );
1216 toType = adjustExprType( toType, tenv, symtab );
1217
1218 CandidateFinder finder( context, tenv, toType );
1219 if (toType->isVoid()) {
1220 finder.allowVoid = true;
1221 }
1222 if ( castExpr->kind == ast::CastExpr::Return ) {
1223 finder.strictMode = true;
1224 finder.find( castExpr->arg, ResolveMode::withAdjustment() );
1225
1226 // return casts are eliminated (merely selecting an overload, no actual operation)
1227 candidates = std::move(finder.candidates);
1228 return;
1229 }
1230 else if (toType->isVoid()) {
1231 finder.find( castExpr->arg ); // no adjust
1232 }
1233 else {
1234 finder.find( castExpr->arg, ResolveMode::withAdjustment() );
1235 }
1236
1237 if ( !finder.candidates.empty() ) reason.code = NoMatch;
1238
1239 CandidateList matches;
1240 Cost minExprCost = Cost::infinity;
1241 // Cost minCastCost = Cost::infinity;
1242 for ( CandidateRef & cand : finder.candidates ) {
1243 ast::ptr< ast::Type > fromType = cand->expr->result;
1244 assert( fromType );
1245 fromType = resolveTypeof( fromType, context );
1246 fromType = adjustExprType( fromType, tenv, symtab );
1247
1248 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
1249 ast::OpenVarSet open( cand->open );
1250
1251 cand->env.extractOpenVars( open );
1252
1253 // It is possible that a cast can throw away some values in a multiply-valued
1254 // expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the
1255 // subexpression results that are cast directly. The candidate is invalid if it
1256 // has fewer results than there are types to cast to.
1257 int discardedValues = fromType->size() - toType->size();
1258 if ( discardedValues < 0 ) continue;
1259
1260 // unification run for side-effects
1261 unify( toType, fromType, cand->env, need, have, open );
1262 Cost thisCost =
1263 (castExpr->isGenerated == ast::GeneratedFlag::GeneratedCast)
1264 ? conversionCost( fromType, toType, cand->expr->get_lvalue(), symtab, cand->env )
1265 : castCost( fromType, toType, cand->expr->get_lvalue(), symtab, cand->env );
1266
1267 // Redefine enum cast
1268 auto argAsEnum = fromType.as<ast::EnumInstType>();
1269 auto toAsEnum = toType.as<ast::EnumInstType>();
1270 if ( argAsEnum && toAsEnum && argAsEnum->name != toAsEnum->name ) {
1271 CandidateFinder subFinder(context, tenv);
1272 ast::ptr<ast::Expr> offsetExpr = subFinder.makeEnumOffsetCast(argAsEnum, toAsEnum, cand->expr, thisCost);
1273 if ( offsetExpr )
1274 cand->expr = offsetExpr;
1275 }
1276
1277 PRINT(
1278 std::cerr << "working on cast with result: " << toType << std::endl;
1279 std::cerr << "and expr type: " << fromType << std::endl;
1280 std::cerr << "env: " << cand->env << std::endl;
1281 )
1282 if ( thisCost != Cost::infinity ) {
1283 PRINT(
1284 std::cerr << "has finite cost." << std::endl;
1285 )
1286 // count one safe conversion for each value that is thrown away
1287 thisCost.incSafe( discardedValues );
1288
1289 // See Aaron Moss, page 47; this reasoning does not hold since implicit conversions
1290 // can create the same resolution issue. The C intrinsic interpretations are pruned
1291 // immediately for the lowest cost option regardless of result type. Related code in
1292 // postvisit (UntypedExpr).
1293 // Cast expression costs are updated now to use the general rules.
1294 /*
1295 // select first on argument cost, then conversion cost
1296 if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
1297 minExprCost = cand->cost;
1298 minCastCost = thisCost;
1299 matches.clear();
1300 }
1301 // ambigious case, still output candidates to print in error message
1302 if ( cand->cost == minExprCost && thisCost == minCastCost ) {
1303 */
1304 cand->cost += thisCost;
1305 if (cand->cost < minExprCost) {
1306 minExprCost = cand->cost;
1307 matches.clear();
1308 }
1309 if (cand->cost == minExprCost) {
1310 CandidateRef newCand = std::make_shared<Candidate>(
1311 restructureCast( cand->expr, toType, castExpr->isGenerated ),
1312 copy( cand->env ), std::move( open ), std::move( need ), cand->cost);
1313 // currently assertions are always resolved immediately so this should have no effect.
1314 // if this somehow changes in the future (e.g. delayed by indeterminate return type)
1315 // we may need to revisit the logic.
1316 inferParameters( newCand, matches );
1317 }
1318 // else skip, better alternatives found
1319
1320 }
1321 }
1322 candidates = std::move(matches);
1323 //CandidateList minArgCost = findMinCost( matches );
1324 //promoteCvtCost( minArgCost );
1325 //candidates = findMinCost( minArgCost );
1326 }
1327
1328 void Finder::postvisit( const ast::VirtualCastExpr * castExpr ) {
1329 assertf( castExpr->result, "Implicit virtual cast targets not yet supported." );
1330 CandidateFinder finder( context, tenv );
1331 // don't prune here, all alternatives guaranteed to have same type
1332 finder.find( castExpr->arg, ResolveMode::withoutPrune() );
1333 for ( CandidateRef & r : finder.candidates ) {
1334 addCandidate(
1335 *r,
1336 new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } );
1337 }
1338 }
1339
1340 void Finder::postvisit( const ast::KeywordCastExpr * castExpr ) {
1341 const auto & loc = castExpr->location;
1342 assertf( castExpr->result, "Cast target should have been set in Validate." );
1343 auto ref = castExpr->result.strict_as<ast::ReferenceType>();
1344 auto inst = ref->base.strict_as<ast::StructInstType>();
1345 auto target = inst->base.get();
1346
1347 CandidateFinder finder( context, tenv );
1348
1349 auto pick_alternatives = [target, this](CandidateList & found, bool expect_ref) {
1350 for (auto & cand : found) {
1351 const ast::Type * expr = cand->expr->result.get();
1352 if (expect_ref) {
1353 auto res = dynamic_cast<const ast::ReferenceType*>(expr);
1354 if (!res) { continue; }
1355 expr = res->base.get();
1356 }
1357
1358 if (auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {
1359 auto td = cand->env.lookup(*insttype);
1360 if (!td) { continue; }
1361 expr = td->bound.get();
1362 }
1363
1364 if (auto base = dynamic_cast<const ast::StructInstType*>(expr)) {
1365 if (base->base == target) {
1366 candidates.push_back( std::move(cand) );
1367 reason.code = NoReason;
1368 }
1369 }
1370 }
1371 };
1372
1373 try {
1374 // Attempt 1 : turn (thread&)X into (thread$&)X.__thrd
1375 // Clone is purely for memory management
1376 std::unique_ptr<const ast::Expr> tech1 { new ast::UntypedMemberExpr(loc, new ast::NameExpr(loc, castExpr->concrete_target.field), castExpr->arg) };
1377
1378 // don't prune here, since it's guaranteed all alternatives will have the same type
1379 finder.find( tech1.get(), ResolveMode::withoutPrune() );
1380 pick_alternatives(finder.candidates, false);
1381
1382 return;
1383 } catch(SemanticErrorException & ) {}
1384
1385 // Fallback : turn (thread&)X into (thread$&)get_thread(X)
1386 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 })) };
1387 // don't prune here, since it's guaranteed all alternatives will have the same type
1388 finder.find( fallback.get(), ResolveMode::withoutPrune() );
1389
1390 pick_alternatives(finder.candidates, true);
1391
1392 // Whatever happens here, we have no more fallbacks
1393 }
1394
1395 void Finder::postvisit( const ast::UntypedMemberExpr * memberExpr ) {
1396 CandidateFinder aggFinder( context, tenv );
1397 aggFinder.find( memberExpr->aggregate, ResolveMode::withAdjustment() );
1398 for ( CandidateRef & agg : aggFinder.candidates ) {
1399 // it's okay for the aggregate expression to have reference type -- cast it to the
1400 // base type to treat the aggregate as the referenced value
1401 Cost addedCost = Cost::zero;
1402 agg->expr = referenceToRvalueConversion( agg->expr, addedCost );
1403
1404 // find member of the given type
1405 if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) {
1406 addAggMembers(
1407 structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
1408 } else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) {
1409 addAggMembers(
1410 unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
1411 } else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) {
1412 addTupleMembers( tupleType, agg->expr, *agg, addedCost, memberExpr->member );
1413 }
1414 }
1415 }
1416
1417 void Finder::postvisit( const ast::MemberExpr * memberExpr ) {
1418 addCandidate( memberExpr, tenv );
1419 }
1420
1421 void Finder::postvisit( const ast::NameExpr * nameExpr ) {
1422 std::vector< ast::SymbolTable::IdData > declList;
1423 if (!selfFinder.otypeKeys.empty()) {
1424 auto kind = ast::SymbolTable::getSpecialFunctionKind(nameExpr->name);
1425 assertf(kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS, "special lookup with non-special target: %s", nameExpr->name.c_str());
1426
1427 for (auto & otypeKey: selfFinder.otypeKeys) {
1428 auto result = symtab.specialLookupId(kind, otypeKey);
1429 declList.insert(declList.end(), std::make_move_iterator(result.begin()), std::make_move_iterator(result.end()));
1430 }
1431 } else {
1432 declList = symtab.lookupIdIgnoreHidden( nameExpr->name );
1433 }
1434 PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
1435
1436 if ( declList.empty() ) return;
1437
1438 reason.code = NoMatch;
1439
1440 for ( auto & data : declList ) {
1441 Cost cost = Cost::zero;
1442 ast::Expr * newExpr = data.combine( nameExpr->location, cost );
1443
1444 CandidateRef newCand = std::make_shared<Candidate>(
1445 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero,
1446 cost );
1447 if (newCand->expr->env) {
1448 newCand->env.add(*newCand->expr->env);
1449 auto mutExpr = newCand->expr.get_and_mutate();
1450 mutExpr->env = nullptr;
1451 newCand->expr = mutExpr;
1452 }
1453
1454 PRINT(
1455 std::cerr << "decl is ";
1456 ast::print( std::cerr, data.id );
1457 std::cerr << std::endl;
1458 std::cerr << "newExpr is ";
1459 ast::print( std::cerr, newExpr );
1460 std::cerr << std::endl;
1461 )
1462 newCand->expr = ast::mutate_field(
1463 newCand->expr.get(), &ast::Expr::result,
1464 renameTyVars( newCand->expr->result ) );
1465 // add anonymous member interpretations whenever an aggregate value type is seen
1466 // as a name expression
1467 addAnonConversions( newCand );
1468 candidates.emplace_back( std::move( newCand ) );
1469 }
1470 }
1471
1472 void Finder::postvisit(const ast::VariableExpr *variableExpr) {
1473 // not sufficient to just pass `variableExpr` here, type might have changed
1474
1475 auto cand = new Candidate(variableExpr, tenv);
1476 candidates.emplace_back(cand);
1477 }
1478
1479 void Finder::postvisit( const ast::ConstantExpr * constantExpr ) {
1480 addCandidate( constantExpr, tenv );
1481 }
1482
1483 void Finder::postvisit( const ast::SizeofExpr * sizeofExpr ) {
1484 addCandidate(
1485 new ast::SizeofExpr{
1486 sizeofExpr->location, resolveTypeof( sizeofExpr->type, context ) },
1487 tenv );
1488 }
1489
1490 void Finder::postvisit( const ast::CountExpr * countExpr ) {
1491 const ast::UntypedExpr * untyped = nullptr;
1492 if ( countExpr->type ) {
1493 auto enumInst = countExpr->type.as<ast::EnumInstType>();
1494 if ( enumInst ) {
1495 addCandidate( ast::ConstantExpr::from_ulong(countExpr->location, enumInst->base->members.size()), tenv );
1496 return;
1497 }
1498 auto untypedFirst = ast::UntypedExpr::createCall( countExpr->location, "lowerBound", {} );
1499 auto castFirst = new ast::CastExpr( countExpr->location, untypedFirst , countExpr->type );
1500 untyped = ast::UntypedExpr::createCall(
1501 countExpr->location, "Countof", { castFirst }
1502 );
1503 }
1504 if (!untyped) untyped = ast::UntypedExpr::createCall(
1505 countExpr->location, "Countof", { countExpr->expr }
1506 );
1507 CandidateFinder finder( context, tenv );
1508 finder.find( untyped );
1509 CandidateList winners = findMinCost( finder.candidates );
1510 if ( winners.size() == 0 ) {
1511 SemanticError( countExpr->expr, "Countof is not implemented for operand: " );
1512 }
1513 if ( winners.size() != 1 ) {
1514 SemanticError( countExpr->expr, "Ambiguous expression in countof operand: " );
1515 }
1516 CandidateRef & choice = winners.front();
1517 choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
1518 addCandidate( *choice, choice->expr );
1519 }
1520
1521 void Finder::postvisit( const ast::AlignofExpr * alignofExpr ) {
1522 addCandidate(
1523 new ast::AlignofExpr{
1524 alignofExpr->location, resolveTypeof( alignofExpr->type, context ) },
1525 tenv );
1526 }
1527
1528 void Finder::postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) {
1529 const ast::BaseInstType * aggInst;
1530 if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ;
1531 else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ;
1532 else return;
1533
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 }, 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.