Changeset 58fe85a for src/ResolvExpr/SatisfyAssertions.cpp
- Timestamp:
- Jan 7, 2021, 3:27:00 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 2b4daf2, 64aeca0
- Parents:
- 3c64c668 (diff), eef8dfb (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/ResolvExpr/SatisfyAssertions.cpp
r3c64c668 r58fe85a 9 9 // Author : Aaron B. Moss 10 10 // Created On : Mon Jun 10 17:45:00 2019 11 // Last Modified By : A aron B. Moss12 // Last Modified On : Mon Jun 10 17:45:00 201913 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 1 13:56:00 2019 13 // Update Count : 2 14 14 // 15 15 … … 57 57 ast::UniqueId resnSlot; ///< Slot for any recursive assertion IDs 58 58 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 59 AssnCandidate( 60 const ast::SymbolTable::IdData c, const ast::Type * at, ast::TypeEnvironment && e, 61 61 ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs ) 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 62 : cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ), 63 63 need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {} 64 64 }; … … 69 69 /// Reference to a single deferred item 70 70 struct DeferRef { 71 const ast:: DeclWithType * decl;71 const ast::VariableExpr * expr; 72 72 const ast::AssertionSetValue & info; 73 73 const AssnCandidate & match; 74 74 }; 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 75 76 /// Wrapper for the deferred items from a single assertion satisfaction. 77 77 /// Acts like an indexed list of DeferRef 78 78 struct DeferItem { 79 const ast:: DeclWithType * decl;79 const ast::VariableExpr * expr; 80 80 const ast::AssertionSetValue & info; 81 81 AssnCandidateList matches; 82 82 83 DeferItem( 84 const ast:: DeclWithType* d, const ast::AssertionSetValue & i, AssnCandidateList && ms )85 : decl( d ), info( i ), matches( std::move( ms ) ) {}83 DeferItem( 84 const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms ) 85 : expr( d ), info( i ), matches( std::move( ms ) ) {} 86 86 87 87 bool empty() const { return matches.empty(); } … … 89 89 AssnCandidateList::size_type size() const { return matches.size(); } 90 90 91 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; }91 DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; } 92 92 }; 93 93 … … 117 117 /// Initial satisfaction state for a candidate 118 118 SatState( CandidateRef & c, const ast::SymbolTable & syms ) 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 119 : cand( c ), need(), newNeed(), deferred(), inferred(), costs{ Cost::zero }, 120 120 symtab( syms ) { need.swap( c->need ); } 121 121 122 122 /// Update satisfaction state for next step after previous state 123 123 SatState( SatState && o, IterateFlag ) 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 124 : cand( std::move( o.cand ) ), need( o.newNeed.begin(), o.newNeed.end() ), newNeed(), 125 deferred(), inferred( std::move( o.inferred ) ), costs( std::move( o.costs ) ), 126 126 symtab( o.symtab ) { costs.emplace_back( Cost::zero ); } 127 127 128 128 /// Field-wise next step constructor 129 129 SatState( 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 130 CandidateRef && c, ast::AssertionSet && nn, InferCache && i, CostVec && cs, 131 131 ast::SymbolTable && syms ) 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 132 : cand( std::move( c ) ), need( nn.begin(), nn.end() ), newNeed(), deferred(), 133 133 inferred( std::move( i ) ), costs( std::move( cs ) ), symtab( std::move( syms ) ) 134 134 { costs.emplace_back( Cost::zero ); } … … 138 138 void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) { 139 139 for ( auto & i : have ) { 140 if ( i.second.isUsed ) { symtab.addId( i.first ); }140 if ( i.second.isUsed ) { symtab.addId( i.first->var ); } 141 141 } 142 142 } 143 143 144 144 /// Binds a single assertion, updating satisfaction state 145 void bindAssertion( 146 const ast:: DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand,145 void bindAssertion( 146 const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand, 147 147 AssnCandidate & match, InferCache & inferred 148 148 ) { 149 149 const ast::DeclWithType * candidate = match.cdata.id; 150 assertf( candidate->uniqueId, 150 assertf( candidate->uniqueId, 151 151 "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() ); 152 152 153 153 ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost ); 154 154 varExpr->result = match.adjType; … … 156 156 157 157 // place newly-inferred assertion in proper location in cache 158 inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{159 candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };158 inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{ 159 candidate->uniqueId, candidate, match.adjType, expr->result, varExpr }; 160 160 } 161 161 … … 167 167 // find candidates that unify with the desired type 168 168 AssnCandidateList matches; 169 for ( const ast::SymbolTable::IdData & cdata : sat.symtab.lookupId( assn.first->name ) ) { 169 170 std::vector<ast::SymbolTable::IdData> candidates; 171 auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name); 172 if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) { 173 // prefilter special decls by argument type, if already known 174 ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base 175 .strict_as<ast::FunctionType>()->params[0] 176 .strict_as<ast::ReferenceType>()->base; 177 sat.cand->env.apply(thisArgType); 178 179 std::string otypeKey = ""; 180 if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer; 181 else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams); 182 183 candidates = sat.symtab.specialLookupId(kind, otypeKey); 184 } 185 else { 186 candidates = sat.symtab.lookupId(assn.first->var->name); 187 } 188 for ( const ast::SymbolTable::IdData & cdata : candidates ) { 170 189 const ast::DeclWithType * candidate = cdata.id; 190 191 // ignore deleted candidates. 192 // NOTE: this behavior is different from main resolver. 193 // further investigations might be needed to determine 194 // if we should implement the same rule here 195 // (i.e. error if unique best match is deleted) 196 if (candidate->isDeleted && candidate->linkage == ast::Linkage::AutoGen) continue; 171 197 172 198 // build independent unification context for candidate … … 174 200 ast::TypeEnvironment newEnv{ sat.cand->env }; 175 201 ast::OpenVarSet newOpen{ sat.cand->open }; 176 ast::ptr< ast::Type > toType = assn.first-> get_type();177 ast::ptr< ast::Type > adjType = 178 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );202 ast::ptr< ast::Type > toType = assn.first->result; 203 ast::ptr< ast::Type > adjType = 204 renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ), GEN_USAGE, false ); 179 205 180 206 // only keep candidates which unify … … 187 213 } 188 214 189 matches.emplace_back( 190 cdata, adjType, std::move( newEnv ), std::move( newNeed ), std::move( have ),215 matches.emplace_back( 216 cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ), 191 217 std::move( newOpen ), crntResnSlot ); 192 218 } … … 229 255 InferMatcher( InferCache & inferred ) : inferred( inferred ) {} 230 256 231 const ast::Expr * post mutate( const ast::Expr * expr ) {257 const ast::Expr * postvisit( const ast::Expr * expr ) { 232 258 // Skip if no slots to find 233 if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 234 259 if ( !expr->inferred.hasSlots() ) return expr; 260 // if ( expr->inferred.mode != ast::Expr::InferUnion::Slots ) return expr; 261 std::vector<UniqueId> missingSlots; 235 262 // find inferred parameters for resolution slots 236 ast::InferredParams newInferred;263 ast::InferredParams * newInferred = new ast::InferredParams(); 237 264 for ( UniqueId slot : expr->inferred.resnSlots() ) { 238 265 // fail if no matching assertions found 239 266 auto it = inferred.find( slot ); 240 267 if ( it == inferred.end() ) { 241 assert(!"missing assertion"); 268 // std::cerr << "missing assertion " << slot << std::endl; 269 missingSlots.push_back(slot); 270 continue; 242 271 } 243 272 … … 245 274 for ( auto & entry : it->second ) { 246 275 // recurse on inferParams of resolved expressions 247 entry.second.expr = post mutate( entry.second.expr );248 auto res = newInferred .emplace( entry );276 entry.second.expr = postvisit( entry.second.expr ); 277 auto res = newInferred->emplace( entry ); 249 278 assert( res.second && "all assertions newly placed" ); 250 279 } … … 252 281 253 282 ast::Expr * ret = mutate( expr ); 254 ret->inferred.set_inferParams( std::move( newInferred ) ); 283 ret->inferred.set_inferParams( newInferred ); 284 if (!missingSlots.empty()) ret->inferred.resnSlots() = missingSlots; 255 285 return ret; 256 286 } 257 287 }; 258 288 259 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 289 /// Replace ResnSlots with InferParams and add alternative to output list, if it meets pruning 260 290 /// threshold. 261 void finalizeAssertions( 262 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 263 CandidateList & out 291 void finalizeAssertions( 292 CandidateRef & cand, InferCache & inferred, PruneMap & thresholds, CostVec && costs, 293 CandidateList & out 264 294 ) { 265 295 // prune if cheaper alternative for same key has already been generated … … 278 308 } 279 309 280 /// Combo iterator that combines candidates into an output list, merging their environments. 281 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 310 /// Combo iterator that combines candidates into an output list, merging their environments. 311 /// Rejects an appended candidate if environments cannot be merged. See `Common/FilterCombos.h` 282 312 /// for description of "combo iterator". 283 313 class CandidateEnvMerger { … … 299 329 Cost cost; 300 330 301 OutType( 302 const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 331 OutType( 332 const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 303 333 const std::vector< DeferRef > & as, const ast::SymbolTable & symtab ) 304 334 : env( e ), open( o ), assns( as ), cost( Cost::zero ) { … … 306 336 for ( const DeferRef & assn : assns ) { 307 337 // compute conversion cost from satisfying decl to assertion 308 cost += computeConversionCost( 309 assn.match.adjType, assn. decl->get_type(), symtab, env );310 338 cost += computeConversionCost( 339 assn.match.adjType, assn.expr->result, false, symtab, env ); 340 311 341 // mark vars+specialization on function-type assertions 312 const ast::FunctionType * func = 342 const ast::FunctionType * func = 313 343 GenPoly::getFunctionType( assn.match.cdata.id->get_type() ); 314 344 if ( ! func ) continue; 315 345 316 for ( const a st::DeclWithType *param : func->params ) {317 cost.decSpec( specCost( param ->get_type()) );346 for ( const auto & param : func->params ) { 347 cost.decSpec( specCost( param ) ); 318 348 } 319 349 320 350 cost.incVar( func->forall.size() ); 321 322 for ( const ast::TypeDecl * td : func->forall ) { 323 cost.decSpec( td->assertions.size() ); 324 } 351 352 cost.decSpec( func->assertions.size() ); 325 353 } 326 354 } … … 329 357 }; 330 358 331 CandidateEnvMerger( 332 const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 359 CandidateEnvMerger( 360 const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 333 361 const ast::SymbolTable & syms ) 334 362 : crnt(), envs{ env }, opens{ open }, symtab( syms ) {} … … 357 385 358 386 /// Limit to depth of recursion of assertion satisfaction 359 static const int recursionLimit = 4;387 static const int recursionLimit = 8; 360 388 /// Maximum number of simultaneously-deferred assertions to attempt concurrent satisfaction of 361 389 static const int deferLimit = 10; 362 390 } // anonymous namespace 363 391 364 void satisfyAssertions( 365 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 392 void satisfyAssertions( 393 CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 366 394 std::vector<std::string> & errors 367 395 ) { … … 389 417 if ( it != thresholds.end() && it->second < sat.costs ) goto nextSat; 390 418 391 // make initial pass at matching assertions 392 for ( auto & assn : sat.need ) { 393 // fail early if any assertion is not satisfiable 394 if ( ! satisfyAssertion( assn, sat ) ) { 419 // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen. 420 for (unsigned resetCount = 0; ; ++resetCount) { 421 ast::AssertionList next; 422 resetTyVarRenaming(); 423 // make initial pass at matching assertions 424 for ( auto & assn : sat.need ) { 425 // fail early if any assertion is not satisfiable 426 if ( ! satisfyAssertion( assn, sat ) ) { 427 next.emplace_back(assn); 428 // goto nextSat; 429 } 430 } 431 // success 432 if (next.empty()) break; 433 // fail if nothing resolves 434 else if (next.size() == sat.need.size()) { 395 435 Indenter tabs{ 3 }; 396 436 std::ostringstream ss; … … 398 438 print( ss, *sat.cand, ++tabs ); 399 439 ss << (tabs-1) << "Could not satisfy assertion:\n"; 400 ast::print( ss, assn.first, tabs );440 ast::print( ss, next[0].first, tabs ); 401 441 402 442 errors.emplace_back( ss.str() ); 403 443 goto nextSat; 404 444 } 445 sat.need = std::move(next); 405 446 } 406 447 … … 408 449 // either add successful match or push back next state 409 450 if ( sat.newNeed.empty() ) { 410 finalizeAssertions( 451 finalizeAssertions( 411 452 sat.cand, sat.inferred, thresholds, std::move( sat.costs ), out ); 412 453 } else { … … 421 462 ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n"; 422 463 for ( const auto & d : sat.deferred ) { 423 ast::print( ss, d. decl, tabs );464 ast::print( ss, d.expr, tabs ); 424 465 } 425 466 … … 430 471 std::vector< CandidateEnvMerger::OutType > compatible = filterCombos( 431 472 sat.deferred, CandidateEnvMerger{ sat.cand->env, sat.cand->open, sat.symtab } ); 432 473 433 474 // fail early if no mutually-compatible assertion satisfaction 434 475 if ( compatible.empty() ) { … … 439 480 ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n"; 440 481 for ( const auto& d : sat.deferred ) { 441 ast::print( ss, d. decl, tabs );482 ast::print( ss, d.expr, tabs ); 442 483 } 443 484 … … 453 494 // set up next satisfaction state 454 495 CandidateRef nextCand = std::make_shared<Candidate>( 455 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 496 sat.cand->expr, std::move( compat.env ), std::move( compat.open ), 456 497 ast::AssertionSet{} /* need moved into satisfaction state */, 457 498 sat.cand->cost, sat.cand->cvtCost ); … … 459 500 ast::AssertionSet nextNewNeed{ sat.newNeed }; 460 501 InferCache nextInferred{ sat.inferred }; 461 502 462 503 CostVec nextCosts{ sat.costs }; 463 504 nextCosts.back() += compat.cost; 464 505 465 506 ast::SymbolTable nextSymtab{ sat.symtab }; 466 507 … … 471 512 nextNewNeed.insert( match.need.begin(), match.need.end() ); 472 513 473 bindAssertion( r. decl, r.info, nextCand, match, nextInferred );514 bindAssertion( r.expr, r.info, nextCand, match, nextInferred ); 474 515 } 475 516 476 517 // either add successful match or push back next state 477 518 if ( nextNewNeed.empty() ) { 478 finalizeAssertions( 519 finalizeAssertions( 479 520 nextCand, nextInferred, thresholds, std::move( nextCosts ), out ); 480 521 } else { 481 nextSats.emplace_back( 482 std::move( nextCand ), std::move( nextNewNeed ), 483 std::move( nextInferred ), std::move( nextCosts ), 522 nextSats.emplace_back( 523 std::move( nextCand ), std::move( nextNewNeed ), 524 std::move( nextInferred ), std::move( nextCosts ), 484 525 std::move( nextSymtab ) ); 485 526 } … … 493 534 nextSats.clear(); 494 535 } 495 536 496 537 // exceeded recursion limit if reaches here 497 538 if ( out.empty() ) {
Note:
See TracChangeset
for help on using the changeset viewer.