Changeset e580aa5


Ignore:
Timestamp:
Nov 16, 2023, 3:31:57 PM (13 months ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
master
Children:
b0845f9
Parents:
3f4f30a
Message:

Round of clean-up in Tuples directory. (Skipping TupleExpansion?, which will be fused together later.)

Location:
src/Tuples
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/Tuples/TupleAssignment.cc

    r3f4f30a re580aa5  
    4646
    4747namespace {
    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.
     50bool 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.
     57bool 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 );
    5364        }
    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.
     68class 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;
    115114                                }
    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
     242public:
     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 ) );
    133270                                }
    134271
    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 ) );
    317281                                        }
    318282                                }
    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                                        }
    350318                                }
    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
    373375} // anonymous namespace
    374376
     
    377379        std::vector< ResolvExpr::CandidateFinder > & args
    378380) {
    379         TupleAssignSpotter spotter{ finder };
     381        TupleAssignSpotter spotter( finder );
    380382        spotter.spot( assign, args );
    381383}
  • src/Tuples/Tuples.cc

    r3f4f30a re580aa5  
    2525namespace {
    2626
    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     struct ImpurityDetector : public ast::WithShortCircuiting {
    31                 bool result = false;
     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.
     30struct ImpurityDetector : public ast::WithShortCircuiting {
     31        bool result = false;
    3232
    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;
    3938                        }
    40                         result = true; visit_children = false;
    4139                }
    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};
    4646
    47         struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
    48                 using ImpurityDetector::previsit;
    49                 void previsit( ast::UniqueExpr const * ) {
    50                         visit_children = false;
    51                 }
    52         };
     47struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
     48        using ImpurityDetector::previsit;
     49        void previsit( ast::UniqueExpr const * ) {
     50                visit_children = false;
     51        }
     52};
     53
    5354} // namespace
    5455
  • src/Tuples/Tuples.h

    r3f4f30a re580aa5  
    2424
    2525namespace Tuples {
    26         // TupleAssignment.cc
    27         void handleTupleAssignment(
    28                 ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
    29                 std::vector< ResolvExpr::CandidateFinder > & args );
    3026
    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
     28void handleTupleAssignment(
     29        ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
     30        std::vector< ResolvExpr::CandidateFinder > & args );
    3431
    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.
     34void expandMemberTuples( ast::TranslationUnit & translationUnit );
    3735
    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.
     37void expandTuples( ast::TranslationUnit & translaionUnit );
    4038
    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.
     40void expandUniqueExpr( ast::TranslationUnit & translationUnit );
    4341
    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.
     43const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs );
    4644
    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
     46const ast::TypeInstType * isTtype( const ast::Type * type );
    4947
    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.
     49bool maybeImpure( const ast::Expr * expr );
     50
     51/// Returns true if the expression may contain side-effect,
     52/// ignoring the presence of unique expressions.
     53bool maybeImpureIgnoreUnique( const ast::Expr * expr );
     54
    5355} // namespace Tuples
    5456
Note: See TracChangeset for help on using the changeset viewer.