Changeset e172f42


Ignore:
Timestamp:
Jun 16, 2023, 9:10:46 AM (2 years ago)
Author:
caparsons <caparson@…>
Branches:
master
Children:
5dbb9f3
Parents:
a0bd9a2 (diff), 62d62db (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'ast-experimental'

Files:
31 edited

Legend:

Unmodified
Added
Removed
  • libcfa/prelude/builtins.c

    ra0bd9a2 re172f42  
    149149
    150150static inline {
    151         long int ?\?( int x, unsigned int y ) { __CFA_EXP__(); }
     151        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 long int ?\?( unsigned int x, unsigned int y ) { __CFA_EXP__(); }
     155        unsigned 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         long int ?\=?( int & x, unsigned int y ) { x = x \ y; return x; }
     178        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 long int ?\=?( unsigned int & x, unsigned int y ) { x = x \ y; return x; }
     181        unsigned 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

    ra0bd9a2 re172f42  
    3232} // extern "C"
    3333static inline __attribute__((always_inline)) {
    34         unsigned char abs( signed char v ) { return abs( (int)v ); }
     34        unsigned char abs( signed char v ) { return (int)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 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; }
    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 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; }
    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

    ra0bd9a2 re172f42  
    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(intsc); }
     42static 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); }
    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

    ra0bd9a2 re172f42  
    367367
    368368        char random( void ) { return (unsigned long int)random(); }
    369         char random( char u ) { return random( (unsigned long int)u ); } // [0,u)
     369        char random( char u ) { return (unsigned long int)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 random( (long int)u ); } // [0,u]
     372        int random( int u ) { return (long int)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 random( (unsigned long int)u ); } // [0,u]
     375        unsigned int random( unsigned int u ) { return (unsigned long int)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

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

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

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

    ra0bd9a2 re172f42  
    1919
    2020#include "Copy.hpp"
     21#include <iostream>
     22#include <algorithm>
     23
    2124#include "Decl.hpp"
    2225#include "Expr.hpp"
     
    203206                        out.push_back(decl.second);
    204207                }
     208
     209                // std::cerr << otypeKey << ' ' << out.size() << std::endl;
    205210        }
    206211
  • src/AST/Type.hpp

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

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

    ra0bd9a2 re172f42  
    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

    ra0bd9a2 re172f42  
    113113        using namespace ResolvExpr;
    114114        ast::OpenVarSet openVars, closedVars;
    115         ast::AssertionSet need, have;
    116         findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
    117         findOpenVars( actualType, openVars, closedVars, need, have, FirstOpen );
     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 );
    118119        for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
    119120                const ast::Type * boundType = subs->lookup( openVar.first );
     
    125126                        if ( closedVars.find( *inst ) == closedVars.end() ) {
    126127                                return true;
     128                        }
     129                        else {
     130                                assertf(false, "closed: %s", inst->name.c_str());
    127131                        }
    128132                // Otherwise, the variable is bound to a concrete type.
  • src/Parser/ExpressionNode.cc

    ra0bd9a2 re172f42  
    601601ast::Expr * build_cast( const CodeLocation & location,
    602602                DeclarationNode * decl_node,
    603                 ExpressionNode * expr_node ) {
     603                ExpressionNode * expr_node,
     604                ast::CastExpr::CastKind kind ) {
    604605        ast::Type * targetType = maybeMoveBuildType( decl_node );
    605606        if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
     
    607608                return new ast::CastExpr( location,
    608609                        maybeMoveBuild( expr_node ),
    609                         ast::ExplicitCast );
     610                        ast::ExplicitCast, kind );
    610611        } else {
    611612                return new ast::CastExpr( location,
    612613                        maybeMoveBuild( expr_node ),
    613614                        targetType,
    614                         ast::ExplicitCast );
     615                        ast::ExplicitCast, kind );
    615616        } // if
    616617} // build_cast
  • src/Parser/ExpressionNode.h

    ra0bd9a2 re172f42  
    6969ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
    7070
    71 ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
     71ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
    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

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

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

    ra0bd9a2 re172f42  
    2525#include "AdjustExprType.hpp"
    2626#include "Candidate.hpp"
    27 #include "CastCost.hpp"           // for castCost
    2827#include "CompilationState.h"
    29 #include "ConversionCost.h"       // for conversionCast
    3028#include "Cost.h"
     29#include "CastCost.hpp"
     30#include "PolyCost.hpp"
     31#include "SpecCost.hpp"
     32#include "ConversionCost.h"
    3133#include "ExplodedArg.hpp"
    32 #include "PolyCost.hpp"
    3334#include "RenameVars.h"           // for renameTyVars
    3435#include "Resolver.h"
    3536#include "ResolveTypeof.h"
     37#include "WidenMode.h"
    3638#include "SatisfyAssertions.hpp"
    37 #include "SpecCost.hpp"
    38 #include "typeops.h"              // for combos
     39#include "typeops.h"              // for adjustExprType, conversionCost, polyCost, specCost
    3940#include "Unify.h"
    4041#include "AST/Expr.hpp"
     
    5556namespace ResolvExpr {
    5657
    57 /// Unique identifier for matching expression resolutions to their requesting expression
    58 UniqueId globalResnSlot = 0;
    59 
    60 namespace {
    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 
    1701 bool 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 
    1810 void 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 
    1917 std::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 
    193558const ast::Expr * referenceToRvalueConversion( const ast::Expr * expr, Cost & cost ) {
    193659        if ( expr->result.as< ast::ReferenceType >() ) {
     
    194265        return expr;
    194366}
     67
     68/// Unique identifier for matching expression resolutions to their requesting expression
     69UniqueId globalResnSlot = 0;
    194470
    194571Cost computeConversionCost(
     
    196894}
    196995
     96namespace {
     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
     1752bool 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
     1866void 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
     1975std::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
    19701993} // namespace ResolvExpr
    19711994
  • src/ResolvExpr/CandidateFinder.hpp

    ra0bd9a2 re172f42  
    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)
    3537        std::set< std::string > otypeKeys;  /// different type may map to same key
    3638
  • src/ResolvExpr/CastCost.cc

    ra0bd9a2 re172f42  
    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                }
    236239                return Cost::zero;
    237240        } else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
  • src/ResolvExpr/CommonType.cc

    ra0bd9a2 re172f42  
    697697                        if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
    698698                                #warning remove casts when `commonTypes` moved to new AST
     699                               
     700                                /*
    699701                                ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
    700702                                if (
     
    706708                                        result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
    707709                                }
     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                               
    708722                        } else if (
    709723                                dynamic_cast< const ast::ZeroType * >( type2 )
     
    712726                                #warning remove casts when `commonTypes` moved to new AST
    713727                                ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)ast::BasicType::SignedInt ];
    714                                 if (
    715                                         ( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
     728                                /*
     729                                if ( // xxx - what does qualifier even do here??
     730                                        ( ( basic->qualifiers >= type2->qualifiers )
    716731                                                || widen.first )
    717                                         && ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
     732                                         && ( ( /* kind != basic->kind && basic->qualifiers <= type2->qualifiers )
    718733                                                || widen.second )
    719                                 ) {
    720                                         result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
     734                                )
     735                                */
     736                                if (widen.second) {
     737                                        result = new ast::BasicType{ basic->kind, basic->qualifiers | type2->qualifiers };
    721738                                }
    722739                        } else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
     
    746763                                auto entry = open.find( *var );
    747764                                if ( entry != open.end() ) {
     765                                // if (tenv.lookup(*var)) {
    748766                                        ast::AssertionSet need, have;
    749767                                        if ( ! tenv.bindVar(
  • src/ResolvExpr/ConversionCost.cc

    ra0bd9a2 re172f42  
    702702
    703703        cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
     704
     705        // xxx - should qualifiers be considered in pass-by-value?
     706        /*
    704707        if ( refType->base->qualifiers == dst->qualifiers ) {
    705708                cost.incReference();
     
    709712                cost.incUnsafe();
    710713        }
     714        */
     715        cost.incReference();
    711716}
    712717
     
    792797                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    793798                }
     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;
    794804        } else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
    795805                cost = Cost::zero;
    796806                // +1 for zero_t ->, +1 for disambiguation
    797807                cost.incSafe( maxIntCost + 2 );
     808                // assuming 0p is supposed to be used for pointers?
    798809        }
    799810}
     
    804815                cost = Cost::zero;
    805816        } else if ( const ast::BasicType * dstAsBasic =
    806                         dynamic_cast< const ast::BasicType * >( dst ) ) {
     817                        dynamic_cast< const ast::BasicType * >( dst ) ) {               
    807818                int tableResult = costMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ];
    808819                if ( -1 == tableResult ) {
     
    813824                        cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
    814825                }
     826               
     827                // cost = Cost::zero;
    815828        }
    816829}
  • src/ResolvExpr/FindOpenVars.cc

    ra0bd9a2 re172f42  
    2121#include "AST/Pass.hpp"
    2222#include "AST/Type.hpp"
     23#include "AST/TypeEnvironment.hpp"
    2324#include "Common/PassVisitor.h"
    2425#include "SynTree/Declaration.h"  // for TypeDecl, DeclarationWithType (ptr ...
    2526#include "SynTree/Type.h"         // for Type, Type::ForallList, ArrayType
     27
     28#include <iostream>
    2629
    2730namespace ResolvExpr {
     
    102105                        ast::AssertionSet & need;
    103106                        ast::AssertionSet & have;
     107                        ast::TypeEnvironment & env;
    104108                        bool nextIsOpen;
    105109
    106110                        FindOpenVars_new(
    107111                                ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n,
    108                                 ast::AssertionSet & h, FirstMode firstIsOpen )
    109                         : open( o ), closed( c ), need( n ), have( h ), nextIsOpen( firstIsOpen ) {}
     112                                ast::AssertionSet & h, ast::TypeEnvironment & env, FirstMode firstIsOpen )
     113                        : open( o ), closed( c ), need( n ), have( h ), env (env), nextIsOpen( firstIsOpen ) {}
    110114
    111115                        void previsit( const ast::FunctionType * type ) {
    112116                                // mark open/closed variables
    113117                                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
    114124                                        for ( auto & decl : type->forall ) {
    115125                                                open[ *decl ] = ast::TypeData{ decl->base };
     
    137147        void findOpenVars(
    138148                        const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
    139                         ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen ) {
    140                 ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, firstIsOpen };
     149                        ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen ) {
     150                ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, env, firstIsOpen };
    141151                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                }
    142160        }
    143161} // namespace ResolvExpr
  • src/ResolvExpr/FindOpenVars.h

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

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

    ra0bd9a2 re172f42  
    1616#include "SatisfyAssertions.hpp"
    1717
     18#include <iostream>
    1819#include <algorithm>
    1920#include <cassert>
     
    4546#include "SymTab/Mangler.h"
    4647
     48
     49
    4750namespace ResolvExpr {
    4851
     
    6568                        ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs )
    6669                : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ),
    67                   need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {}
     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                  }
    6875        };
    6976
     
    139146        };
    140147
    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         }
     148        enum AssertionResult {Fail, Skip, Success} ;
    147149
    148150        /// Binds a single assertion, updating satisfaction state
     
    155157                        "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
    156158
    157                 ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost );
     159                ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cost );
    158160                varExpr->result = match.adjType;
    159161                if ( match.resnSlot ) { varExpr->inferred.resnSlots().emplace_back( match.resnSlot ); }
     
    165167
    166168        /// Satisfy a single assertion
    167         bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool allowConversion = false, bool skipUnbound = false) {
     169        AssertionResult satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool skipUnbound = false) {
    168170                // skip unused assertions
    169                 if ( ! assn.second.isUsed ) return true;
     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;
    170175
    171176                // find candidates that unify with the desired type
    172                 AssnCandidateList matches;
     177                AssnCandidateList matches, inexactMatches;
    173178
    174179                std::vector<ast::SymbolTable::IdData> candidates;
     
    179184                                .strict_as<ast::FunctionType>()->params[0]
    180185                                .strict_as<ast::ReferenceType>()->base;
    181                         sat.cand->env.apply(thisArgType);
     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                        }
    182192
    183193                        std::string otypeKey = "";
    184194                        if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
    185195                        else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
    186                         else if (skipUnbound) return false;
     196                        else if (skipUnbound) return AssertionResult::Skip;
    187197
    188198                        candidates = sat.symtab.specialLookupId(kind, otypeKey);
     
    212222
    213223                        ast::OpenVarSet closed;
    214                         findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
    215                         findOpenVars( adjType, newOpen, closed, newNeed, have, FirstOpen );
    216                         if ( allowConversion ) {
     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();
    217244                                if ( auto c = commonType( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
    218245                                        // set up binding slot for recursive assertions
     
    223250                                        }
    224251
    225                                         matches.emplace_back(
     252                                        inexactMatches.emplace_back(
    226253                                                cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
    227254                                                std::move( newOpen ), crntResnSlot );
    228255                                }
    229256                        }
    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                         }
    244257                }
    245258
    246259                // break if no satisfying match
    247                 if ( matches.empty() ) return false;
     260                if ( matches.empty() ) matches = std::move(inexactMatches);
     261                if ( matches.empty() ) return AssertionResult::Fail;
    248262
    249263                // defer if too many satisfying matches
    250264                if ( matches.size() > 1 ) {
    251265                        sat.deferred.emplace_back( assn.first, assn.second, std::move( matches ) );
    252                         return true;
     266                        return AssertionResult::Success;
    253267                }
    254268
    255269                // otherwise bind unique match in ongoing scope
    256270                AssnCandidate & match = matches.front();
    257                 addToSymbolTable( match.have, sat.symtab );
     271                // addToSymbolTable( match.have, sat.symtab );
    258272                sat.newNeed.insert( match.need.begin(), match.need.end() );
    259273                sat.cand->env = std::move( match.env );
     
    261275
    262276                bindAssertion( assn.first, assn.second, sat.cand, match, sat.inferred );
    263                 return true;
     277                return AssertionResult::Success;
    264278        }
    265279
     
    438452                // for each current mutually-compatible set of assertions
    439453                for ( SatState & sat : sats ) {
    440                         bool allowConversion = false;
    441454                        // stop this branch if a better option is already found
    442455                        auto it = thresholds.find( pruneKey( *sat.cand ) );
     
    447460                        for (unsigned resetCount = 0; ; ++resetCount) {
    448461                                ast::AssertionList next;
    449                                 resetTyVarRenaming();
    450462                                // make initial pass at matching assertions
    451463                                for ( auto & assn : sat.need ) {
     464                                        resetTyVarRenaming();
    452465                                        // fail early if any assertion is not satisfiable
    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) {
     466                                        auto result = satisfyAssertion( assn, sat, !next.empty() );
     467                                        if ( result == AssertionResult::Fail ) {
    463468                                                Indenter tabs{ 3 };
    464469                                                std::ostringstream ss;
     
    466471                                                print( ss, *sat.cand, ++tabs );
    467472                                                ss << (tabs-1) << "Could not satisfy assertion:\n";
    468                                                 ast::print( ss, next[0].first, tabs );
     473                                                ast::print( ss, assn.first, tabs );
    469474
    470475                                                errors.emplace_back( ss.str() );
    471476                                                goto nextSat;
    472477                                        }
    473 
    474                                         else {
    475                                                 allowConversion = true;
    476                                                 continue;
    477                                         }
    478                                 }
    479                                 allowConversion = false;
     478                                        else if ( result == AssertionResult::Skip ) {
     479                                                next.emplace_back(assn);
     480                                                // goto nextSat;
     481                                        }
     482                                }
     483                                // success
     484                                if (next.empty()) break;
     485
    480486                                sat.need = std::move(next);
    481487                        }
     
    531537                                                sat.cand->expr, std::move( compat.env ), std::move( compat.open ),
    532538                                                ast::AssertionSet{} /* need moved into satisfaction state */,
    533                                                 sat.cand->cost, sat.cand->cvtCost );
     539                                                sat.cand->cost );
    534540
    535541                                        ast::AssertionSet nextNewNeed{ sat.newNeed };
     
    544550                                        for ( DeferRef r : compat.assns ) {
    545551                                                AssnCandidate match = r.match;
    546                                                 addToSymbolTable( match.have, nextSymtab );
     552                                                // addToSymbolTable( match.have, nextSymtab );
    547553                                                nextNewNeed.insert( match.need.begin(), match.need.end() );
    548554
  • src/ResolvExpr/Unify.cc

    ra0bd9a2 re172f42  
    160160                env.apply( newSecond );
    161161
    162                 findOpenVars( newFirst, open, closed, need, have, FirstClosed );
    163                 findOpenVars( newSecond, open, closed, need, have, FirstOpen );
     162                // findOpenVars( newFirst, open, closed, need, have, FirstClosed );
     163                findOpenVars( newSecond, open, closed, need, have, newEnv, 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) this->result = otherInst;
     966                        if (otherInst && inst->name == otherInst->name)
     967                                this->result = otherInst;
    967968                        return otherInst;
    968969                }
     
    10491050
    10501051                void postvisit( const ast::TypeInstType * typeInst ) {
    1051                         assert( open.find( *typeInst ) == open.end() );
    1052                         handleRefType( typeInst, type2 );
     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;
    10531057                }
    10541058
     
    11611165        ) {
    11621166                ast::OpenVarSet closed;
    1163                 findOpenVars( type1, open, closed, need, have, FirstClosed );
    1164                 findOpenVars( type2, open, closed, need, have, FirstOpen );
     1167                // findOpenVars( type1, open, closed, need, have, FirstClosed );
     1168                findOpenVars( type2, open, closed, need, have, env, FirstOpen );
    11651169                return unifyInexact(
    11661170                        type1, type2, env, need, have, open, WidenMode{ true, true }, common );
     
    11791183                        entry1 = var1 ? open.find( *var1 ) : open.end(),
    11801184                        entry2 = var2 ? open.find( *var2 ) : open.end();
    1181                 bool isopen1 = entry1 != open.end();
    1182                 bool isopen2 = entry2 != open.end();
    1183 
     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                /*
    11841191                if ( isopen1 && isopen2 ) {
    11851192                        if ( entry1->second.kind != entry2->second.kind ) return false;
     
    11901197                        return env.bindVar( var1, type2, entry1->second, need, have, open, widen );
    11911198                } else if ( isopen2 ) {
    1192                         return env.bindVar( var2, type1, entry2->second, need, have, open, widen );
    1193                 } else {
     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 {
    11941211                        return ast::Pass<Unify_new>::read(
    11951212                                type1, type2, env, need, have, open, widen );
    11961213                }
     1214               
    11971215        }
    11981216
  • src/SynTree/Expression.cc

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

    ra0bd9a2 re172f42  
    271271        bool isGenerated = true;
    272272
    273         CastExpr( Expression * arg, bool isGenerated = true );
    274         CastExpr( Expression * arg, Type * toType, bool isGenerated = true );
     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 );
    275283        CastExpr( Expression * arg, void * ) = delete; // prevent accidentally passing pointers for isGenerated in the first constructor
    276284        CastExpr( const CastExpr & other );
  • src/Tuples/TupleAssignment.cc

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

    ra0bd9a2 re172f42  
    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        */
    323335
    324336        definitions.push_back( decl );
     
    352364        std::vector<ast::ptr<ast::TypeDecl>> type_params;
    353365        std::vector<ast::ptr<ast::DeclWithType>> assertions;
     366
     367        ast::DeclReplacer::TypeMap typeMap;
    354368        for ( auto & old_param : old_type_params ) {
    355369                ast::TypeDecl * decl = ast::deepCopy( old_param );
     
    358372                oldToNew.emplace( std::make_pair( old_param, decl ) );
    359373                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);
    360382        }
    361383        replaceAll( params, oldToNew );
     
    522544        InitTweak::InitExpander_new srcParam( src );
    523545        // Assign to destination.
    524         ast::Expr * dstSelect = new ast::MemberExpr(
     546        ast::MemberExpr * dstSelect = new ast::MemberExpr(
    525547                location,
    526548                field,
     
    574596                }
    575597
    576                 ast::Expr * srcSelect = (srcParam) ? new ast::MemberExpr(
     598                ast::MemberExpr * srcSelect = (srcParam) ? new ast::MemberExpr(
    577599                        location, field, new ast::VariableExpr( location, srcParam )
    578600                ) : nullptr;
  • tests/collections/vector-demo.cfa

    ra0bd9a2 re172f42  
    143143    assert( v`capacity >  5 && v`length == 5 );
    144144
    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
     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
    146146
    147147    float val3 = iter`val;
Note: See TracChangeset for help on using the changeset viewer.