source: src/ResolvExpr/CandidateFinder.cpp @ 17fa94f

Last change on this file since 17fa94f was 17fa94f, checked in by Andrew Beach <ajbeach@…>, 2 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.