Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Tuples/TupleAssignment.cc

    re580aa5 r0bd3faf  
    4646
    4747namespace {
    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 );
     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() );
    6453        }
    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() );
     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 );
    8463                }
    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 );
     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;
     115                                }
     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 );
     133                                }
     134
     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                                                }
     317                                        }
     318                                }
     319                        }
    94320                }
    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;
    114                                 }
    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                         }
     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;
     350                                }
     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 ) );
    158371                }
    159372        };
    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 ) );
    270                                 }
    271 
    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 ) );
    281                                         }
    282                                 }
    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                                         }
    318                                 }
    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 
    375373} // anonymous namespace
    376374
     
    379377        std::vector< ResolvExpr::CandidateFinder > & args
    380378) {
    381         TupleAssignSpotter spotter( finder );
     379        TupleAssignSpotter spotter{ finder };
    382380        spotter.spot( assign, args );
    383381}
Note: See TracChangeset for help on using the changeset viewer.