source: src/ResolvExpr/CandidateFinder.cpp @ c82dad4

Last change on this file since c82dad4 was b6f2e7ab, checked in by Andrew Beach <ajbeach@…>, 8 weeks ago

Removed SizeofExpr::expr and AlignofExpr::expr, expressions that would be stored there are wrapped in TypeofType? and stored in the type field. Some special cases to hide the typeof in code generation were added. In addition, initializer length is calculated in more cases so that the full type of more arrays is known sooner. Other than that, most of the code changes were just stripping out the conditional code and checks no longer needed. Some tests had to be updated, because the typeof is not hidden in dumps and the resolver replaces known typeof expressions with the type. The extension case caused some concern but it appears that just hides warnings in the expression which no longer exists.

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