Changeset ae265b5


Ignore:
Timestamp:
Aug 7, 2019, 5:08:58 PM (2 years ago)
Author:
Michael Brooks <mlbrooks@…>
Branches:
arm-eh, jacob/cs343-translation, master, new-ast, new-ast-unique-expr
Children:
ba417e2, c570806
Parents:
2890212
Message:

Two fixes of MemberExpr? handling on new-AST.

Each bug on its own, and also both bugs together, produce the same symptom: Expressions that use function-pointer typed members of structs, where the function has a generically-typed argument, were lacking casts (vs old-AST) in the output of:

./driver/cfa-cpp --prelude-dir ./libcfa/x64-debug/prelude -tm ../cfa-cc/libcfa/prelude/bootloader.cf bootloader.c

First bug: a conversion inaccuracy, which is a regression of convert-convert-post-resolve, introduced at Jun 28 rev 55b647. The constructor of a MemberExpr?, that Convert old-to-new uses, does type-variable substitution. This is intended behaviour when it occurs during resolve, but is incorrect when done twice. In absence of this fix, the old-to-new converter runs this substitution a second time, both in: oldresolve-old2new-new2old and old2new-newresolve-new2old. The fix is offering/using a no-op constructor to/at the converter.

Second bug: a mutation of a transitively-multiply-referenced object. The type-variable substitution just mentioned, which should be happening to the result type of the MemberExpr? and not to the top-level struct-member declaration, actually happens as:

  • old ast: as should; all type-value intentions are clones
  • new ast pre this fix: incorrectly to both types; ref-counted sharing means theExpr.result == theDecl.type, substitution happens on sub-object (see details in code comment)
  • new ast with this fix: as should; deep copy of the type is explicitly forced upfront

All bootloader output differences (new-AST's resolver vs old-AST's resolver) that remain after this fix are: four constructor-destructor calls in builtin function bodies, and a missing copy-constructor call on invoke-main. These differences were present ahead of this change.

This change has been tested for a clean convert-convert difference (vs no new-AST at all), for convert-convert at: pre-resolve, post-resolve, post-parse. In the case of cvcv post-resolve, this clean difference is being restored from the first-bug regression.

This change has been tested for, and causes a small-to-questionable regression of, cfa-cpp speed of compiling bootloader. Old AST = 6.04 s, New AST before this change = 4.64 s, New ast after this change 4.76 s. All numbers are mean of 5 samples with sample SD ~= 0.3 sec. New-AST improvement before this fix = 23% = 4.2 SDs. New-AST improvement after this fix = 21% = 3.9 SDs.

Location:
src/AST
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/AST/Convert.cpp

    r2890212 rae265b5  
    21242124                                old->location,
    21252125                                GET_ACCEPT_1(member, DeclWithType),
    2126                                 GET_ACCEPT_1(aggregate, Expr)
     2126                                GET_ACCEPT_1(aggregate, Expr),
     2127                                ast::MemberExpr::NoOpConstructionChosen
    21272128                        )
    21282129                );
  • src/AST/Expr.cpp

    r2890212 rae265b5  
    158158        assert( aggregate->result );
    159159
    160         // take ownership of member type
    161         result = mem->get_type();
     160        // Deep copy on result type avoids mutation on transitively multiply referenced object.
     161        //
     162        // Example, adapted from parts of builtins and bootloader:
     163        //
     164        // forall(dtype T)
     165        // struct __Destructor {
     166        //   T * object;
     167        //   void (*dtor)(T *);
     168        // };
     169        //
     170        // forall(dtype S)
     171        // void foo(__Destructor(S) &d) {
     172        //   if (d.dtor) {  // here
     173        //   }
     174        // }
     175        //
     176        // Let e be the "d.dtor" guard espression, which is MemberExpr after resolve.  Let d be the
     177        // declaration of member __Destructor.dtor (an ObjectDecl), as accessed via the top-level
     178        // declaration of __Destructor.  Consider the types e.result and d.type.  In the old AST, one
     179        // is a clone of the other.  Ordinary new-AST use would set them up as a multiply-referenced
     180        // object.
     181        //
     182        // e.result: PointerType
     183        // .base: FunctionType
     184        // .params.front(): ObjectDecl, the anonymous parameter of type T*
     185        // .type: PointerType
     186        // .base: TypeInstType
     187        // let x = that
     188        // let y = similar, except start from d.type
     189        //
     190        // Consider two code lines down, genericSubstitution(...).apply(result).
     191        //
     192        // Applying this chosen-candidate's type substitution means modifying x, substituting
     193        // S for T.  This mutation should affect x and not y.
     194
     195        result = deepCopy(mem->get_type());
     196
    162197        // substitute aggregate generic parameters into member type
    163198        genericSubstitution( aggregate->result ).apply( result );
    164199        // ensure lvalue and appropriate restrictions from aggregate type
    165200        add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
     201}
     202
     203MemberExpr::MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
     204    MemberExpr::NoOpConstruction overloadSelector )
     205: Expr( loc ), member( mem ), aggregate( agg ) {
     206        assert( member );
     207        assert( aggregate );
     208        assert( aggregate->result );
     209        (void) overloadSelector;
    166210}
    167211
  • src/AST/Expr.hpp

    r2890212 rae265b5  
    358358        MemberExpr * clone() const override { return new MemberExpr{ *this }; }
    359359        MUTATE_FRIEND
     360
     361        // Custructor overload meant only for AST conversion
     362        enum NoOpConstruction { NoOpConstructionChosen };
     363        MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
     364            NoOpConstruction overloadSelector );
     365        friend class ::ConverterOldToNew;
     366        friend class ::ConverterNewToOld;
    360367};
    361368
Note: See TracChangeset for help on using the changeset viewer.