Changeset 8f557161


Ignore:
Timestamp:
Jun 13, 2023, 4:33:16 PM (11 months ago)
Author:
Michael Brooks <mlbrooks@…>
Branches:
master
Children:
5668740, fec8bd1
Parents:
576aadb
Message:

Clarify and fix accuracy in eval public API, on reporting "unable to evaluate."

While the eval internals always used the flag pair valid and cfavalid almost correctly, the public interface exposed the outcome as a single flag, and the various interpretations of this flag were a mess.

Old cfacc treated sizeof(whatever) in some contexts as "known to be zero," which is wrong.
The generally correct answer, which new cfacc now uses is, "unknown now, but GCC will see it as a fine constant."
New tests/eval.cfa captures this fact: is runnable and would fail on old cfacc; it passes with new cfacc.

Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • src/AST/Decl.cpp

    r576aadb r8f557161  
    142142bool EnumDecl::valueOf( const Decl * enumerator, long long& value ) const {
    143143        if ( enumValues.empty() ) {
    144                 long long crntVal = 0;
     144                Evaluation crntVal = {0, true, true};  // until expression is given, we know to start counting from 0
    145145                for ( const Decl * member : members ) {
    146146                        const ObjectDecl* field = strict_dynamic_cast< const ObjectDecl* >( member );
    147147                        if ( field->init ) {
    148148                                const SingleInit * init = strict_dynamic_cast< const SingleInit* >( field->init.get() );
    149                                 auto result = eval( init->value );
    150                                 if ( ! result.second ) {
     149                                crntVal = eval( init->value );
     150                                if ( ! crntVal.isEvaluableInGCC ) {
    151151                                        SemanticError( init->location, ::toString( "Non-constexpr in initialization of "
    152152                                                "enumerator: ", field ) );
    153153                                }
    154                                 crntVal = result.first;
    155154                        }
    156155                        if ( enumValues.count( field->name ) != 0 ) {
    157156                                SemanticError( location, ::toString( "Enum ", name, " has multiple members with the "   "name ", field->name ) );
    158157                        }
    159                         enumValues[ field->name ] = crntVal;
    160                         ++crntVal;
     158                        if (crntVal.hasKnownValue) {
     159                                enumValues[ field->name ] = crntVal.knownValue;
     160                        }
     161                        ++crntVal.knownValue;
    161162                }
    162163        }
  • src/Common/Eval.cc

    r576aadb r8f557161  
    9696// New AST
    9797struct EvalNew : public ast::WithShortCircuiting {
    98         long long int value = 0;                                                        // compose the result of the constant expression
    99         bool valid = true;                                                                      // true => constant expression and value is the result
    100                                                                                                                 // false => not constant expression, e.g., ++i
    101         bool cfavalid = true;                                                           // true => constant expression and value computable
    102                                                                                                                 // false => constant expression but value not computable, e.g., sizeof(int)
     98        Evaluation result = { 0, true, true };
    10399
    104100        void previsit( const ast::Node * ) { visit_children = false; }
    105         void postvisit( const ast::Node * ) { cfavalid = valid = false; }
     101        void postvisit( const ast::Node * ) { result.isEvaluableInGCC = result.hasKnownValue = false; }
    106102
    107103        void postvisit( const ast::UntypedExpr * ) {
     
    110106
    111107        void postvisit( const ast::ConstantExpr * expr ) {      // only handle int constants
    112                 value = expr->intValue();
     108                result.knownValue = expr->intValue();
     109                result.hasKnownValue = true;
     110                result.isEvaluableInGCC = true;
    113111        }
    114112
    115113        void postvisit( const ast::SizeofExpr * ) {
    116                 // do not change valid or value => let C figure it out
    117                 cfavalid = false;
     114                result.hasKnownValue = false;
     115                result.isEvaluableInGCC = true;
    118116        }
    119117
    120118        void postvisit( const ast::AlignofExpr * ) {
    121                 // do not change valid or value => let C figure it out
    122                 cfavalid = false;
     119                result.hasKnownValue = false;
     120                result.isEvaluableInGCC = true;
    123121        }
    124122
    125123        void postvisit( const ast::OffsetofExpr * ) {
    126                 // do not change valid or value => let C figure it out
    127                 cfavalid = false;
     124                result.hasKnownValue = false;
     125                result.isEvaluableInGCC = true;
    128126        }
    129127
    130128        void postvisit( const ast::LogicalExpr * expr ) {
    131                 std::pair<long long int, bool> arg1, arg2;
     129                Evaluation arg1, arg2;
    132130                arg1 = eval( expr->arg1 );
    133                 valid &= arg1.second;
    134                 if ( ! valid ) return;
     131                result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
     132                if ( ! result.isEvaluableInGCC ) return;
    135133                arg2 = eval( expr->arg2 );
    136                 valid &= arg2.second;
    137                 if ( ! valid ) return;
     134                result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
     135                if ( ! result.isEvaluableInGCC ) return;
     136
     137                result.hasKnownValue &= arg1.hasKnownValue;
     138                result.hasKnownValue &= arg2.hasKnownValue;
     139                if ( ! result.hasKnownValue ) return;
    138140
    139141                if ( expr->isAnd ) {
    140                         value = arg1.first && arg2.first;
     142                        result.knownValue = arg1.knownValue && arg2.knownValue;
    141143                } else {
    142                         value = arg1.first || arg2.first;
     144                        result.knownValue = arg1.knownValue || arg2.knownValue;
    143145                } // if
    144146        }
    145147
    146148        void postvisit( const ast::ConditionalExpr * expr ) {
    147                 std::pair<long long int, bool> arg1, arg2, arg3;
     149                Evaluation arg1, arg2, arg3;
    148150                arg1 = eval( expr->arg1 );
    149                 valid &= arg1.second;
    150                 if ( ! valid ) return;
     151                result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
     152                if ( ! result.isEvaluableInGCC ) return;
    151153                arg2 = eval( expr->arg2 );
    152                 valid &= arg2.second;
    153                 if ( ! valid ) return;
     154                result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
     155                if ( ! result.isEvaluableInGCC ) return;
    154156                arg3 = eval( expr->arg3 );
    155                 valid &= arg3.second;
    156                 if ( ! valid ) return;
    157 
    158                 value = arg1.first ? arg2.first : arg3.first;
     157                result.isEvaluableInGCC &= arg3.isEvaluableInGCC;
     158                if ( ! result.isEvaluableInGCC ) return;
     159
     160                result.hasKnownValue &= arg1.hasKnownValue;
     161                result.hasKnownValue &= arg2.hasKnownValue;
     162                result.hasKnownValue &= arg3.hasKnownValue;
     163                if ( ! result.hasKnownValue ) return;
     164
     165                result.knownValue = arg1.knownValue ? arg2.knownValue : arg3.knownValue;
    159166        }
    160167
    161168        void postvisit( const ast::CastExpr * expr ) {         
    162                 // cfa-cc generates a cast before every constant and many other places, e.g., (int)3, so the cast argument must
    163                 // be evaluated to get the constant value.
    164                 auto arg = eval(expr->arg);
    165                 valid = arg.second;
    166                 value = arg.first;
    167                 cfavalid = false;
     169                // cfa-cc generates a cast before every constant and many other places, e.g., (int)3,
     170                // so we must use the value from the cast argument, even though we lack any basis for evaluating wraparound effects, etc
     171                result = eval(expr->arg);
    168172        }
    169173
    170174        void postvisit( const ast::VariableExpr * expr ) {
     175                result.hasKnownValue = false;
     176                result.isEvaluableInGCC = false;
    171177                if ( const ast::EnumInstType * inst = dynamic_cast<const ast::EnumInstType *>(expr->result.get()) ) {
    172178                        if ( const ast::EnumDecl * decl = inst->base ) {
    173                                 if ( decl->valueOf( expr->var, value ) ) { // value filled by valueOf
    174                                         return;
    175                                 }
     179                                result.isEvaluableInGCC = true;
     180                                result.hasKnownValue = decl->valueOf( expr->var, result.knownValue ); // result.knownValue filled by valueOf
    176181                        }
    177182                }
    178                 valid = false;
    179183        }
    180184
    181185        void postvisit( const ast::ApplicationExpr * expr ) {
    182186                const ast::DeclWithType * function = ast::getFunction(expr);
    183                 if ( ! function || function->linkage != ast::Linkage::Intrinsic ) { valid = false; return; }
     187                if ( ! function || function->linkage != ast::Linkage::Intrinsic ) {
     188                        result.isEvaluableInGCC = false;
     189                        result.hasKnownValue = false;
     190                        return;
     191                }
    184192                const std::string & fname = function->name;
    185193                assertf( expr->args.size() == 1 || expr->args.size() == 2, "Intrinsic function with %zd arguments: %s", expr->args.size(), fname.c_str() );
     
    187195                if ( expr->args.size() == 1 ) {
    188196                        // pre/postfix operators ++ and -- => assignment, which is not constant
    189                         std::pair<long long int, bool> arg1;
     197                        Evaluation arg1;
    190198                        arg1 = eval(expr->args.front());
    191                         valid &= arg1.second;
    192                         if ( ! valid ) return;
     199                        result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
     200                        if ( ! result.isEvaluableInGCC ) return;
     201
     202                        result.hasKnownValue &= arg1.hasKnownValue;
     203                        if ( ! result.hasKnownValue ) return;
    193204
    194205                        if (fname == "+?") {
    195                                 value = arg1.first;
     206                                result.knownValue = arg1.knownValue;
    196207                        } else if (fname == "-?") {
    197                                 value = -arg1.first;
     208                                result.knownValue = -arg1.knownValue;
    198209                        } else if (fname == "~?") {
    199                                 value = ~arg1.first;
     210                                result.knownValue = ~arg1.knownValue;
    200211                        } else if (fname == "!?") {
    201                                 value = ! arg1.first;
     212                                result.knownValue = ! arg1.knownValue;
    202213                        } else {
    203                                 valid = false;
     214                                result.isEvaluableInGCC = false;
     215                                result.hasKnownValue = false;
    204216                        } // if
    205217                } else { // => expr->args.size() == 2
    206218                        // infix assignment operators => assignment, which is not constant
    207                         std::pair<long long int, bool> arg1, arg2;
     219                        Evaluation arg1, arg2;
    208220                        arg1 = eval(expr->args.front());
    209                         valid &= arg1.second;
    210                         if ( ! valid ) return;
     221                        result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
     222                        if ( ! result.isEvaluableInGCC ) return;
    211223                        arg2 = eval(expr->args.back());
    212                         valid &= arg2.second;
    213                         if ( ! valid ) return;
     224                        result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
     225                        if ( ! result.isEvaluableInGCC ) return;
     226
     227                        result.hasKnownValue &= arg1.hasKnownValue;
     228                        result.hasKnownValue &= arg2.hasKnownValue;
     229                        if ( ! result.hasKnownValue ) return;
    214230
    215231                        if (fname == "?+?") {
    216                                 value = arg1.first + arg2.first;
     232                                result.knownValue = arg1.knownValue + arg2.knownValue;
    217233                        } else if (fname == "?-?") {
    218                                 value = arg1.first - arg2.first;
     234                                result.knownValue = arg1.knownValue - arg2.knownValue;
    219235                        } else if (fname == "?*?") {
    220                                 value = arg1.first * arg2.first;
     236                                result.knownValue = arg1.knownValue * arg2.knownValue;
    221237                        } else if (fname == "?/?") {
    222                                 if ( arg2.first ) value = arg1.first / arg2.first;
     238                                if ( arg2.knownValue ) result.knownValue = arg1.knownValue / arg2.knownValue;
    223239                        } else if (fname == "?%?") {
    224                                 if ( arg2.first ) value = arg1.first % arg2.first;
     240                                if ( arg2.knownValue ) result.knownValue = arg1.knownValue % arg2.knownValue;
    225241                        } else if (fname == "?<<?") {
    226                                 value = arg1.first << arg2.first;
     242                                result.knownValue = arg1.knownValue << arg2.knownValue;
    227243                        } else if (fname == "?>>?") {
    228                                 value = arg1.first >> arg2.first;
     244                                result.knownValue = arg1.knownValue >> arg2.knownValue;
    229245                        } else if (fname == "?<?") {
    230                                 value = arg1.first < arg2.first;
     246                                result.knownValue = arg1.knownValue < arg2.knownValue;
    231247                        } else if (fname == "?>?") {
    232                                 value = arg1.first > arg2.first;
     248                                result.knownValue = arg1.knownValue > arg2.knownValue;
    233249                        } else if (fname == "?<=?") {
    234                                 value = arg1.first <= arg2.first;
     250                                result.knownValue = arg1.knownValue <= arg2.knownValue;
    235251                        } else if (fname == "?>=?") {
    236                                 value = arg1.first >= arg2.first;
     252                                result.knownValue = arg1.knownValue >= arg2.knownValue;
    237253                        } else if (fname == "?==?") {
    238                                 value = arg1.first == arg2.first;
     254                                result.knownValue = arg1.knownValue == arg2.knownValue;
    239255                        } else if (fname == "?!=?") {
    240                                 value = arg1.first != arg2.first;
     256                                result.knownValue = arg1.knownValue != arg2.knownValue;
    241257                        } else if (fname == "?&?") {
    242                                 value = arg1.first & arg2.first;
     258                                result.knownValue = arg1.knownValue & arg2.knownValue;
    243259                        } else if (fname == "?^?") {
    244                                 value = arg1.first ^ arg2.first;
     260                                result.knownValue = arg1.knownValue ^ arg2.knownValue;
    245261                        } else if (fname == "?|?") {
    246                                 value = arg1.first | arg2.first;
     262                                result.knownValue = arg1.knownValue | arg2.knownValue;
    247263                        } else {
    248                                 valid = false;
     264                                result.isEvaluableInGCC = false;
     265                                result.hasKnownValue = false;
    249266                        }
    250267                } // if
     
    263280}
    264281
    265 std::pair<long long int, bool> eval( const ast::Expr * expr ) {
    266         ast::Pass<EvalNew> ev;
     282Evaluation eval( const ast::Expr * expr ) {
    267283        if ( expr ) {
    268                 expr->accept( ev );
    269                 return std::make_pair( ev.core.value, ev.core.valid );
     284
     285                return ast::Pass<EvalNew>::read(expr);
     286                // Evaluation ret = ast::Pass<EvalNew>::read(expr);
     287                // ret.knownValue = 777;
     288                // return ret;
     289
    270290        } else {
    271                 return std::make_pair( 0, false );
     291                return { 0, false, false };
    272292        }
    273293}
  • src/Common/Eval.h

    r576aadb r8f557161  
    2323}
    2424
     25struct Evaluation {
     26        long long int knownValue;
     27        bool hasKnownValue;
     28        bool isEvaluableInGCC;
     29};
     30
    2531/// Evaluates expr as a long long int.
    2632/// If second is false, expr could not be evaluated.
    2733std::pair<long long int, bool> eval(const Expression * expr);
    28 std::pair<long long int, bool> eval(const ast::Expr * expr);
     34Evaluation eval(const ast::Expr * expr);
    2935
    3036// Local Variables: //
  • src/ResolvExpr/CurrentObject.cc

    r576aadb r8f557161  
    689689
    690690                        auto arg = eval( expr );
    691                         index = arg.first;
     691                        assertf( arg.hasKnownValue, "Non-evaluable expression made it to IndexIterator" );
     692                        index = arg.knownValue;
    692693
    693694                        // if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
     
    728729                size_t getSize( const Expr * expr ) {
    729730                        auto res = eval( expr );
    730                         if ( !res.second ) {
     731                        if ( !res.hasKnownValue ) {
    731732                                SemanticError( location, toString( "Array designator must be a constant expression: ", expr ) );
    732733                        }
    733                         return res.first;
     734                        return res.knownValue;
    734735                }
    735736
  • src/ResolvExpr/Resolver.cc

    r576aadb r8f557161  
    13411341                                        auto mutAttr = mutate(attr);
    13421342                                        mutAttr->params.front() = resolved;
    1343                                         if (! result.second) {
     1343                                        if (! result.hasKnownValue) {
    13441344                                                SemanticWarning(loc, Warning::GccAttributes,
    13451345                                                        toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
    13461346                                        }
    13471347                                        else {
    1348                                                 auto priority = result.first;
     1348                                                auto priority = result.knownValue;
    13491349                                                if (priority < 101) {
    13501350                                                        SemanticWarning(loc, Warning::GccAttributes,
Note: See TracChangeset for help on using the changeset viewer.