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