Ignore:
Timestamp:
Jul 12, 2019, 10:49:02 AM (5 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
ef5b828
Parents:
ee6dbae
Message:

Cast cost and conversion cost now take constant parameters.
This required supporting visiting const node.
The PassVisitor? can now visit const nodes but not when using the Indexer

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/ResolvExpr/Unify.cc

    ree6dbae r7870799  
    9797        bool unifyExact( Type *type1, Type *type2, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widen, const SymTab::Indexer &indexer );
    9898
    99         bool unifyExact( 
    100                 const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 
    101                 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
     99        bool unifyExact(
     100                const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
     101                ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
    102102                WidenMode widen, const ast::SymbolTable & symtab );
    103103
     
    121121        }
    122122
    123         bool typesCompatible( 
    124                         const ast::Type * first, const ast::Type * second, const ast::SymbolTable & symtab, 
     123        bool typesCompatible(
     124                        const ast::Type * first, const ast::Type * second, const ast::SymbolTable & symtab,
    125125                        const ast::TypeEnvironment & env ) {
    126126                ast::TypeEnvironment newEnv;
     
    135135                findOpenVars( newSecond, open, closed, need, have, FirstOpen );
    136136
    137                 return unifyExact( 
     137                return unifyExact(
    138138                        newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
    139139        }
    140140
    141         bool typesCompatibleIgnoreQualifiers( Type *first, Type *second, const SymTab::Indexer &indexer, const TypeEnvironment &env ) {
     141        bool typesCompatibleIgnoreQualifiers( const Type * first, const Type * second, const SymTab::Indexer &indexer, const TypeEnvironment &env ) {
    142142                TypeEnvironment newEnv;
    143143                OpenVarSet openVars;
     
    163163        }
    164164
    165         bool typesCompatibleIgnoreQualifiers( 
    166                         const ast::Type * first, const ast::Type * second, const ast::SymbolTable & symtab, 
     165        bool typesCompatibleIgnoreQualifiers(
     166                        const ast::Type * first, const ast::Type * second, const ast::SymbolTable & symtab,
    167167                        const ast::TypeEnvironment & env ) {
    168168                ast::TypeEnvironment newEnv;
    169169                ast::OpenVarSet open;
    170170                ast::AssertionSet need, have;
    171                
     171
    172172                ast::ptr<ast::Type> newFirst{ first }, newSecond{ second };
    173173                env.apply( newFirst );
     
    176176                reset_qualifiers( newSecond );
    177177
    178                 return unifyExact( 
     178                return unifyExact(
    179179                        newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
    180180        }
     
    490490
    491491                        // sizes don't have to match if ttypes are involved; need to be more precise wrt where the ttype is to prevent errors
    492                         if ( 
    493                                         (flatFunc->parameters.size() == flatOther->parameters.size() && 
    494                                                 flatFunc->returnVals.size() == flatOther->returnVals.size()) 
    495                                         || flatFunc->isTtype() 
    496                                         || flatOther->isTtype() 
     492                        if (
     493                                        (flatFunc->parameters.size() == flatOther->parameters.size() &&
     494                                                flatFunc->returnVals.size() == flatOther->returnVals.size())
     495                                        || flatFunc->isTtype()
     496                                        || flatOther->isTtype()
    497497                        ) {
    498498                                if ( unifyDeclList( flatFunc->parameters.begin(), flatFunc->parameters.end(), flatOther->parameters.begin(), flatOther->parameters.end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
     
    711711                bool result;
    712712
    713                 Unify_new( 
    714                         const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need, 
    715                         ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen, 
     713                Unify_new(
     714                        const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need,
     715                        ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen,
    716716                        const ast::SymbolTable & symtab )
    717                 : type2(type2), tenv(env), need(need), have(have), open(open), widen(widen), 
     717                : type2(type2), tenv(env), need(need), have(have), open(open), widen(widen),
    718718                  symtab(symtab), result(false) {}
    719719
    720720                void previsit( const ast::Node * ) { visit_children = false; }
    721                
     721
    722722                void postvisit( const ast::VoidType * ) {
    723723                        result = dynamic_cast< const ast::VoidType * >( type2 );
     
    732732                void postvisit( const ast::PointerType * pointer ) {
    733733                        if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
    734                                 result = unifyExact( 
    735                                         pointer->base, pointer2->base, tenv, need, have, open, 
     734                                result = unifyExact(
     735                                        pointer->base, pointer2->base, tenv, need, have, open,
    736736                                        noWiden(), symtab );
    737737                        }
     
    742742                        if ( ! array2 ) return;
    743743
    744                         // to unify, array types must both be VLA or both not VLA and both must have a 
     744                        // to unify, array types must both be VLA or both not VLA and both must have a
    745745                        // dimension expression or not have a dimension
    746746                        if ( array->isVarLen != array2->isVarLen ) return;
    747                         if ( ! array->isVarLen && ! array2->isVarLen 
     747                        if ( ! array->isVarLen && ! array2->isVarLen
    748748                                        && array->dimension && array2->dimension ) {
    749749                                auto ce1 = array->dimension.as< ast::ConstantExpr >();
     
    751751
    752752                                // see C11 Reference Manual 6.7.6.2.6
    753                                 // two array types with size specifiers that are integer constant expressions are 
     753                                // two array types with size specifiers that are integer constant expressions are
    754754                                // compatible if both size specifiers have the same constant value
    755755                                if ( ce1 && ce2 && ce1->intValue() != ce2->intValue() ) return;
    756756                        }
    757757
    758                         result = unifyExact( 
    759                                 array->base, array2->base, tenv, need, have, open, noWiden(), 
     758                        result = unifyExact(
     759                                array->base, array2->base, tenv, need, have, open, noWiden(),
    760760                                symtab );
    761761                }
     
    763763                void postvisit( const ast::ReferenceType * ref ) {
    764764                        if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
    765                                 result = unifyExact( 
    766                                         ref->base, ref2->base, tenv, need, have, open, noWiden(), 
     765                                result = unifyExact(
     766                                        ref->base, ref2->base, tenv, need, have, open, noWiden(),
    767767                                        symtab );
    768768                        }
     
    771771        private:
    772772                /// Replaces ttype variables with their bound types.
    773                 /// If this isn't done when satifying ttype assertions, then argument lists can have 
     773                /// If this isn't done when satifying ttype assertions, then argument lists can have
    774774                /// different size and structure when they should be compatible.
    775775                struct TtypeExpander_new : public ast::WithShortCircuiting {
     
    800800                                auto types = flatten( d->get_type() );
    801801                                for ( ast::ptr< ast::Type > & t : types ) {
    802                                         // outermost const, volatile, _Atomic qualifiers in parameters should not play 
    803                                         // a role in the unification of function types, since they do not determine 
     802                                        // outermost const, volatile, _Atomic qualifiers in parameters should not play
     803                                        // a role in the unification of function types, since they do not determine
    804804                                        // whether a function is callable.
    805                                         // NOTE: **must** consider at least mutex qualifier, since functions can be 
    806                                         // overloaded on outermost mutex and a mutex function has different 
     805                                        // NOTE: **must** consider at least mutex qualifier, since functions can be
     806                                        // overloaded on outermost mutex and a mutex function has different
    807807                                        // requirements than a non-mutex function
    808808                                        remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
     
    818818                        std::vector< ast::ptr< ast::Type > > types;
    819819                        while ( crnt != end ) {
    820                                 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 
     820                                // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
    821821                                // that this results in a flat tuple
    822822                                flatten( (*crnt)->get_type(), types );
     
    829829
    830830                template< typename Iter >
    831                 static bool unifyDeclList( 
    832                         Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env, 
    833                         ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
     831                static bool unifyDeclList(
     832                        Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env,
     833                        ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
    834834                        const ast::SymbolTable & symtab
    835835                ) {
     
    843843                                if ( isTuple1 && ! isTuple2 ) {
    844844                                        // combine remainder of list2, then unify
    845                                         return unifyExact( 
    846                                                 t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 
     845                                        return unifyExact(
     846                                                t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
    847847                                                noWiden(), symtab );
    848848                                } else if ( ! isTuple1 && isTuple2 ) {
    849849                                        // combine remainder of list1, then unify
    850                                         return unifyExact( 
    851                                                 tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 
     850                                        return unifyExact(
     851                                                tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
    852852                                                noWiden(), symtab );
    853853                                }
    854854
    855                                 if ( ! unifyExact( 
    856                                         t1, t2, env, need, have, open, noWiden(), symtab ) 
     855                                if ( ! unifyExact(
     856                                        t1, t2, env, need, have, open, noWiden(), symtab )
    857857                                ) return false;
    858858
     
    860860                        }
    861861
    862                         // May get to the end of one argument list before the other. This is only okay if the 
     862                        // May get to the end of one argument list before the other. This is only okay if the
    863863                        // other is a ttype
    864864                        if ( crnt1 != end1 ) {
     
    866866                                const ast::Type * t1 = (*crnt1)->get_type();
    867867                                if ( ! Tuples::isTtype( t1 ) ) return false;
    868                                 return unifyExact( 
    869                                         t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 
     868                                return unifyExact(
     869                                        t1, tupleFromDecls( crnt2, end2 ), env, need, have, open,
    870870                                        noWiden(), symtab );
    871871                        } else if ( crnt2 != end2 ) {
     
    873873                                const ast::Type * t2 = (*crnt2)->get_type();
    874874                                if ( ! Tuples::isTtype( t2 ) ) return false;
    875                                 return unifyExact( 
    876                                         tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 
     875                                return unifyExact(
     876                                        tupleFromDecls( crnt1, end1 ), t2, env, need, have, open,
    877877                                        noWiden(), symtab );
    878878                        }
     
    881881                }
    882882
    883                 static bool unifyDeclList( 
    884                         const std::vector< ast::ptr< ast::DeclWithType > > & list1, 
    885                         const std::vector< ast::ptr< ast::DeclWithType > > & list2, 
    886                         ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
     883                static bool unifyDeclList(
     884                        const std::vector< ast::ptr< ast::DeclWithType > > & list1,
     885                        const std::vector< ast::ptr< ast::DeclWithType > > & list2,
     886                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
    887887                        const ast::OpenVarSet & open, const ast::SymbolTable & symtab
    888888                ) {
    889                         return unifyDeclList( 
    890                                 list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open, 
     889                        return unifyDeclList(
     890                                list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open,
    891891                                symtab );
    892892                }
     
    900900
    901901                /// mark all assertions in `type` used in both `assn1` and `assn2`
    902                 static void markAssertions( 
    903                         ast::AssertionSet & assn1, ast::AssertionSet & assn2, 
    904                         const ast::ParameterizedType * type 
     902                static void markAssertions(
     903                        ast::AssertionSet & assn1, ast::AssertionSet & assn2,
     904                        const ast::ParameterizedType * type
    905905                ) {
    906906                        for ( const auto & tyvar : type->forall ) {
     
    918918
    919919                        if ( func->isVarArgs != func2->isVarArgs ) return;
    920                        
    921                         // Flatten the parameter lists for both functions so that tuple structure does not 
     920
     921                        // Flatten the parameter lists for both functions so that tuple structure does not
    922922                        // affect unification. Does not actually mutate function parameters.
    923923                        auto params = flattenList( func->params, tenv );
    924924                        auto params2 = flattenList( func2->params, tenv );
    925925
    926                         // sizes don't have to match if ttypes are involved; need to be more precise w.r.t. 
     926                        // sizes don't have to match if ttypes are involved; need to be more precise w.r.t.
    927927                        // where the ttype is to prevent errors
    928                         if ( 
     928                        if (
    929929                                ( params.size() != params2.size() || func->returns.size() != func2->returns.size() )
    930930                                && ! func->isTtype()
     
    933933
    934934                        if ( ! unifyDeclList( params, params2, tenv, need, have, open, symtab ) ) return;
    935                         if ( ! unifyDeclList( 
     935                        if ( ! unifyDeclList(
    936936                                func->returns, func2->returns, tenv, need, have, open, symtab ) ) return;
    937                        
     937
    938938                        markAssertions( have, need, func );
    939939                        markAssertions( have, need, func2 );
     
    941941                        result = true;
    942942                }
    943        
     943
    944944        private:
    945945                template< typename RefType >
     
    953953                /// Creates a tuple type based on a list of TypeExpr
    954954                template< typename Iter >
    955                 static const ast::Type * tupleFromExprs( 
     955                static const ast::Type * tupleFromExprs(
    956956                        const ast::TypeExpr * param, Iter & crnt, Iter end, ast::CV::Qualifiers qs
    957957                ) {
     
    973973                        const RefType * inst2 = handleRefType( inst, other );
    974974                        if ( ! inst2 ) return;
    975                        
     975
    976976                        // check that parameters of types unify, if any
    977977                        const std::vector< ast::ptr< ast::Expr > > & params = inst->params;
     
    10021002                                }
    10031003
    1004                                 if ( ! unifyExact( 
     1004                                if ( ! unifyExact(
    10051005                                                pty, pty2, tenv, need, have, open, noWiden(), symtab ) ) {
    10061006                                        result = false;
     
    10381038        private:
    10391039                /// Creates a tuple type based on a list of Type
    1040                 static ast::ptr< ast::Type > tupleFromTypes( 
     1040                static ast::ptr< ast::Type > tupleFromTypes(
    10411041                        const std::vector< ast::ptr< ast::Type > > & tys
    10421042                ) {
    10431043                        std::vector< ast::ptr< ast::Type > > out;
    10441044                        for ( const ast::Type * ty : tys ) {
    1045                                 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 
     1045                                // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
    10461046                                // that this results in a flat tuple
    10471047                                flatten( ty, out );
     
    10511051                }
    10521052
    1053                 static bool unifyList( 
    1054                         const std::vector< ast::ptr< ast::Type > > & list1, 
    1055                         const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env, 
    1056                         ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
     1053                static bool unifyList(
     1054                        const std::vector< ast::ptr< ast::Type > > & list1,
     1055                        const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env,
     1056                        ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
    10571057                        const ast::SymbolTable & symtab
    10581058                ) {
     
    10681068                                if ( isTuple1 && ! isTuple2 ) {
    10691069                                        // combine entirety of list2, then unify
    1070                                         return unifyExact( 
    1071                                                 t1, tupleFromTypes( list2 ), env, need, have, open, 
     1070                                        return unifyExact(
     1071                                                t1, tupleFromTypes( list2 ), env, need, have, open,
    10721072                                                noWiden(), symtab );
    10731073                                } else if ( ! isTuple1 && isTuple2 ) {
    10741074                                        // combine entirety of list1, then unify
    10751075                                        return unifyExact(
    1076                                                 tupleFromTypes( list1 ), t2, env, need, have, open, 
     1076                                                tupleFromTypes( list1 ), t2, env, need, have, open,
    10771077                                                noWiden(), symtab );
    10781078                                }
    10791079
    1080                                 if ( ! unifyExact( 
    1081                                         t1, t2, env, need, have, open, noWiden(), symtab ) 
     1080                                if ( ! unifyExact(
     1081                                        t1, t2, env, need, have, open, noWiden(), symtab )
    10821082                                ) return false;
    10831083
     
    10891089                                const ast::Type * t1 = *crnt1;
    10901090                                if ( ! Tuples::isTtype( t1 ) ) return false;
    1091                                 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 
     1091                                // xxx - this doesn't generate an empty tuple, contrary to comment; both ported
    10921092                                // from Rob's code
    1093                                 return unifyExact( 
    1094                                                 t1, tupleFromTypes( list2 ), env, need, have, open, 
     1093                                return unifyExact(
     1094                                                t1, tupleFromTypes( list2 ), env, need, have, open,
    10951095                                                noWiden(), symtab );
    10961096                        } else if ( crnt2 != list2.end() ) {
     
    10981098                                const ast::Type * t2 = *crnt2;
    10991099                                if ( ! Tuples::isTtype( t2 ) ) return false;
    1100                                 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 
     1100                                // xxx - this doesn't generate an empty tuple, contrary to comment; both ported
    11011101                                // from Rob's code
    11021102                                return unifyExact(
    1103                                                 tupleFromTypes( list1 ), t2, env, need, have, open, 
     1103                                                tupleFromTypes( list1 ), t2, env, need, have, open,
    11041104                                                noWiden(), symtab );
    11051105                        }
     
    11331133                void postvisit( const ast::OneType * ) {
    11341134                        result = dynamic_cast< const ast::OneType * >( type2 );
    1135                 }       
     1135                }
    11361136
    11371137          private:
     
    11401140        };
    11411141
    1142         bool unify( 
    1143                         const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
    1144                         ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
     1142        bool unify(
     1143                        const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
     1144                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
    11451145                        ast::OpenVarSet & open, const ast::SymbolTable & symtab
    11461146        ) {
     
    11491149        }
    11501150
    1151         bool unify( 
    1152                         const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
    1153                         ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
    1154                         ast::OpenVarSet & open, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common 
     1151        bool unify(
     1152                        const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
     1153                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
     1154                        ast::OpenVarSet & open, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common
    11551155        ) {
    11561156                ast::OpenVarSet closed;
    11571157                findOpenVars( type1, open, closed, need, have, FirstClosed );
    11581158                findOpenVars( type2, open, closed, need, have, FirstOpen );
    1159                 return unifyInexact( 
     1159                return unifyInexact(
    11601160                        type1, type2, env, need, have, open, WidenMode{ true, true }, symtab, common );
    11611161        }
    11621162
    1163         bool unifyExact( 
    1164                         const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 
    1165                         ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
     1163        bool unifyExact(
     1164                        const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
     1165                        ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
    11661166                        WidenMode widen, const ast::SymbolTable & symtab
    11671167        ) {
     
    11701170                auto var1 = dynamic_cast< const ast::TypeInstType * >( type1 );
    11711171                auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
    1172                 ast::OpenVarSet::const_iterator 
    1173                         entry1 = var1 ? open.find( var1->name ) : open.end(), 
     1172                ast::OpenVarSet::const_iterator
     1173                        entry1 = var1 ? open.find( var1->name ) : open.end(),
    11741174                        entry2 = var2 ? open.find( var2->name ) : open.end();
    11751175                bool isopen1 = entry1 != open.end();
     
    11781178                if ( isopen1 && isopen2 ) {
    11791179                        if ( entry1->second.kind != entry2->second.kind ) return false;
    1180                         return env.bindVarToVar( 
    1181                                 var1, var2, ast::TypeDecl::Data{ entry1->second, entry2->second }, need, have, 
     1180                        return env.bindVarToVar(
     1181                                var1, var2, ast::TypeDecl::Data{ entry1->second, entry2->second }, need, have,
    11821182                                open, widen, symtab );
    11831183                } else if ( isopen1 ) {
     
    11921192        }
    11931193
    1194         bool unifyInexact( 
    1195                         const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
    1196                         ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
    1197                         const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab, 
    1198                         ast::ptr<ast::Type> & common 
     1194        bool unifyInexact(
     1195                        const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
     1196                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
     1197                        const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab,
     1198                        ast::ptr<ast::Type> & common
    11991199        ) {
    12001200                ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
    1201                
    1202                 // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and 
     1201
     1202                // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and
    12031203                // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
    12041204                ast::ptr<ast::Type> t1{ type1 }, t2{ type2 };
    12051205                reset_qualifiers( t1 );
    12061206                reset_qualifiers( t2 );
    1207                
     1207
    12081208                if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) {
    12091209                        t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones
Note: See TracChangeset for help on using the changeset viewer.