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