- Timestamp:
- Apr 30, 2019, 2:53:47 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, cleanup-dtors, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 02af79b0, 8278abf
- Parents:
- 1bc5975 (diff), 98d4df9 (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. - Location:
- src
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
src/ResolvExpr/ConversionCost.cc
r1bc5975 rec28948 10 10 // Created On : Sun May 17 07:06:19 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 14 17:04:31201913 // Update Count : 2 312 // Last Modified On : Fri Apr 26 16:33:04 2019 13 // Update Count : 24 14 14 // 15 15 … … 28 28 29 29 namespace ResolvExpr { 30 #if 0 30 31 const Cost Cost::zero = Cost{ 0, 0, 0, 0, 0, 0, 0 }; 31 32 const Cost Cost::infinity = Cost{ -1, -1, -1, -1, -1, 1, -1 }; … … 37 38 const Cost Cost::spec = Cost{ 0, 0, 0, 0, 0, -1, 0 }; 38 39 const Cost Cost::reference = Cost{ 0, 0, 0, 0, 0, 0, 1 }; 40 #endif 39 41 40 42 #if 0 -
src/ResolvExpr/Cost.h
r1bc5975 rec28948 7 7 // Cost.h -- 8 8 // 9 // Author : Richard C. Bilson9 // Author : Peter Buhr and Aaron Moss 10 10 // Created On : Sun May 17 09:39:50 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 7 20:54:29201913 // Update Count : 812 // Last Modified On : Mon Apr 29 18:33:44 2019 13 // Update Count : 49 14 14 // 15 15 … … 17 17 18 18 #include <iostream> 19 #include <cassert> 20 #include <climits> 19 21 20 22 namespace ResolvExpr { 23 #if 0 24 25 //*************************** OLD *************************** 26 21 27 class Cost { 22 28 private: 23 29 Cost( int unsafeCost, int polyCost, int safeCost, int signCost, 24 int varCost, int specCost, int referenceCost );30 int varCost, int specCost, int referenceCost ); 25 31 public: 26 32 Cost & incUnsafe( int inc = 1 ); … … 71 77 72 78 inline Cost::Cost( int unsafeCost, int polyCost, int safeCost, int signCost, 73 int varCost, int specCost, int referenceCost )79 int varCost, int specCost, int referenceCost ) 74 80 : unsafeCost( unsafeCost ), polyCost( polyCost ), safeCost( safeCost ), signCost( signCost ), 75 81 varCost( varCost ), specCost( specCost ), referenceCost( referenceCost ) {} … … 121 127 return Cost{ 122 128 unsafeCost + other.unsafeCost, polyCost + other.polyCost, safeCost + other.safeCost, 123 signCost + other.signCost, varCost + other.varCost, specCost + other.specCost,124 referenceCost + other.referenceCost };129 signCost + other.signCost, varCost + other.varCost, specCost + other.specCost, 130 referenceCost + other.referenceCost }; 125 131 } 126 132 … … 211 217 << cost.referenceCost << " )"; 212 218 } 219 220 #else 221 222 //*************************** NEW *************************** 223 224 // To maximize performance and space, the 7 resolution costs are packed into a single 64-bit word. However, the 225 // specialization cost is a negative value so a correction is needed is a few places. 226 227 class Cost { 228 union { 229 struct { 230 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 231 // Little-endian => first value is low priority and last is high priority. 232 unsigned char padding; ///< unused 233 unsigned char referenceCost; ///< reference conversions 234 unsigned char specCost; ///< Polymorphic type specializations (type assertions), negative cost 235 unsigned char varCost; ///< Count of polymorphic type variables 236 unsigned char signCost; ///< Count of safe sign conversions 237 unsigned char safeCost; ///< Safe (widening) conversions 238 unsigned char polyCost; ///< Count of parameters and return values bound to some poly type 239 unsigned char unsafeCost; ///< Unsafe (narrowing) conversions 240 #else 241 #error Cost BIG_ENDIAN unsupported 242 #endif 243 } v; 244 uint64_t all; 245 }; 246 static const unsigned char correctb = 0xff; // byte correction for negative spec cost 247 static const uint64_t correctw = 0x00'00'00'00'00'ff'00'00; //' word correction for negative spec cost 248 public: 249 // Compiler adjusts constants for correct endian. 250 enum : uint64_t { 251 zero = 0x00'00'00'00'00'ff'00'00, 252 infinity = 0xff'ff'ff'ff'ff'00'ff'ff, 253 unsafe = 0x01'00'00'00'00'ff'00'00, 254 poly = 0x00'01'00'00'00'ff'00'00, 255 safe = 0x00'00'01'00'00'ff'00'00, 256 sign = 0x00'00'00'01'00'ff'00'00, 257 var = 0x00'00'00'00'01'ff'00'00, 258 spec = 0x00'00'00'00'00'fe'00'00, 259 reference = 0x00'00'00'00'00'ff'01'00, 260 }; //' 261 262 Cost( uint64_t all ) { Cost::all = all; } 263 Cost( int unsafeCost, int polyCost, int safeCost, int signCost, int varCost, int specCost, int referenceCost ) { 264 // Assume little-endian => first value is low priority and last is high priority. 265 v = { 266 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 267 (unsigned char)0, // padding 268 (unsigned char)referenceCost, // low priority 269 (unsigned char)(specCost + correctb), // correct for signedness 270 (unsigned char)varCost, 271 (unsigned char)signCost, 272 (unsigned char)safeCost, 273 (unsigned char)polyCost, 274 (unsigned char)unsafeCost, // high priority 275 #else 276 #error Cost BIG_ENDIAN unsupported 277 #endif 278 }; 279 } 280 281 int get_unsafeCost() const { return v.unsafeCost; } 282 int get_polyCost() const { return v.polyCost; } 283 int get_safeCost() const { return v.safeCost; } 284 int get_signCost() const { return v.signCost; } 285 int get_varCost() const { return v.varCost; } 286 int get_specCost() const { return -(correctb - v.specCost); } 287 int get_referenceCost() const { return v.referenceCost; } 288 289 friend bool operator==( const Cost, const Cost ); 290 friend bool operator!=( const Cost lhs, const Cost rhs ); 291 // returns negative for *this < rhs, 0 for *this == rhs, positive for *this > rhs 292 int compare( const Cost rhs ) const { 293 if ( all == infinity ) return 1; 294 if ( rhs.all == infinity ) return -1; 295 return all > rhs.all ? 1 : all == rhs.all ? 0 : -1; 296 } 297 friend bool operator<( const Cost lhs, const Cost rhs ); 298 299 friend Cost operator+( const Cost lhs, const Cost rhs ); 300 301 Cost operator+=( const Cost rhs ) { 302 if ( all == infinity ) return *this; 303 if ( rhs.all == infinity ) { 304 all = infinity; 305 return *this; 306 } 307 all += rhs.all - correctw; // correct for negative spec cost 308 return *this; 309 } 310 311 Cost incUnsafe( int inc = 1 ) { 312 if ( all != infinity ) { assert( v.unsafeCost + inc <= UCHAR_MAX ); v.unsafeCost += inc; } 313 return *this; 314 } 315 316 Cost incPoly( int inc = 1 ) { 317 if ( all != infinity ) { assert( v.polyCost + inc <= UCHAR_MAX ); v.polyCost += inc; } 318 return *this; 319 } 320 321 Cost incSafe( int inc = 1 ) { 322 if ( all != infinity ) { assert( v.safeCost + inc <= UCHAR_MAX ); v.safeCost += inc; } 323 return *this; 324 } 325 326 Cost incSign( int inc = 1 ) { 327 if ( all != infinity ) { assert( v.signCost + inc <= UCHAR_MAX ); v.signCost += inc; } 328 return *this; 329 } 330 331 Cost incVar( int inc = 1 ) { 332 if ( all != infinity ) { assert( v.varCost + inc <= UCHAR_MAX ); v.varCost += inc; } 333 return *this; 334 } 335 336 Cost decSpec( int dec = 1 ) { 337 if ( all != infinity ) { assert( v.specCost - dec >= 0 ); v.specCost -= dec; } 338 return *this; 339 } 340 341 Cost incReference( int inc = 1 ) { 342 if ( all != infinity ) { assert( v.referenceCost + inc <= UCHAR_MAX ); v.referenceCost += inc; } 343 return *this; 344 } 345 346 friend std::ostream & operator<<( std::ostream & os, const Cost cost ); 347 }; 348 349 inline bool operator==( const Cost lhs, const Cost rhs ) { 350 return lhs.all == rhs.all; 351 } 352 353 inline bool operator!=( const Cost lhs, const Cost rhs ) { 354 return !( lhs.all == rhs.all ); 355 } 356 357 inline bool operator<( const Cost lhs, const Cost rhs ) { 358 if ( lhs.all == Cost::infinity ) return false; 359 if ( rhs.all == Cost::infinity ) return true; 360 return lhs.all < rhs.all; 361 } 362 363 inline Cost operator+( const Cost lhs, const Cost rhs ) { 364 if ( lhs.all == Cost::infinity || rhs.all == Cost::infinity ) return Cost{ Cost::infinity }; 365 return Cost{ lhs.all + rhs.all - Cost::correctw }; // correct for negative spec cost 366 } 367 368 inline std::ostream & operator<<( std::ostream & os, const Cost cost ) { 369 return os << "( " << cost.get_unsafeCost() << ", " << cost.get_polyCost() << ", " << cost.get_safeCost() 370 << ", " << cost.get_signCost() << ", " << cost.get_varCost() << ", " << cost.get_specCost() 371 << ", " << cost.get_referenceCost() << " )"; 372 } 373 #endif // 0 213 374 } // namespace ResolvExpr 214 375 -
src/ResolvExpr/ResolveAssertions.cc
r1bc5975 rec28948 35 35 #include "SynTree/Expression.h" // for InferredParams 36 36 #include "TypeEnvironment.h" // for TypeEnvironment, etc. 37 #include "typeops.h" // for adjustExprType 37 #include "typeops.h" // for adjustExprType, specCost 38 38 #include "Unify.h" // for unify 39 39 … … 58 58 using CandidateList = std::vector<AssnCandidate>; 59 59 60 /// Unique identifier for a yet-to-be-resolved assertion61 struct AssnId {62 DeclarationWithType* decl; ///< Declaration of assertion63 AssertionSetValue info; ///< Information about assertion64 65 AssnId(DeclarationWithType* decl, const AssertionSetValue& info) : decl(decl), info(info) {}66 };67 68 /// Cached assertion items69 struct AssnCacheItem {70 CandidateList matches; ///< Possible matches for this assertion71 std::vector<AssnId> deferIds; ///< Deferred assertions which resolve to this item72 73 AssnCacheItem( CandidateList&& m ) : matches(std::move(m)), deferIds() {}74 };75 76 /// Cache of resolved assertions77 using AssnCache = std::unordered_map<std::string, AssnCacheItem>;78 79 60 /// Reference to single deferred item 80 61 struct DeferRef { 81 const AssnCacheItem& item; 62 const DeclarationWithType* decl; 63 const AssertionSetValue& info; 82 64 const AssnCandidate& match; 83 65 }; … … 86 68 /// Acts like indexed list of DeferRef 87 69 struct DeferItem { 88 const AssnCache* cache; ///< Cache storing assertion item 89 std::string key; ///< Key into cache 90 91 DeferItem( const AssnCache& cache, const std::string& key ) : cache(&cache), key(key) {} 92 93 bool empty() const { return cache->at(key).matches.empty(); } 94 95 CandidateList::size_type size() const { return cache->at(key).matches.size(); } 96 97 DeferRef operator[] ( unsigned i ) const { 98 const AssnCacheItem& item = cache->at(key); 99 return { item, item.matches[i] }; 100 } 101 102 const DeclarationWithType* get_decl() const { return cache->at(key).deferIds[0].decl; } 103 104 // sortable by key 105 // TODO look into optimizing combination process with other sort orders (e.g. by number 106 // of matches in candidate) 107 bool operator< ( const DeferItem& o ) const { return key < o.key; } 108 bool operator== ( const DeferItem& o ) const { return key == o.key; } 70 const DeclarationWithType* decl; 71 const AssertionSetValue& info; 72 CandidateList matches; 73 74 DeferItem( DeclarationWithType* decl, const AssertionSetValue& info, CandidateList&& matches ) 75 : decl(decl), info(info), matches(std::move(matches)) {} 76 77 bool empty() const { return matches.empty(); } 78 79 CandidateList::size_type size() const { return matches.size(); } 80 81 DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; } 109 82 }; 110 83 … … 181 154 for ( const auto& assn : x.assns ) { 182 155 k += computeConversionCost( 183 assn.match.adjType, assn.item.deferIds[0].decl->get_type(), indexer, 184 x.env ); 156 assn.match.adjType, assn.decl->get_type(), indexer, x.env ); 157 158 // mark vars+specialization cost on function-type assertions 159 PointerType* ptr = dynamic_cast< PointerType* >( assn.decl->get_type() ); 160 if ( ! ptr ) continue; 161 FunctionType* func = dynamic_cast< FunctionType* >( ptr->base ); 162 if ( ! func ) continue; 163 164 for ( DeclarationWithType* formal : func->parameters ) { 165 k.decSpec( specCost( formal->get_type() ) ); 166 } 167 k.incVar( func->forall.size() ); 168 for ( TypeDecl* td : func->forall ) { 169 k.decSpec( td->assertions.size() ); 170 } 185 171 } 186 172 it = cache.emplace_hint( it, &x, k ); … … 253 239 254 240 /// Resolve a single assertion, in context 255 bool resolveAssertion( AssertionItem& assn, ResnState& resn , AssnCache& cache) {241 bool resolveAssertion( AssertionItem& assn, ResnState& resn ) { 256 242 // skip unused assertions 257 243 if ( ! assn.info.isUsed ) return true; 258 244 259 // check cache for this assertion 260 std::string assnKey = SymTab::Mangler::mangleAssnKey( assn.decl, resn.alt.env ); 261 auto it = cache.find( assnKey ); 262 263 // attempt to resolve assertion if this is the first time seen 264 if ( it == cache.end() ) { 265 // lookup candidates for this assertion 266 std::list< SymTab::Indexer::IdData > candidates; 267 resn.indexer.lookupId( assn.decl->name, candidates ); 268 269 // find the candidates that unify with the desired type 270 CandidateList matches; 271 for ( const auto& cdata : candidates ) { 272 DeclarationWithType* candidate = cdata.id; 273 274 // build independent unification context for candidate 275 AssertionSet have, newNeed; 276 TypeEnvironment newEnv{ resn.alt.env }; 277 OpenVarSet newOpenVars{ resn.alt.openVars }; 278 Type* adjType = candidate->get_type()->clone(); 279 adjustExprType( adjType, newEnv, resn.indexer ); 280 renameTyVars( adjType ); 281 282 // keep unifying candidates 283 if ( unify( assn.decl->get_type(), adjType, newEnv, newNeed, have, newOpenVars, 284 resn.indexer ) ) { 285 // set up binding slot for recursive assertions 286 UniqueId crntResnSlot = 0; 287 if ( ! newNeed.empty() ) { 288 crntResnSlot = ++globalResnSlot; 289 for ( auto& a : newNeed ) { 290 a.second.resnSlot = crntResnSlot; 291 } 292 } 293 294 matches.emplace_back( cdata, adjType, std::move(newEnv), std::move(have), 295 std::move(newNeed), std::move(newOpenVars), crntResnSlot ); 296 } else { 297 delete adjType; 298 } 245 // lookup candidates for this assertion 246 std::list< SymTab::Indexer::IdData > candidates; 247 resn.indexer.lookupId( assn.decl->name, candidates ); 248 249 // find the candidates that unify with the desired type 250 CandidateList matches; 251 for ( const auto& cdata : candidates ) { 252 DeclarationWithType* candidate = cdata.id; 253 254 // build independent unification context for candidate 255 AssertionSet have, newNeed; 256 TypeEnvironment newEnv{ resn.alt.env }; 257 OpenVarSet newOpenVars{ resn.alt.openVars }; 258 Type* adjType = candidate->get_type()->clone(); 259 adjustExprType( adjType, newEnv, resn.indexer ); 260 renameTyVars( adjType ); 261 262 // keep unifying candidates 263 if ( unify( assn.decl->get_type(), adjType, newEnv, newNeed, have, newOpenVars, 264 resn.indexer ) ) { 265 // set up binding slot for recursive assertions 266 UniqueId crntResnSlot = 0; 267 if ( ! newNeed.empty() ) { 268 crntResnSlot = ++globalResnSlot; 269 for ( auto& a : newNeed ) { 270 a.second.resnSlot = crntResnSlot; 271 } 272 } 273 274 matches.emplace_back( cdata, adjType, std::move(newEnv), std::move(have), 275 std::move(newNeed), std::move(newOpenVars), crntResnSlot ); 276 } else { 277 delete adjType; 299 278 } 300 301 it = cache.emplace_hint( it, assnKey, AssnCacheItem{ std::move(matches) } ); 302 } 303 304 CandidateList& matches = it->second.matches; 279 } 305 280 306 281 // break if no suitable assertion … … 309 284 // defer if too many suitable assertions 310 285 if ( matches.size() > 1 ) { 311 it->second.deferIds.emplace_back( assn.decl, assn.info ); 312 resn.deferred.emplace_back( cache, assnKey ); 286 resn.deferred.emplace_back( assn.decl, assn.info, std::move(matches) ); 313 287 return true; 314 288 } … … 318 292 addToIndexer( match.have, resn.indexer ); 319 293 resn.newNeed.insert( match.need.begin(), match.need.end() ); 320 resn.alt.env = match.env;321 resn.alt.openVars = match.openVars;294 resn.alt.env = std::move(match.env); 295 resn.alt.openVars = std::move(match.openVars); 322 296 323 297 bindAssertion( assn.decl, assn.info, resn.alt, match, resn.inferred ); … … 380 354 ResnList resns{ ResnState{ alt, root_indexer } }; 381 355 ResnList new_resns{}; 382 AssnCache assnCache;383 356 384 357 // resolve assertions in breadth-first-order up to a limited number of levels deep … … 389 362 for ( auto& assn : resn.need ) { 390 363 // fail early if any assertion is not resolvable 391 if ( ! resolveAssertion( assn, resn , assnCache) ) {364 if ( ! resolveAssertion( assn, resn ) ) { 392 365 Indenter tabs{ Indenter::tabsize, 3 }; 393 366 std::ostringstream ss; … … 410 383 } 411 384 } else { 412 // only resolve each deferred assertion once413 std::sort( resn.deferred.begin(), resn.deferred.end() );414 auto last = std::unique( resn.deferred.begin(), resn.deferred.end() );415 resn.deferred.erase( last, resn.deferred.end() );416 385 // resolve deferred assertions by mutual compatibility 417 386 std::vector<CandidateEnvMerger::OutType> compatible = filterCombos( … … 427 396 ++tabs; 428 397 for ( const auto& d : resn.deferred ) { 429 d. get_decl()->print( ss, tabs );398 d.decl->print( ss, tabs ); 430 399 } 431 400 … … 458 427 new_resn.newNeed.insert( match.need.begin(), match.need.end() ); 459 428 460 // for each deferred assertion with the same form 461 for ( AssnId id : r.item.deferIds ) { 462 bindAssertion( 463 id.decl, id.info, new_resn.alt, match, new_resn.inferred ); 464 } 429 bindAssertion( r.decl, r.info, new_resn.alt, match, new_resn.inferred ); 465 430 } 466 431 -
src/SymTab/Mangler.cc
r1bc5975 rec28948 38 38 struct Mangler : public WithShortCircuiting, public WithVisitorRef<Mangler>, public WithGuards { 39 39 Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams ); 40 Mangler( const ResolvExpr::TypeEnvironment& env );41 40 Mangler( const Mangler & ) = delete; 42 41 … … 67 66 private: 68 67 std::ostringstream mangleName; ///< Mangled name being constructed 69 typedef std::map< std::string, std::pair< std::string, int > > VarMapType;68 typedef std::map< std::string, std::pair< int, int > > VarMapType; 70 69 VarMapType varNums; ///< Map of type variables to indices 71 70 int nextVarNum; ///< Next type variable index 72 const ResolvExpr::TypeEnvironment* env; ///< optional environment for substitutions73 71 bool isTopLevel; ///< Is the Mangler at the top level 74 72 bool mangleOverridable; ///< Specially mangle overridable built-in methods … … 80 78 public: 81 79 Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 82 int nextVarNum, const ResolvExpr::TypeEnvironment* env, 83 const VarMapType& varNums ); 80 int nextVarNum, const VarMapType& varNums ); 84 81 85 82 private: … … 109 106 } 110 107 111 std::string mangleAssnKey( DeclarationWithType* decl,112 const ResolvExpr::TypeEnvironment& env ) {113 PassVisitor<Mangler> mangler( env );114 maybeAccept( decl, mangler );115 return mangler.pass.get_mangleName();116 }117 118 108 namespace { 119 109 Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams ) 120 : nextVarNum( 0 ), env(nullptr),isTopLevel( true ),110 : nextVarNum( 0 ), isTopLevel( true ), 121 111 mangleOverridable( mangleOverridable ), typeMode( typeMode ), 122 112 mangleGenericParams( mangleGenericParams ) {} 123 113 124 Mangler::Mangler( const ResolvExpr::TypeEnvironment& env )125 : nextVarNum( 0 ), env( &env ), isTopLevel( true ), mangleOverridable( false ),126 typeMode( false ), mangleGenericParams( true ) {}127 128 114 Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 129 int nextVarNum, const ResolvExpr::TypeEnvironment* env, 130 const VarMapType& varNums ) 131 : varNums( varNums ), nextVarNum( nextVarNum ), env( env ), isTopLevel( false ), 115 int nextVarNum, const VarMapType& varNums ) 116 : varNums( varNums ), nextVarNum( nextVarNum ), isTopLevel( false ), 132 117 mangleOverridable( mangleOverridable ), typeMode( typeMode ), 133 118 mangleGenericParams( mangleGenericParams ) {} … … 358 343 assert( false ); 359 344 } // switch 360 std::string varName; 361 // replace type with substitution name if environment is available and bound 362 if ( env ) { 363 const ResolvExpr::EqvClass* varClass = env->lookup( (*i)->name ); 364 if ( varClass && varClass->type ) { 365 PassVisitor<Mangler> sub_mangler( 366 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, 367 env, varNums ); 368 varClass->type->accept( sub_mangler ); 369 varName = std::string{"%"} + sub_mangler.pass.get_mangleName(); 370 } 371 } 372 // otherwise just give type numeric name 373 if ( varName.empty() ) { 374 varName = std::to_string( nextVarNum++ ); 375 } 376 varNums[ (*i)->name ] = std::make_pair( varName, (int)(*i)->get_kind() ); 345 varNums[ (*i)->name ] = std::make_pair( nextVarNum, (int)(*i)->get_kind() ); 377 346 for ( std::list< DeclarationWithType* >::iterator assert = (*i)->assertions.begin(); assert != (*i)->assertions.end(); ++assert ) { 378 347 PassVisitor<Mangler> sub_mangler( 379 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, env, 380 varNums ); 348 mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ); 381 349 (*assert)->accept( sub_mangler ); 382 350 assertionNames.push_back( sub_mangler.pass.get_mangleName() ); -
src/SymTab/Mangler.h
r1bc5975 rec28948 44 44 /// Mangle ignoring generic type parameters 45 45 std::string mangleConcrete( Type* ty ); 46 /// Mangle for assertion key47 std::string mangleAssnKey( DeclarationWithType* decl,48 const ResolvExpr::TypeEnvironment& env );49 46 50 47 namespace Encoding {
Note: See TracChangeset
for help on using the changeset viewer.