Changeset e580aa5
- Timestamp:
- Nov 16, 2023, 3:31:57 PM (13 months ago)
- Branches:
- master
- Children:
- b0845f9
- Parents:
- 3f4f30a
- Location:
- src/Tuples
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Tuples/TupleAssignment.cc
r3f4f30a re580aa5 46 46 47 47 namespace { 48 /// true if `expr` is of tuple type 49 bool isTuple( const ast::Expr * expr ) { 50 if ( ! expr ) return false; 51 assert( expr->result ); 52 return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() ); 48 49 /// Checks if `expr` is of tuple type. 50 bool isTuple( const ast::Expr * expr ) { 51 if ( !expr ) return false; 52 assert( expr->result ); 53 return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() ); 54 } 55 56 /// Checks if `expr` is of tuple type or a cast to one. 57 bool refToTuple( const ast::Expr * expr ) { 58 assert( expr->result ); 59 // Check for function returning tuple of reference types. 60 if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) { 61 return refToTuple( castExpr->arg ); 62 } else { 63 return isTuple( expr ); 53 64 } 54 55 /// true if `expr` is of tuple type or a reference to one 56 bool refToTuple( const ast::Expr * expr ) { 57 assert( expr->result ); 58 // check for function returning tuple of reference types 59 if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) { 60 return refToTuple( castExpr->arg ); 61 } else { 62 return isTuple( expr ); 63 } 64 } 65 66 /// Dispatcher for tuple (multiple and mass) assignment operations 67 class TupleAssignSpotter final { 68 /// Actually finds tuple assignment operations, by subclass 69 struct Matcher { 70 ResolvExpr::CandidateList lhs, rhs; 71 TupleAssignSpotter & spotter; 72 CodeLocation location; 73 ResolvExpr::Cost baseCost; 74 std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls; 75 ast::TypeEnvironment env; 76 ast::OpenVarSet open; 77 ast::AssertionSet need; 78 79 void combineState( const ResolvExpr::Candidate & cand ) { 80 env.simpleCombine( cand.env ); 81 ast::mergeOpenVars( open, cand.open ); 82 need.insert( cand.need.begin(), cand.need.end() ); 83 } 84 85 Matcher( 86 TupleAssignSpotter & s, const CodeLocation & loc, 87 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 88 : lhs( l ), rhs( r ), spotter( s ), location( loc ), 89 baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(), 90 env(), open(), need() { 91 for ( auto & cand : lhs ) combineState( *cand ); 92 for ( auto & cand : rhs ) combineState( *cand ); 93 } 94 virtual ~Matcher() = default; 95 96 virtual std::vector< ast::ptr< ast::Expr > > match() = 0; 97 98 /// removes environments from subexpressions within statement expressions, which could 99 /// throw off later passes like those in Box which rely on PolyMutator, and adds the 100 /// bindings to the env 101 struct EnvRemover { 102 /// environment to hoist ExprStmt environments to 103 ast::TypeEnvironment & tenv; 104 105 EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {} 106 107 const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) { 108 if ( stmt->expr->env ) { 109 tenv.add( *stmt->expr->env ); 110 ast::ExprStmt * mut = mutate( stmt ); 111 mut->expr.get_and_mutate()->env = nullptr; 112 return mut; 113 } 114 return stmt; 65 } 66 67 /// Dispatcher for tuple (multiple and mass) assignment operations. 68 class TupleAssignSpotter final { 69 /// Actually finds tuple assignment operations, by subclass. 70 struct Matcher { 71 ResolvExpr::CandidateList lhs, rhs; 72 TupleAssignSpotter & spotter; 73 CodeLocation location; 74 ResolvExpr::Cost baseCost; 75 std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls; 76 ast::TypeEnvironment env; 77 ast::OpenVarSet open; 78 ast::AssertionSet need; 79 80 void combineState( const ResolvExpr::Candidate & cand ) { 81 env.simpleCombine( cand.env ); 82 ast::mergeOpenVars( open, cand.open ); 83 need.insert( cand.need.begin(), cand.need.end() ); 84 } 85 86 Matcher( 87 TupleAssignSpotter & s, const CodeLocation & loc, 88 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 89 : lhs( l ), rhs( r ), spotter( s ), location( loc ), 90 baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(), 91 env(), open(), need() { 92 for ( auto & cand : lhs ) combineState( *cand ); 93 for ( auto & cand : rhs ) combineState( *cand ); 94 } 95 virtual ~Matcher() = default; 96 97 virtual std::vector< ast::ptr< ast::Expr > > match() = 0; 98 99 /// Removes environments from subexpressions within statement expressions, which could 100 /// throw off later passes like those in Box which rely on PolyMutator, and adds the 101 /// bindings to the env. 102 struct EnvRemover { 103 /// Environment to hoist ExprStmt environments to. 104 ast::TypeEnvironment & tenv; 105 106 EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {} 107 108 const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) { 109 if ( stmt->expr->env ) { 110 tenv.add( *stmt->expr->env ); 111 ast::ExprStmt * mut = mutate( stmt ); 112 mut->expr.get_and_mutate()->env = nullptr; 113 return mut; 115 114 } 116 }; 117 118 ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) { 119 assert( expr->result && ! expr->result->isVoid() ); 120 121 ast::ObjectDecl * ret = new ast::ObjectDecl{ 122 location, namer.newName(), expr->result, new ast::SingleInit{ location, expr }, 123 ast::Storage::Classes{}, ast::Linkage::Cforall }; 124 125 // if expression type is a reference, just need an initializer, otherwise construct 126 if ( ! expr->result.as< ast::ReferenceType >() ) { 127 // resolve ctor/dtor for the new object 128 ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit( 129 InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context ); 130 // remove environments from subexpressions of stmtExpr 131 ast::Pass< EnvRemover > rm{ env }; 132 ret->init = ctorInit->accept( rm ); 115 return stmt; 116 } 117 }; 118 119 ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) { 120 assert( expr->result && !expr->result->isVoid() ); 121 122 ast::ObjectDecl * ret = new ast::ObjectDecl( 123 location, namer.newName(), expr->result, new ast::SingleInit( location, expr ), 124 ast::Storage::Classes{}, ast::Linkage::Cforall ); 125 126 // If expression type is a reference, just need an initializer, otherwise construct. 127 if ( ! expr->result.as< ast::ReferenceType >() ) { 128 // Resolve ctor/dtor for the new object. 129 ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit( 130 InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context ); 131 // Remove environments from subexpressions of stmtExpr. 132 ast::Pass< EnvRemover > rm( env ); 133 ret->init = ctorInit->accept( rm ); 134 } 135 136 PRINT( std::cerr << "new object: " << ret << std::endl; ) 137 return ret; 138 } 139 140 ast::UntypedExpr * createFunc( 141 const std::string & fname, const ast::ObjectDecl * left, 142 const ast::ObjectDecl * right 143 ) { 144 assert( left ); 145 std::vector< ast::ptr< ast::Expr > > args; 146 args.emplace_back( new ast::VariableExpr( location, left ) ); 147 if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); } 148 149 if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) { 150 args.front() = new ast::AddressExpr( location, args.front() ); 151 if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); } 152 return new ast::UntypedExpr( 153 location, new ast::NameExpr( location, "?=?" ), std::move( args ) ); 154 } else { 155 return new ast::UntypedExpr( 156 location, new ast::NameExpr( location, fname ), std::move( args ) ); 157 } 158 } 159 }; 160 161 /// Finds mass-assignment operations. 162 struct MassAssignMatcher final : public Matcher { 163 MassAssignMatcher( 164 TupleAssignSpotter & s, const CodeLocation & loc, 165 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 166 : Matcher( s, loc, l, r ) {} 167 168 std::vector< ast::ptr< ast::Expr > > match() override { 169 static UniqueName lhsNamer( "__massassign_L" ); 170 static UniqueName rhsNamer( "__massassign_R" ); 171 // Empty tuple case falls into this matcher. 172 assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 ); 173 174 ast::ptr< ast::ObjectDecl > rtmp = 175 1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr; 176 177 std::vector< ast::ptr< ast::Expr > > out; 178 for ( ResolvExpr::CandidateRef & lhsCand : lhs ) { 179 // Create a temporary object for each value in 180 // the LHS and create a call involving the RHS. 181 ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr ); 182 out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) ); 183 tmpDecls.emplace_back( std::move( ltmp ) ); 184 } 185 if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) ); 186 187 return out; 188 } 189 }; 190 191 /// Finds multiple-assignment operations. 192 struct MultipleAssignMatcher final : public Matcher { 193 MultipleAssignMatcher( 194 TupleAssignSpotter & s, const CodeLocation & loc, 195 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 196 : Matcher( s, loc, l, r ) {} 197 198 std::vector< ast::ptr< ast::Expr > > match() override { 199 static UniqueName lhsNamer( "__multassign_L" ); 200 static UniqueName rhsNamer( "__multassign_R" ); 201 202 if ( lhs.size() != rhs.size() ) return {}; 203 204 // Produce a new temporary object for each value in 205 // the LHS and RHS and pairwise create the calls. 206 std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp; 207 208 std::vector< ast::ptr< ast::Expr > > out; 209 for ( unsigned i = 0; i < lhs.size(); ++i ) { 210 ResolvExpr::CandidateRef & lhsCand = lhs[i]; 211 ResolvExpr::CandidateRef & rhsCand = rhs[i]; 212 213 // Convert RHS to LHS type minus one reference -- 214 // important for case where LHS is && and RHS is lvalue. 215 auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >(); 216 rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base ); 217 ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr ); 218 ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr ); 219 out.emplace_back( createFunc( spotter.fname, lobj, robj ) ); 220 ltmp.emplace_back( std::move( lobj ) ); 221 rtmp.emplace_back( std::move( robj ) ); 222 223 // Resolve the cast expression so that rhsCand return type is bound 224 // by the cast type as needed, and transfer the resulting environment. 225 ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env ); 226 finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() ); 227 assert( 1 == finder.candidates.size() ); 228 env = std::move( finder.candidates.front()->env ); 229 } 230 231 splice( tmpDecls, ltmp ); 232 splice( tmpDecls, rtmp ); 233 234 return out; 235 } 236 }; 237 238 ResolvExpr::CandidateFinder & crntFinder; 239 std::string fname; 240 std::unique_ptr< Matcher > matcher; 241 242 public: 243 TupleAssignSpotter( ResolvExpr::CandidateFinder & f ) 244 : crntFinder( f ), fname(), matcher() {} 245 246 // Find left- and right-hand-sides for mass or multiple assignment. 247 void spot( 248 const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args 249 ) { 250 if ( auto op = expr->func.as< ast::NameExpr >() ) { 251 // Skip non-assignment functions. 252 if ( !CodeGen::isCtorDtorAssign( op->name ) ) return; 253 fname = op->name; 254 255 // Handled by CandidateFinder if applicable (both odd cases). 256 if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) { 257 return; 258 } 259 260 // Look over all possible left-hand-side. 261 for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) { 262 // Skip non-tuple LHS. 263 if ( !refToTuple( lhsCand->expr ) ) continue; 264 265 // Explode is aware of casts - ensure every LHS 266 // is sent into explode with a reference cast. 267 if ( !lhsCand->expr.as< ast::CastExpr >() ) { 268 lhsCand->expr = new ast::CastExpr( 269 lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) ); 133 270 } 134 271 135 PRINT( std::cerr << "new object: " << ret << std::endl; ) 136 return ret; 137 } 138 139 ast::UntypedExpr * createFunc( 140 const std::string & fname, const ast::ObjectDecl * left, 141 const ast::ObjectDecl * right 142 ) { 143 assert( left ); 144 std::vector< ast::ptr< ast::Expr > > args; 145 args.emplace_back( new ast::VariableExpr{ location, left } ); 146 if ( right ) { args.emplace_back( new ast::VariableExpr{ location, right } ); } 147 148 if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) { 149 args.front() = new ast::AddressExpr{ location, args.front() }; 150 if ( right ) { args.back() = new ast::AddressExpr{ location, args.back() }; } 151 return new ast::UntypedExpr{ 152 location, new ast::NameExpr{ location, "?=?" }, std::move(args) }; 153 } else { 154 return new ast::UntypedExpr{ 155 location, new ast::NameExpr{ location, fname }, std::move(args) }; 156 } 157 } 158 }; 159 160 /// Finds mass-assignment operations 161 struct MassAssignMatcher final : public Matcher { 162 MassAssignMatcher( 163 TupleAssignSpotter & s, const CodeLocation & loc, 164 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 165 : Matcher( s, loc, l, r ) {} 166 167 std::vector< ast::ptr< ast::Expr > > match() override { 168 static UniqueName lhsNamer( "__massassign_L" ); 169 static UniqueName rhsNamer( "__massassign_R" ); 170 // empty tuple case falls into this matcher 171 assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 ); 172 173 ast::ptr< ast::ObjectDecl > rtmp = 174 rhs.size() == 1 ? newObject( rhsNamer, rhs.front()->expr ) : nullptr; 175 176 std::vector< ast::ptr< ast::Expr > > out; 177 for ( ResolvExpr::CandidateRef & lhsCand : lhs ) { 178 // create a temporary object for each value in the LHS and create a call 179 // involving the RHS 180 ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr ); 181 out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) ); 182 tmpDecls.emplace_back( std::move( ltmp ) ); 183 } 184 if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) ); 185 186 return out; 187 } 188 }; 189 190 /// Finds multiple-assignment operations 191 struct MultipleAssignMatcher final : public Matcher { 192 MultipleAssignMatcher( 193 TupleAssignSpotter & s, const CodeLocation & loc, 194 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r ) 195 : Matcher( s, loc, l, r ) {} 196 197 std::vector< ast::ptr< ast::Expr > > match() override { 198 static UniqueName lhsNamer( "__multassign_L" ); 199 static UniqueName rhsNamer( "__multassign_R" ); 200 201 if ( lhs.size() != rhs.size() ) return {}; 202 203 // produce a new temporary object for each value in the LHS and RHS and pairwise 204 // create the calls 205 std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp; 206 207 std::vector< ast::ptr< ast::Expr > > out; 208 for ( unsigned i = 0; i < lhs.size(); ++i ) { 209 ResolvExpr::CandidateRef & lhsCand = lhs[i]; 210 ResolvExpr::CandidateRef & rhsCand = rhs[i]; 211 212 // convert RHS to LHS type minus one reference -- important for case where LHS 213 // is && and RHS is lvalue 214 auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >(); 215 rhsCand->expr = new ast::CastExpr{ rhsCand->expr, lhsType->base }; 216 ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr ); 217 ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr ); 218 out.emplace_back( createFunc( spotter.fname, lobj, robj ) ); 219 ltmp.emplace_back( std::move( lobj ) ); 220 rtmp.emplace_back( std::move( robj ) ); 221 222 // resolve the cast expression so that rhsCand return type is bound by the cast 223 // type as needed, and transfer the resulting environment 224 ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env ); 225 finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() ); 226 assert( finder.candidates.size() == 1 ); 227 env = std::move( finder.candidates.front()->env ); 228 } 229 230 splice( tmpDecls, ltmp ); 231 splice( tmpDecls, rtmp ); 232 233 return out; 234 } 235 }; 236 237 ResolvExpr::CandidateFinder & crntFinder; 238 std::string fname; 239 std::unique_ptr< Matcher > matcher; 240 241 public: 242 TupleAssignSpotter( ResolvExpr::CandidateFinder & f ) 243 : crntFinder( f ), fname(), matcher() {} 244 245 // find left- and right-hand-sides for mass or multiple assignment 246 void spot( 247 const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args 248 ) { 249 if ( auto op = expr->func.as< ast::NameExpr >() ) { 250 // skip non-assignment functions 251 if ( ! CodeGen::isCtorDtorAssign( op->name ) ) return; 252 fname = op->name; 253 254 // handled by CandidateFinder if applicable (both odd cases) 255 if ( args.empty() || ( args.size() == 1 && CodeGen::isAssignment( fname ) ) ) { 256 return; 257 } 258 259 // look over all possible left-hand-side 260 for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) { 261 // skip non-tuple LHS 262 if ( ! refToTuple( lhsCand->expr ) ) continue; 263 264 // explode is aware of casts - ensure every LHS is sent into explode with a 265 // reference cast 266 if ( ! lhsCand->expr.as< ast::CastExpr >() ) { 267 lhsCand->expr = new ast::CastExpr{ 268 lhsCand->expr, new ast::ReferenceType{ lhsCand->expr->result } }; 269 } 270 271 // explode the LHS so that each field of a tuple-valued expr is assigned 272 ResolvExpr::CandidateList lhs; 273 explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true ); 274 for ( ResolvExpr::CandidateRef & cand : lhs ) { 275 // each LHS value must be a reference - some come in with a cast, if not 276 // just cast to reference here 277 if ( ! cand->expr->result.as< ast::ReferenceType >() ) { 278 cand->expr = new ast::CastExpr{ 279 cand->expr, new ast::ReferenceType{ cand->expr->result } }; 280 } 281 } 282 283 if ( args.size() == 1 ) { 284 // mass default-initialization/destruction 285 ResolvExpr::CandidateList rhs{}; 286 matcher.reset( new MassAssignMatcher{ *this, expr->location, lhs, rhs } ); 287 match(); 288 } else if ( args.size() == 2 ) { 289 for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) { 290 ResolvExpr::CandidateList rhs; 291 if ( isTuple( rhsCand->expr ) ) { 292 // multiple assignment 293 explode( *rhsCand, crntFinder.context.symtab, back_inserter(rhs), true ); 294 matcher.reset( 295 new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } ); 296 } else { 297 // mass assignment 298 rhs.emplace_back( rhsCand ); 299 matcher.reset( 300 new MassAssignMatcher{ *this, expr->location, lhs, rhs } ); 301 } 302 match(); 303 } 304 } else { 305 // expand all possible RHS possibilities 306 std::vector< ResolvExpr::CandidateList > rhsCands; 307 combos( 308 std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) ); 309 for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) { 310 // multiple assignment 311 ResolvExpr::CandidateList rhs; 312 explode( rhsCand, crntFinder.context.symtab, back_inserter(rhs), true ); 313 matcher.reset( 314 new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } ); 315 match(); 316 } 272 // Explode the LHS so that each field of a tuple-valued expr is assigned. 273 ResolvExpr::CandidateList lhs; 274 explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true ); 275 for ( ResolvExpr::CandidateRef & cand : lhs ) { 276 // Each LHS value must be a reference - some come in 277 // with a cast, if not just cast to reference here. 278 if ( !cand->expr->result.as< ast::ReferenceType >() ) { 279 cand->expr = new ast::CastExpr( 280 cand->expr, new ast::ReferenceType( cand->expr->result ) ); 317 281 } 318 282 } 319 } 320 } 321 322 void match() { 323 assert( matcher ); 324 325 std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match(); 326 327 if ( ! ( matcher->lhs.empty() && matcher->rhs.empty() ) ) { 328 // if both LHS and RHS are empty than this is the empty tuple case, wherein it's 329 // okay for newAssigns to be empty. Otherwise, return early so that no new 330 // candidates are generated 331 if ( newAssigns.empty() ) return; 332 } 333 334 ResolvExpr::CandidateList crnt; 335 // now resolve new assignments 336 for ( const ast::Expr * expr : newAssigns ) { 337 PRINT( 338 std::cerr << "== resolving tuple assign ==" << std::endl; 339 std::cerr << expr << std::endl; 340 ) 341 342 ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env ); 343 finder.allowVoid = true; 344 345 try { 346 finder.find( expr, ResolvExpr::ResolvMode::withAdjustment() ); 347 } catch (...) { 348 // no match is not failure, just that this tuple assignment is invalid 349 return; 283 284 if ( 1 == args.size() ) { 285 // Mass default-initialization/destruction. 286 ResolvExpr::CandidateList rhs{}; 287 matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) ); 288 match(); 289 } else if ( 2 == args.size() ) { 290 for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) { 291 ResolvExpr::CandidateList rhs; 292 if ( isTuple( rhsCand->expr ) ) { 293 // Multiple assignment: 294 explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true ); 295 matcher.reset( 296 new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) ); 297 } else { 298 // Mass assignment: 299 rhs.emplace_back( rhsCand ); 300 matcher.reset( 301 new MassAssignMatcher( *this, expr->location, lhs, rhs ) ); 302 } 303 match(); 304 } 305 } else { 306 // Expand all possible RHS possibilities. 307 std::vector< ResolvExpr::CandidateList > rhsCands; 308 combos( 309 std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) ); 310 for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) { 311 // Multiple assignment: 312 ResolvExpr::CandidateList rhs; 313 explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true ); 314 matcher.reset( 315 new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) ); 316 match(); 317 } 350 318 } 351 352 ResolvExpr::CandidateList & cands = finder.candidates; 353 assert( cands.size() == 1 ); 354 assert( cands.front()->expr ); 355 crnt.emplace_back( std::move( cands.front() ) ); 356 } 357 358 // extract expressions from the assignment candidates to produce a list of assignments 359 // that together form a sigle candidate 360 std::vector< ast::ptr< ast::Expr > > solved; 361 for ( ResolvExpr::CandidateRef & cand : crnt ) { 362 solved.emplace_back( cand->expr ); 363 matcher->combineState( *cand ); 364 } 365 366 crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >( 367 new ast::TupleAssignExpr{ 368 matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) }, 369 std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ), 370 ResolvExpr::sumCost( crnt ) + matcher->baseCost ) ); 371 } 372 }; 319 } 320 } 321 } 322 323 void match() { 324 assert( matcher ); 325 326 std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match(); 327 328 if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) { 329 // If both LHS and RHS are empty than this is the empty tuple 330 // case, wherein it's okay for newAssigns to be empty. Otherwise, 331 // return early so that no new candidates are generated. 332 if ( newAssigns.empty() ) return; 333 } 334 335 ResolvExpr::CandidateList crnt; 336 // Now resolve new assignments. 337 for ( const ast::Expr * expr : newAssigns ) { 338 PRINT( 339 std::cerr << "== resolving tuple assign ==" << std::endl; 340 std::cerr << expr << std::endl; 341 ) 342 343 ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env ); 344 finder.allowVoid = true; 345 346 try { 347 finder.find( expr, ResolvExpr::ResolvMode::withAdjustment() ); 348 } catch (...) { 349 // No match is not failure, just that this tuple assignment is invalid. 350 return; 351 } 352 353 ResolvExpr::CandidateList & cands = finder.candidates; 354 assert( 1 == cands.size() ); 355 assert( cands.front()->expr ); 356 crnt.emplace_back( std::move( cands.front() ) ); 357 } 358 359 // extract expressions from the assignment candidates to produce a list of assignments 360 // that together form a sigle candidate 361 std::vector< ast::ptr< ast::Expr > > solved; 362 for ( ResolvExpr::CandidateRef & cand : crnt ) { 363 solved.emplace_back( cand->expr ); 364 matcher->combineState( *cand ); 365 } 366 367 crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >( 368 new ast::TupleAssignExpr( 369 matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ), 370 std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ), 371 ResolvExpr::sumCost( crnt ) + matcher->baseCost ) ); 372 } 373 }; 374 373 375 } // anonymous namespace 374 376 … … 377 379 std::vector< ResolvExpr::CandidateFinder > & args 378 380 ) { 379 TupleAssignSpotter spotter { finder };381 TupleAssignSpotter spotter( finder ); 380 382 spotter.spot( assign, args ); 381 383 } -
src/Tuples/Tuples.cc
r3f4f30a re580aa5 25 25 namespace { 26 26 27 /// Determines if impurity (read: side-effects) may exist in a piece of code. Currently gives 28 /// a very crude approximation, wherein any function call expression means the code may be 29 ///impure.30 31 27 /// Determines if impurity (read: side-effects) may exist in a piece of code. 28 /// Currently gives a very crude approximation, wherein almost any function 29 /// call expression means the code may be impure. 30 struct ImpurityDetector : public ast::WithShortCircuiting { 31 bool result = false; 32 32 33 void previsit( ast::ApplicationExpr const * appExpr ) { 34 if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) { 35 if ( function->linkage == ast::Linkage::Intrinsic 36 && ( function->name == "*?" || function->name == "?[?]" ) ) { 37 return; 38 } 33 void previsit( ast::ApplicationExpr const * appExpr ) { 34 if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) { 35 if ( function->linkage == ast::Linkage::Intrinsic 36 && ( function->name == "*?" || function->name == "?[?]" ) ) { 37 return; 39 38 } 40 result = true; visit_children = false;41 39 } 42 void previsit( ast::UntypedExpr const * ) { 43 result = true; visit_children = false; 44 } 45 }; 40 result = true; visit_children = false; 41 } 42 void previsit( ast::UntypedExpr const * ) { 43 result = true; visit_children = false; 44 } 45 }; 46 46 47 struct ImpurityDetectorIgnoreUnique : public ImpurityDetector { 48 using ImpurityDetector::previsit; 49 void previsit( ast::UniqueExpr const * ) { 50 visit_children = false; 51 } 52 }; 47 struct ImpurityDetectorIgnoreUnique : public ImpurityDetector { 48 using ImpurityDetector::previsit; 49 void previsit( ast::UniqueExpr const * ) { 50 visit_children = false; 51 } 52 }; 53 53 54 } // namespace 54 55 -
src/Tuples/Tuples.h
r3f4f30a re580aa5 24 24 25 25 namespace Tuples { 26 // TupleAssignment.cc27 void handleTupleAssignment(28 ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,29 std::vector< ResolvExpr::CandidateFinder > & args );30 26 31 // TupleExpansion.cc 32 /// expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate 33 void expandMemberTuples( ast::TranslationUnit & translationUnit ); 27 // TupleAssignment.cc 28 void handleTupleAssignment( 29 ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign, 30 std::vector< ResolvExpr::CandidateFinder > & args ); 34 31 35 /// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc. 36 void expandTuples( ast::TranslationUnit & translaionUnit ); 32 // TupleExpansion.cc 33 /// Expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate. 34 void expandMemberTuples( ast::TranslationUnit & translationUnit ); 37 35 38 /// replaces UniqueExprs with a temporary variable and one call 39 void expandUniqueExpr( ast::TranslationUnit & translationUnit );36 /// Replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc. 37 void expandTuples( ast::TranslationUnit & translaionUnit ); 40 38 41 /// returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types 42 const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs);39 /// Replaces UniqueExprs with a temporary variable and one call. 40 void expandUniqueExpr( ast::TranslationUnit & translationUnit ); 43 41 44 /// returns a TypeInstType if `type` is a ttype, nullptr otherwise 45 const ast::TypeInstType * isTtype( const ast::Type * type);42 /// Returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types. 43 const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs ); 46 44 47 /// returns true if the expression may contain side-effects. 48 bool maybeImpure( const ast::Expr * expr);45 /// Returns a TypeInstType if `type` is a ttype, nullptr otherwise 46 const ast::TypeInstType * isTtype( const ast::Type * type ); 49 47 50 /// Returns true if the expression may contain side-effect, 51 /// ignoring the presence of unique expressions. 52 bool maybeImpureIgnoreUnique( const ast::Expr * expr ); 48 /// Returns true if the expression may contain side-effects. 49 bool maybeImpure( const ast::Expr * expr ); 50 51 /// Returns true if the expression may contain side-effect, 52 /// ignoring the presence of unique expressions. 53 bool maybeImpureIgnoreUnique( const ast::Expr * expr ); 54 53 55 } // namespace Tuples 54 56
Note: See TracChangeset
for help on using the changeset viewer.