Changes in / [e172f42:a0bd9a2]


Ignore:
Files:
31 edited

Legend:

Unmodified
Added
Removed
  • libcfa/prelude/builtins.c

    re172f42 ra0bd9a2  
    149149
    150150static inline {
    151         int ?\?( int x, unsigned int y ) { __CFA_EXP__(); }
     151        long int ?\?( int x, unsigned int y ) { __CFA_EXP__(); }
    152152        long int ?\?( long int x, unsigned long int y ) { __CFA_EXP__(); }
    153153        long long int ?\?( long long int x, unsigned long long int y ) { __CFA_EXP__(); }
    154154        // unsigned computation may be faster and larger
    155         unsigned int ?\?( unsigned int x, unsigned int y ) { __CFA_EXP__(); }
     155        unsigned long int ?\?( unsigned int x, unsigned int y ) { __CFA_EXP__(); }
    156156        unsigned long int ?\?( unsigned long int x, unsigned long int y ) { __CFA_EXP__(); }
    157157        unsigned long long int ?\?( unsigned long long int x, unsigned long long int y ) { __CFA_EXP__(); }
     
    176176
    177177static inline {
    178         int ?\=?( int & x, unsigned int y ) { x = x \ y; return x; }
     178        long int ?\=?( int & x, unsigned int y ) { x = x \ y; return x; }
    179179        long int ?\=?( long int & x, unsigned long int y ) { x = x \ y; return x; }
    180180        long long int ?\=?( long long int & x, unsigned long long int y ) { x = x \ y; return x; }
    181         unsigned int ?\=?( unsigned int & x, unsigned int y ) { x = x \ y; return x; }
     181        unsigned long int ?\=?( unsigned int & x, unsigned int y ) { x = x \ y; return x; }
    182182        unsigned long int ?\=?( unsigned long int & x, unsigned long int y ) { x = x \ y; return x; }
    183183        unsigned long long int ?\=?( unsigned long long int & x, unsigned long long int y ) { x = x \ y; return x; }
  • libcfa/src/common.hfa

    re172f42 ra0bd9a2  
    3232} // extern "C"
    3333static inline __attribute__((always_inline)) {
    34         unsigned char abs( signed char v ) { return (int)abs( (int)v ); }
     34        unsigned char abs( signed char v ) { return abs( (int)v ); }
    3535        // use default C routine for int
    3636        unsigned long int abs( long int v ) { return labs( v ); }
     
    7070        unsigned int min( unsigned int v1, unsigned int v2 ) { return v1 < v2 ? v1 : v2; }
    7171        long int min( long int v1, long int v2 ) { return v1 < v2 ? v1 : v2; }
    72         unsigned long int min( unsigned long int v1, unsigned long int v2 ) { return v1 < v2 ? v1 : v2; }
     72        unsigned long int min( unsigned long int v1, unsigned int v2 ) { return v1 < v2 ? v1 : v2; }
    7373        long long int min( long long int v1, long long int v2 ) { return v1 < v2 ? v1 : v2; }
    74         unsigned long long int min( unsigned long long int v1, unsigned long long int v2 ) { return v1 < v2 ? v1 : v2; }
     74        unsigned long long int min( unsigned long long int v1, unsigned int v2 ) { return v1 < v2 ? v1 : v2; }
    7575        forall( T | { int ?<?( T, T ); } )                                      // generic
    7676        T min( T v1, T v2 ) { return v1 < v2 ? v1 : v2; }
  • libcfa/src/concurrency/kernel/cluster.hfa

    re172f42 ra0bd9a2  
    4040
    4141// convert to log2 scale but using double
    42 static inline __readyQ_avg_t __to_readyQ_avg(unsigned long long intsc) { if(unlikely(0 == intsc)) return 0.0; else return log2((__readyQ_avg_t)intsc); }
     42static inline __readyQ_avg_t __to_readyQ_avg(unsigned long long intsc) { if(unlikely(0 == intsc)) return 0.0; else return log2(intsc); }
    4343
    4444#define warn_large_before warnf( !strict || old_avg < 35.0, "Suspiciously large previous average: %'lf, %'" PRId64 "ms \n", old_avg, program()`ms )
  • libcfa/src/stdlib.hfa

    re172f42 ra0bd9a2  
    367367
    368368        char random( void ) { return (unsigned long int)random(); }
    369         char random( char u ) { return (unsigned long int)random( (unsigned long int)u ); } // [0,u)
     369        char random( char u ) { return random( (unsigned long int)u ); } // [0,u)
    370370        char random( char l, char u ) { return random( (unsigned long int)l, (unsigned long int)u ); } // [l,u)
    371371        int random( void ) { return (long int)random(); }
    372         int random( int u ) { return (long int)random( (long int)u ); } // [0,u]
     372        int random( int u ) { return random( (long int)u ); } // [0,u]
    373373        int random( int l, int u ) { return random( (long int)l, (long int)u ); } // [l,u)
    374374        unsigned int random( void ) { return (unsigned long int)random(); }
    375         unsigned int random( unsigned int u ) { return (unsigned long int)random( (unsigned long int)u ); } // [0,u]
     375        unsigned int random( unsigned int u ) { return random( (unsigned long int)u ); } // [0,u]
    376376        unsigned int random( unsigned int l, unsigned int u ) { return random( (unsigned long int)l, (unsigned long int)u ); } // [l,u)
    377377} // distribution
  • src/AST/Convert.cpp

    re172f42 ra0bd9a2  
    23432343                                old->location,
    23442344                                GET_ACCEPT_1(arg, Expr),
    2345                                 old->isGenerated ? ast::GeneratedCast : ast::ExplicitCast,
    2346                                 (ast::CastExpr::CastKind) old->kind
     2345                                old->isGenerated ? ast::GeneratedCast : ast::ExplicitCast
    23472346                        )
    23482347                );
  • src/AST/Expr.cpp

    re172f42 ra0bd9a2  
    186186// --- CastExpr
    187187
    188 CastExpr::CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g, CastKind kind )
    189 : Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ), kind( kind ) {}
     188CastExpr::CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g )
     189: Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {}
    190190
    191191bool CastExpr::get_lvalue() const {
  • src/AST/Expr.hpp

    re172f42 ra0bd9a2  
    5555                const Expr * e )
    5656        : decl( id ), declptr( declptr ), actualType( actual ), formalType( formal ), expr( e ) {}
    57 
    58         operator bool() {return declptr;}
    5957};
    6058
     
    337335        GeneratedFlag isGenerated;
    338336
    339         enum CastKind {
    340                 Default, // C
    341                 Coerce, // reinterpret cast
    342                 Return  // overload selection
    343         };
    344 
    345         CastKind kind = Default;
    346 
    347337        CastExpr( const CodeLocation & loc, const Expr * a, const Type * to,
    348                 GeneratedFlag g = GeneratedCast, CastKind kind = Default ) : Expr( loc, to ), arg( a ), isGenerated( g ), kind( kind ) {}
     338                GeneratedFlag g = GeneratedCast ) : Expr( loc, to ), arg( a ), isGenerated( g ) {}
    349339        /// Cast-to-void
    350         CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast, CastKind kind = Default );
     340        CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast );
    351341
    352342        /// Wrap a cast expression around an existing expression (always generated)
  • src/AST/SymbolTable.cpp

    re172f42 ra0bd9a2  
    1919
    2020#include "Copy.hpp"
    21 #include <iostream>
    22 #include <algorithm>
    23 
    2421#include "Decl.hpp"
    2522#include "Expr.hpp"
     
    206203                        out.push_back(decl.second);
    207204                }
    208 
    209                 // std::cerr << otypeKey << ' ' << out.size() << std::endl;
    210205        }
    211206
  • src/AST/Type.hpp

    re172f42 ra0bd9a2  
    451451        bool operator==(const TypeEnvKey & other) const;
    452452        bool operator<(const TypeEnvKey & other) const;
    453         operator bool() {return base;}
    454 };
    455 
     453};
    456454
    457455/// tuple type e.g. `[int, char]`
  • src/AST/TypeEnvironment.cpp

    re172f42 ra0bd9a2  
    135135                }
    136136        }
    137         // sub.normalize();
     137        sub.normalize();
    138138}
    139139
  • src/AST/TypeEnvironment.hpp

    re172f42 ra0bd9a2  
    6363
    6464                int cmp = d1->var->name.compare( d2->var->name );
    65                 return cmp > 0 || ( cmp == 0 && d1->result < d2->result );
     65                return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
    6666        }
    6767};
  • src/GenPoly/SpecializeNew.cpp

    re172f42 ra0bd9a2  
    113113        using namespace ResolvExpr;
    114114        ast::OpenVarSet openVars, closedVars;
    115         ast::AssertionSet need, have; // unused
    116         ast::TypeEnvironment env; // unused
    117         // findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
    118         findOpenVars( actualType, openVars, closedVars, need, have, env, FirstOpen );
     115        ast::AssertionSet need, have;
     116        findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
     117        findOpenVars( actualType, openVars, closedVars, need, have, FirstOpen );
    119118        for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
    120119                const ast::Type * boundType = subs->lookup( openVar.first );
     
    126125                        if ( closedVars.find( *inst ) == closedVars.end() ) {
    127126                                return true;
    128                         }
    129                         else {
    130                                 assertf(false, "closed: %s", inst->name.c_str());
    131127                        }
    132128                // Otherwise, the variable is bound to a concrete type.
  • src/Parser/ExpressionNode.cc

    re172f42 ra0bd9a2  
    601601ast::Expr * build_cast( const CodeLocation & location,
    602602                DeclarationNode * decl_node,
    603                 ExpressionNode * expr_node,
    604                 ast::CastExpr::CastKind kind ) {
     603                ExpressionNode * expr_node ) {
    605604        ast::Type * targetType = maybeMoveBuildType( decl_node );
    606605        if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
     
    608607                return new ast::CastExpr( location,
    609608                        maybeMoveBuild( expr_node ),
    610                         ast::ExplicitCast, kind );
     609                        ast::ExplicitCast );
    611610        } else {
    612611                return new ast::CastExpr( location,
    613612                        maybeMoveBuild( expr_node ),
    614613                        targetType,
    615                         ast::ExplicitCast, kind );
     614                        ast::ExplicitCast );
    616615        } // if
    617616} // build_cast
  • src/Parser/ExpressionNode.h

    re172f42 ra0bd9a2  
    6969ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
    7070
    71 ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
     71ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
    7272ast::Expr * build_keyword_cast( const CodeLocation &, ast::AggregateDecl::Aggregate target, ExpressionNode * expr_node );
    7373ast::Expr * build_virtual_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
  • src/Parser/parser.yy

    re172f42 ra0bd9a2  
    931931                { $$ = new ExpressionNode( new ast::VirtualCastExpr( yylloc, maybeMoveBuild( $5 ), maybeMoveBuildType( $3 ) ) ); }
    932932        | '(' RETURN type_no_function ')' cast_expression       // CFA
    933                 { $$ = new ExpressionNode( build_cast( yylloc, $3, $5, ast::CastExpr::Return ) ); }
     933                { SemanticError( yylloc, "Return cast is currently unimplemented." ); $$ = nullptr; }
    934934        | '(' COERCE type_no_function ')' cast_expression       // CFA
    935935                { SemanticError( yylloc, "Coerce cast is currently unimplemented." ); $$ = nullptr; }
  • src/ResolvExpr/Candidate.hpp

    re172f42 ra0bd9a2  
    9191
    9292/// Holdover behaviour from old `findMinCost` -- xxx -- can maybe be eliminated?
    93 /*
    9493static inline void promoteCvtCost( CandidateList & candidates ) {
    9594        for ( CandidateRef & r : candidates ) {
     
    9796        }
    9897}
    99 */
    10098
    10199void print( std::ostream & os, const Candidate & cand, Indenter indent = {} );
  • src/ResolvExpr/CandidateFinder.cpp

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

    re172f42 ra0bd9a2  
    3333        const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
    3434        ast::ptr< ast::Type > targetType;  ///< Target type for resolution
    35         bool strictMode = false;           ///< If set to true, requires targetType to be exact match (inside return cast)
    36         bool allowVoid = false;            ///< If set to true, allow void-returning function calls (only top level, cast to void and first in comma)
    3735        std::set< std::string > otypeKeys;  /// different type may map to same key
    3836
  • src/ResolvExpr/CastCost.cc

    re172f42 ra0bd9a2  
    234234        if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
    235235                PRINT( std::cerr << "compatible!" << std::endl; )
    236                 if (dynamic_cast<const ast::ZeroType *>(dst) || dynamic_cast<const ast::OneType *>(dst)) {
    237                         return Cost::spec;
    238                 }
    239236                return Cost::zero;
    240237        } else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
  • src/ResolvExpr/CommonType.cc

    re172f42 ra0bd9a2  
    697697                        if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
    698698                                #warning remove casts when `commonTypes` moved to new AST
    699                                
    700                                 /*
    701699                                ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
    702700                                if (
     
    708706                                        result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
    709707                                }
    710                                 */
    711                                 ast::BasicType::Kind kind;
    712                                 if (basic->kind != basic2->kind && !widen.first && !widen.second) return;
    713                                 else if (!widen.first) kind = basic->kind; // widen.second
    714                                 else if (!widen.second) kind = basic2->kind;
    715                                 else kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
    716                                 // xxx - what does qualifiers even do here??
    717                                 if ( (basic->qualifiers >= basic2->qualifiers || widen.first)
    718                                         && (basic->qualifiers <= basic2->qualifiers || widen.second) ) {
    719                                         result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
    720                                 }
    721                                
    722708                        } else if (
    723709                                dynamic_cast< const ast::ZeroType * >( type2 )
     
    726712                                #warning remove casts when `commonTypes` moved to new AST
    727713                                ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)ast::BasicType::SignedInt ];
    728                                 /*
    729                                 if ( // xxx - what does qualifier even do here??
    730                                         ( ( basic->qualifiers >= type2->qualifiers )
     714                                if (
     715                                        ( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
    731716                                                || widen.first )
    732                                          && ( ( /* kind != basic->kind && basic->qualifiers <= type2->qualifiers )
     717                                        && ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
    733718                                                || widen.second )
    734                                 )
    735                                 */
    736                                 if (widen.second) {
    737                                         result = new ast::BasicType{ basic->kind, basic->qualifiers | type2->qualifiers };
     719                                ) {
     720                                        result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
    738721                                }
    739722                        } else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
     
    763746                                auto entry = open.find( *var );
    764747                                if ( entry != open.end() ) {
    765                                 // if (tenv.lookup(*var)) {
    766748                                        ast::AssertionSet need, have;
    767749                                        if ( ! tenv.bindVar(
  • src/ResolvExpr/ConversionCost.cc

    re172f42 ra0bd9a2  
    702702
    703703        cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
    704 
    705         // xxx - should qualifiers be considered in pass-by-value?
    706         /*
    707704        if ( refType->base->qualifiers == dst->qualifiers ) {
    708705                cost.incReference();
     
    712709                cost.incUnsafe();
    713710        }
    714         */
    715         cost.incReference();
    716711}
    717712
     
    797792                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    798793                }
    799                 // this has the effect of letting any expr such as x+0, x+1 to be typed
    800                 // the same as x, instead of at least int. are we willing to sacrifice this little
    801                 // bit of coherence with C?
    802                 // TODO: currently this does not work when no zero/one overloads exist. Find a fix for it.
    803                 // cost = Cost::zero;
    804794        } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
    805795                cost = Cost::zero;
    806796                // +1 for zero_t ->, +1 for disambiguation
    807797                cost.incSafe( maxIntCost + 2 );
    808                 // assuming 0p is supposed to be used for pointers?
    809798        }
    810799}
     
    815804                cost = Cost::zero;
    816805        } else if ( const ast::BasicType * dstAsBasic =
    817                         dynamic_cast< const ast::BasicType * >( dst ) ) {               
     806                        dynamic_cast< const ast::BasicType * >( dst ) ) {
    818807                int tableResult = costMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ];
    819808                if ( -1 == tableResult ) {
     
    824813                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    825814                }
    826                
    827                 // cost = Cost::zero;
    828815        }
    829816}
  • src/ResolvExpr/FindOpenVars.cc

    re172f42 ra0bd9a2  
    2121#include "AST/Pass.hpp"
    2222#include "AST/Type.hpp"
    23 #include "AST/TypeEnvironment.hpp"
    2423#include "Common/PassVisitor.h"
    2524#include "SynTree/Declaration.h"  // for TypeDecl, DeclarationWithType (ptr ...
    2625#include "SynTree/Type.h"         // for Type, Type::ForallList, ArrayType
    27 
    28 #include <iostream>
    2926
    3027namespace ResolvExpr {
     
    105102                        ast::AssertionSet & need;
    106103                        ast::AssertionSet & have;
    107                         ast::TypeEnvironment & env;
    108104                        bool nextIsOpen;
    109105
    110106                        FindOpenVars_new(
    111107                                ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n,
    112                                 ast::AssertionSet & h, ast::TypeEnvironment & env, FirstMode firstIsOpen )
    113                         : open( o ), closed( c ), need( n ), have( h ), env (env), nextIsOpen( firstIsOpen ) {}
     108                                ast::AssertionSet & h, FirstMode firstIsOpen )
     109                        : open( o ), closed( c ), need( n ), have( h ), nextIsOpen( firstIsOpen ) {}
    114110
    115111                        void previsit( const ast::FunctionType * type ) {
    116112                                // mark open/closed variables
    117113                                if ( nextIsOpen ) {
    118                                         // trying to remove this from resolver.
    119                                         // occasionally used in other parts so not deleting right now.
    120 
    121                                         // insert open variables unbound to environment.
    122                                         env.add(type->forall);
    123 
    124114                                        for ( auto & decl : type->forall ) {
    125115                                                open[ *decl ] = ast::TypeData{ decl->base };
     
    147137        void findOpenVars(
    148138                        const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
    149                         ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen ) {
    150                 ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, env, firstIsOpen };
     139                        ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen ) {
     140                ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, firstIsOpen };
    151141                type->accept( finder );
    152 
    153                 if (!closed.empty()) {
    154                         std::cerr << "closed: ";
    155                         for (auto& i : closed) {
    156                                 std::cerr << i.first.base->location << ":" << i.first.base->name << ' ';
    157                         }
    158                         std::cerr << std::endl;
    159                 }
    160142        }
    161143} // namespace ResolvExpr
  • src/ResolvExpr/FindOpenVars.h

    re172f42 ra0bd9a2  
    3333        void findOpenVars(
    3434                const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
    35                 ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen );
     35                ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen );
    3636} // namespace ResolvExpr
    3737
  • src/ResolvExpr/Resolver.cc

    re172f42 ra0bd9a2  
    10111011                        ast::TypeEnvironment env;
    10121012                        CandidateFinder finder( context, env );
    1013                         finder.allowVoid = true;
    10141013                        finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
    10151014                        --recursion_level;
     
    10551054
    10561055                        // promote candidate.cvtCost to .cost
    1057                         // promoteCvtCost( winners );
     1056                        promoteCvtCost( winners );
    10581057
    10591058                        // produce ambiguous errors, if applicable
  • src/ResolvExpr/SatisfyAssertions.cpp

    re172f42 ra0bd9a2  
    1616#include "SatisfyAssertions.hpp"
    1717
    18 #include <iostream>
    1918#include <algorithm>
    2019#include <cassert>
     
    4645#include "SymTab/Mangler.h"
    4746
    48 
    49 
    5047namespace ResolvExpr {
    5148
     
    6865                        ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs )
    6966                : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ),
    70                   need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {
    71                         if (!have.empty()) {
    72                                 std::cerr << c.id->location << ':' << c.id->name << std::endl;
    73                         }
    74                   }
     67                  need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {}
    7568        };
    7669
     
    146139        };
    147140
    148         enum AssertionResult {Fail, Skip, Success} ;
     141        /// Adds a captured assertion to the symbol table
     142        void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) {
     143                for ( auto & i : have ) {
     144                        if ( i.second.isUsed ) { symtab.addId( i.first->var ); }
     145                }
     146        }
    149147
    150148        /// Binds a single assertion, updating satisfaction state
     
    157155                        "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
    158156
    159                 ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cost );
     157                ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost );
    160158                varExpr->result = match.adjType;
    161159                if ( match.resnSlot ) { varExpr->inferred.resnSlots().emplace_back( match.resnSlot ); }
     
    167165
    168166        /// Satisfy a single assertion
    169         AssertionResult satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool skipUnbound = false) {
     167        bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool allowConversion = false, bool skipUnbound = false) {
    170168                // skip unused assertions
    171                 static unsigned int cnt = 0;
    172                 if ( ! assn.second.isUsed ) return AssertionResult::Success;
    173 
    174                 if (assn.first->var->name[1] == '|') std::cerr << ++cnt << std::endl;
     169                if ( ! assn.second.isUsed ) return true;
    175170
    176171                // find candidates that unify with the desired type
    177                 AssnCandidateList matches, inexactMatches;
     172                AssnCandidateList matches;
    178173
    179174                std::vector<ast::SymbolTable::IdData> candidates;
     
    184179                                .strict_as<ast::FunctionType>()->params[0]
    185180                                .strict_as<ast::ReferenceType>()->base;
    186                         // sat.cand->env.apply(thisArgType);
    187 
    188                         if (auto inst = thisArgType.as<ast::TypeInstType>()) {
    189                                 auto cls = sat.cand->env.lookup(*inst);
    190                                 if (cls && cls->bound) thisArgType = cls->bound;
    191                         }
     181                        sat.cand->env.apply(thisArgType);
    192182
    193183                        std::string otypeKey = "";
    194184                        if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
    195185                        else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
    196                         else if (skipUnbound) return AssertionResult::Skip;
     186                        else if (skipUnbound) return false;
    197187
    198188                        candidates = sat.symtab.specialLookupId(kind, otypeKey);
     
    222212
    223213                        ast::OpenVarSet closed;
    224                         // findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
    225                         findOpenVars( adjType, newOpen, closed, newNeed, have, newEnv, FirstOpen );
    226                         ast::TypeEnvironment tempNewEnv {newEnv};
    227 
    228                         if ( unifyExact( toType, adjType, tempNewEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
    229                                 // set up binding slot for recursive assertions
    230                                 ast::UniqueId crntResnSlot = 0;
    231                                 if ( ! newNeed.empty() ) {
    232                                         crntResnSlot = ++globalResnSlot;
    233                                         for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
    234                                 }
    235 
    236                                 matches.emplace_back(
    237                                         cdata, adjType, std::move( tempNewEnv ), std::move( have ), std::move( newNeed ),
    238                                         std::move( newOpen ), crntResnSlot );
    239                         }
    240                         else if ( matches.empty() ) {
    241                                 // restore invalidated env
    242                                 // newEnv = sat.cand->env;
    243                                 // newNeed.clear();
     214                        findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
     215                        findOpenVars( adjType, newOpen, closed, newNeed, have, FirstOpen );
     216                        if ( allowConversion ) {
    244217                                if ( auto c = commonType( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
    245218                                        // set up binding slot for recursive assertions
     
    250223                                        }
    251224
    252                                         inexactMatches.emplace_back(
     225                                        matches.emplace_back(
    253226                                                cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
    254227                                                std::move( newOpen ), crntResnSlot );
    255228                                }
    256229                        }
     230                        else {
     231                                if ( unifyExact( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
     232                                        // set up binding slot for recursive assertions
     233                                        ast::UniqueId crntResnSlot = 0;
     234                                        if ( ! newNeed.empty() ) {
     235                                                crntResnSlot = ++globalResnSlot;
     236                                                for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
     237                                        }
     238
     239                                        matches.emplace_back(
     240                                                cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
     241                                                std::move( newOpen ), crntResnSlot );
     242                                }
     243                        }
    257244                }
    258245
    259246                // break if no satisfying match
    260                 if ( matches.empty() ) matches = std::move(inexactMatches);
    261                 if ( matches.empty() ) return AssertionResult::Fail;
     247                if ( matches.empty() ) return false;
    262248
    263249                // defer if too many satisfying matches
    264250                if ( matches.size() > 1 ) {
    265251                        sat.deferred.emplace_back( assn.first, assn.second, std::move( matches ) );
    266                         return AssertionResult::Success;
     252                        return true;
    267253                }
    268254
    269255                // otherwise bind unique match in ongoing scope
    270256                AssnCandidate & match = matches.front();
    271                 // addToSymbolTable( match.have, sat.symtab );
     257                addToSymbolTable( match.have, sat.symtab );
    272258                sat.newNeed.insert( match.need.begin(), match.need.end() );
    273259                sat.cand->env = std::move( match.env );
     
    275261
    276262                bindAssertion( assn.first, assn.second, sat.cand, match, sat.inferred );
    277                 return AssertionResult::Success;
     263                return true;
    278264        }
    279265
     
    452438                // for each current mutually-compatible set of assertions
    453439                for ( SatState & sat : sats ) {
     440                        bool allowConversion = false;
    454441                        // stop this branch if a better option is already found
    455442                        auto it = thresholds.find( pruneKey( *sat.cand ) );
     
    460447                        for (unsigned resetCount = 0; ; ++resetCount) {
    461448                                ast::AssertionList next;
     449                                resetTyVarRenaming();
    462450                                // make initial pass at matching assertions
    463451                                for ( auto & assn : sat.need ) {
    464                                         resetTyVarRenaming();
    465452                                        // fail early if any assertion is not satisfiable
    466                                         auto result = satisfyAssertion( assn, sat, !next.empty() );
    467                                         if ( result == AssertionResult::Fail ) {
     453                                        if ( ! satisfyAssertion( assn, sat, allowConversion, !next.empty() ) ) {
     454                                                next.emplace_back(assn);
     455                                                // goto nextSat;
     456                                        }
     457                                }
     458                                // success
     459                                if (next.empty()) break;
     460                                // fail if nothing resolves
     461                                else if (next.size() == sat.need.size()) {
     462                                        if (allowConversion) {
    468463                                                Indenter tabs{ 3 };
    469464                                                std::ostringstream ss;
     
    471466                                                print( ss, *sat.cand, ++tabs );
    472467                                                ss << (tabs-1) << "Could not satisfy assertion:\n";
    473                                                 ast::print( ss, assn.first, tabs );
     468                                                ast::print( ss, next[0].first, tabs );
    474469
    475470                                                errors.emplace_back( ss.str() );
    476471                                                goto nextSat;
    477472                                        }
    478                                         else if ( result == AssertionResult::Skip ) {
    479                                                 next.emplace_back(assn);
    480                                                 // goto nextSat;
    481                                         }
    482                                 }
    483                                 // success
    484                                 if (next.empty()) break;
    485 
     473
     474                                        else {
     475                                                allowConversion = true;
     476                                                continue;
     477                                        }
     478                                }
     479                                allowConversion = false;
    486480                                sat.need = std::move(next);
    487481                        }
     
    537531                                                sat.cand->expr, std::move( compat.env ), std::move( compat.open ),
    538532                                                ast::AssertionSet{} /* need moved into satisfaction state */,
    539                                                 sat.cand->cost );
     533                                                sat.cand->cost, sat.cand->cvtCost );
    540534
    541535                                        ast::AssertionSet nextNewNeed{ sat.newNeed };
     
    550544                                        for ( DeferRef r : compat.assns ) {
    551545                                                AssnCandidate match = r.match;
    552                                                 // addToSymbolTable( match.have, nextSymtab );
     546                                                addToSymbolTable( match.have, nextSymtab );
    553547                                                nextNewNeed.insert( match.need.begin(), match.need.end() );
    554548
  • src/ResolvExpr/Unify.cc

    re172f42 ra0bd9a2  
    160160                env.apply( newSecond );
    161161
    162                 // findOpenVars( newFirst, open, closed, need, have, FirstClosed );
    163                 findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen );
     162                findOpenVars( newFirst, open, closed, need, have, FirstClosed );
     163                findOpenVars( newSecond, open, closed, need, have, FirstOpen );
    164164
    165165                return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() );
     
    964964                        // check that the other type is compatible and named the same
    965965                        auto otherInst = dynamic_cast< const XInstType * >( other );
    966                         if (otherInst && inst->name == otherInst->name)
    967                                 this->result = otherInst;
     966                        if (otherInst && inst->name == otherInst->name) this->result = otherInst;
    968967                        return otherInst;
    969968                }
     
    10501049
    10511050                void postvisit( const ast::TypeInstType * typeInst ) {
    1052                         // assert( open.find( *typeInst ) == open.end() );
    1053                         auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 );
    1054                         if (otherInst && typeInst->name == otherInst->name)
    1055                                 this->result = otherInst;
    1056                         // return otherInst;
     1051                        assert( open.find( *typeInst ) == open.end() );
     1052                        handleRefType( typeInst, type2 );
    10571053                }
    10581054
     
    11651161        ) {
    11661162                ast::OpenVarSet closed;
    1167                 // findOpenVars( type1, open, closed, need, have, FirstClosed );
    1168                 findOpenVars( type2, open, closed, need, have, env, FirstOpen );
     1163                findOpenVars( type1, open, closed, need, have, FirstClosed );
     1164                findOpenVars( type2, open, closed, need, have, FirstOpen );
    11691165                return unifyInexact(
    11701166                        type1, type2, env, need, have, open, WidenMode{ true, true }, common );
     
    11831179                        entry1 = var1 ? open.find( *var1 ) : open.end(),
    11841180                        entry2 = var2 ? open.find( *var2 ) : open.end();
    1185                 // bool isopen1 = entry1 != open.end();
    1186                 // bool isopen2 = entry2 != open.end();
    1187                 bool isopen1 = var1 && env.lookup(*var1);
    1188                 bool isopen2 = var2 && env.lookup(*var2);
    1189 
    1190                 /*
     1181                bool isopen1 = entry1 != open.end();
     1182                bool isopen2 = entry2 != open.end();
     1183
    11911184                if ( isopen1 && isopen2 ) {
    11921185                        if ( entry1->second.kind != entry2->second.kind ) return false;
     
    11971190                        return env.bindVar( var1, type2, entry1->second, need, have, open, widen );
    11981191                } else if ( isopen2 ) {
    1199                         return env.bindVar( var2, type1, entry2->second, need, have, open, widen, symtab );
    1200                 } */
    1201                 if ( isopen1 && isopen2 ) {
    1202                         if ( var1->base->kind != var2->base->kind ) return false;
    1203                         return env.bindVarToVar(
    1204                                 var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have,
    1205                                 open, widen );
    1206                 } else if ( isopen1 ) {
    1207                         return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen );
    1208                 } else if ( isopen2 ) {
    1209                         return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen );
    1210                 }else {
     1192                        return env.bindVar( var2, type1, entry2->second, need, have, open, widen );
     1193                } else {
    12111194                        return ast::Pass<Unify_new>::read(
    12121195                                type1, type2, env, need, have, open, widen );
    12131196                }
    1214                
    12151197        }
    12161198
  • src/SynTree/Expression.cc

    re172f42 ra0bd9a2  
    267267}
    268268
    269 CastExpr::CastExpr( Expression * arg, Type * toType, bool isGenerated, CastKind kind ) : arg(arg), isGenerated( isGenerated ), kind( kind ) {
     269CastExpr::CastExpr( Expression * arg, Type * toType, bool isGenerated ) : arg(arg), isGenerated( isGenerated ) {
    270270        set_result(toType);
    271271}
    272272
    273 CastExpr::CastExpr( Expression * arg, bool isGenerated, CastKind kind ) : arg(arg), isGenerated( isGenerated ), kind( kind ) {
     273CastExpr::CastExpr( Expression * arg, bool isGenerated ) : arg(arg), isGenerated( isGenerated ) {
    274274        set_result( new VoidType( Type::Qualifiers() ) );
    275275}
    276276
    277 CastExpr::CastExpr( const CastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), isGenerated( other.isGenerated ), kind( other.kind ) {
     277CastExpr::CastExpr( const CastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), isGenerated( other.isGenerated ) {
    278278}
    279279
  • src/SynTree/Expression.h

    re172f42 ra0bd9a2  
    271271        bool isGenerated = true;
    272272
    273         enum CastKind {
    274                 Default, // C
    275                 Coerce, // reinterpret cast
    276                 Return  // overload selection
    277         };
    278 
    279         CastKind kind = Default;
    280 
    281         CastExpr( Expression * arg, bool isGenerated = true, CastKind kind = Default );
    282         CastExpr( Expression * arg, Type * toType, bool isGenerated = true, CastKind kind = Default );
     273        CastExpr( Expression * arg, bool isGenerated = true );
     274        CastExpr( Expression * arg, Type * toType, bool isGenerated = true );
    283275        CastExpr( Expression * arg, void * ) = delete; // prevent accidentally passing pointers for isGenerated in the first constructor
    284276        CastExpr( const CastExpr & other );
  • src/Tuples/TupleAssignment.cc

    re172f42 ra0bd9a2  
    679679
    680680                                ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
    681                                 finder.allowVoid = true;
    682681
    683682                                try {
  • src/Validate/Autogen.cpp

    re172f42 ra0bd9a2  
    321321void FuncGenerator::produceDecl( const ast::FunctionDecl * decl ) {
    322322        assert( nullptr != decl->stmts );
    323         const auto & oldParams = getGenericParams(type);
    324         assert( decl->type_params.size() == oldParams.size());
    325 
    326         /*
    327         ast::DeclReplacer::TypeMap typeMap;
    328         for (auto it = oldParams.begin(), jt = decl->type_params.begin(); it != oldParams.end(); ++it, ++jt) {
    329                 typeMap.emplace(*it, *jt);
    330         }
    331 
    332         const ast::FunctionDecl * mut = strict_dynamic_cast<const ast::FunctionDecl *>(ast::DeclReplacer::replace(decl, typeMap));
    333         assert (mut == decl);
    334         */
    335323
    336324        definitions.push_back( decl );
     
    364352        std::vector<ast::ptr<ast::TypeDecl>> type_params;
    365353        std::vector<ast::ptr<ast::DeclWithType>> assertions;
    366 
    367         ast::DeclReplacer::TypeMap typeMap;
    368354        for ( auto & old_param : old_type_params ) {
    369355                ast::TypeDecl * decl = ast::deepCopy( old_param );
     
    372358                oldToNew.emplace( std::make_pair( old_param, decl ) );
    373359                type_params.push_back( decl );
    374                 typeMap.emplace(old_param, decl);
    375         }
    376 
    377         for (auto & param : params) {
    378                 param = ast::DeclReplacer::replace(param, typeMap);
    379         }
    380         for (auto & param : returns) {
    381                 param = ast::DeclReplacer::replace(param, typeMap);
    382360        }
    383361        replaceAll( params, oldToNew );
     
    544522        InitTweak::InitExpander_new srcParam( src );
    545523        // Assign to destination.
    546         ast::MemberExpr * dstSelect = new ast::MemberExpr(
     524        ast::Expr * dstSelect = new ast::MemberExpr(
    547525                location,
    548526                field,
     
    596574                }
    597575
    598                 ast::MemberExpr * srcSelect = (srcParam) ? new ast::MemberExpr(
     576                ast::Expr * srcSelect = (srcParam) ? new ast::MemberExpr(
    599577                        location, field, new ast::VariableExpr( location, srcParam )
    600578                ) : nullptr;
  • tests/collections/vector-demo.cfa

    re172f42 ra0bd9a2  
    143143    assert( v`capacity >  5 && v`length == 5 );
    144144
    145     v[2] = -0.1f;  // v is [0.0, 98.6, -0.1, 0.2, 0.3]; iter at -0.1, where only the new memory had that change
     145    v[2] = -0.1;  // v is [0.0, 98.6, -0.1, 0.2, 0.3]; iter at -0.1, where only the new memory had that change
    146146
    147147    float val3 = iter`val;
Note: See TracChangeset for help on using the changeset viewer.