/**************************************************************************/ /* */ /* This file is part of Frama-Clang */ /* */ /* Copyright (C) 2012-2020 */ /* CEA (Commissariat à l'énergie atomique et aux énergies */ /* alternatives) */ /* */ /* you can redistribute it and/or modify it under the terms of the GNU */ /* Lesser General Public License as published by the Free Software */ /* Foundation, version 2.1. */ /* */ /* It is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU Lesser General Public License for more details. */ /* */ /* See the GNU Lesser General Public License version 2.1 */ /* for more details (enclosed in the file LICENSE). */ /* */ /**************************************************************************/ // // Description: // Implementation of the ACSL logic types. // #include "ACSLTermOrPredicate.h" #include "ACSLParser.h" namespace Acsl { bool is_signed(ikind i, Parser::Arguments &context) { switch (i) { case IBOOL: case ICHAR_U: case IUCHAR: case IWCHAR_U: case IWUCHAR: case IUSHORT: case IUINT: case IULONG: case ICHAR16: case ICHAR32: return false; case ICHAR: return context.is_char_signed(); case IWCHAR: return context.is_wchar_signed(); default: return true; } } bool equivalent_int_kind(ikind i1, ikind i2, Parser::Arguments &context) { if (i1 == i2) return true; if (is_signed(i1,context) != is_signed(i2,context)) return false; if ((i1 == IWCHAR || i1 == IWCHAR_U || i1 == IWCHAR_S) && (i2 == IWCHAR || i2 == IWCHAR_U || i2 == IWCHAR_S)) return true; if ((i1 == ICHAR || i1 == ICHAR_U || i1 == ICHAR_S) && (i2 == ICHAR || i2 == ICHAR_U || i2 == ICHAR_S)) return true; return false; } bool compatible_int_kind(ikind i1, ikind i2, Parser::Arguments &context) { if (equivalent_int_kind(i1,i2,context)) return true; if (i1 >= ISHORT && i2 < ISHORT) // char and bool are compatible with anything longer than short return true; if (is_signed(i1, context) != is_signed(i2, context)) return false; if (i1 == IULONG || i1 == ILONG) return true; if (i2 == IULONG || i2 == ILONG) return false; if (i1 == IUINT || i1 == IINT) return true; if (i2 == IUINT || i2 == IINT) return false; if (i1 == ISHORT || i1 == IUSHORT) return true; if (i2 == ISHORT || i2 == IUSHORT) return false; if (i1 == ICHAR || i1 == IUCHAR || i1 == ISCHAR) return true; // i1 is bool, i2 is not compatible with it return false; } bool compatible_float_kind(fkind f1, fkind f2) { if (f1 == FLONGDOUBLE) return true; if (f2 == FLONGDOUBLE) return false; if (f1 == FDOUBLE) return true; if (f2 == FDOUBLE) return false; // f1 == f2 == FFLOAT return true; } // true if v2 is compatible with v1, i.e. is a subtype of v1 // note that we don't use inheritance relation here to check for // compatibility. bool logic_type_compatible(logic_type v1, logic_type v2, Parser::Arguments &context) { while (v1->tag_logic_type == LREFERENCE) v1 = v1->cons_logic_type.Lreference.subtype; while (v2->tag_logic_type == LREFERENCE) v2 = v2->cons_logic_type.Lreference.subtype; tag_logic_type first = v1->tag_logic_type; tag_logic_type second = v2->tag_logic_type; // VP: technically, any int is compatible with a real, the test below // should be improved... if (first == LINT) first = LINTEGER; else if (first == LFLOAT) first = LREAL; if (second == LINT) second = LINTEGER; else if (second == LFLOAT) second = LREAL; if (second == LPOINTER && first == LARRAY) { v2 = v2->cons_logic_type.Lpointer.subtype; v1 = v1->cons_logic_type.Larray.subtype; first = v1->tag_logic_type; second = v2->tag_logic_type; }; if (first == LENUM && second != LENUM) first = LINTEGER; if (first == LREAL && second == LINTEGER) second = LREAL; if (first != second) return false; switch (v1->tag_logic_type) { case LVOID: return true; case LINTEGER: return true; case LREAL: return true; case LINT: { if (v2->tag_logic_type == LINT) { return compatible_int_kind( v1->cons_logic_type.Lint.kind, v2->cons_logic_type.Lint.kind, context); } return false; // v2 is a LINTEGER } case LFLOAT: { if (v2->tag_logic_type == LFLOAT) { return compatible_float_kind( v1->cons_logic_type.Lfloat.kind, v2->cons_logic_type.Lfloat.kind); } return false; // v2 is a LREAL } case LARRAY: if (!logic_type_compatible( v1->cons_logic_type.Larray.subtype, v2->cons_logic_type.Larray.subtype, context)) return false; if (v1->cons_logic_type.Larray.dim->is_some != v2->cons_logic_type.Larray.dim->is_some) { if (v2->cons_logic_type.Larray.dim->is_some) return false; } if (v1->cons_logic_type.Larray.dim->is_some) { if (!logic_constant_equal((logic_constant) v1->cons_logic_type .Larray.dim->content.container, (logic_constant)v2->cons_logic_type.Larray.dim->content.container)) return false; }; return true; case LPOINTER: return logic_type_compatible( v1->cons_logic_type.Lpointer.subtype, v2->cons_logic_type.Lpointer.subtype, context); case LENUM: return qualified_name_equal(v1->cons_logic_type.Lenum.name, v2->cons_logic_type.Lenum.name); case LSTRUCT: return qualified_name_equal(v1->cons_logic_type.Lstruct.name, v2->cons_logic_type.Lstruct.name) && tkind_equal(v1->cons_logic_type.Lstruct.template_kind, v2->cons_logic_type.Lstruct.template_kind); case LUNION: return qualified_name_equal(v1->cons_logic_type.Lunion.name, v2->cons_logic_type.Lunion.name) && tkind_equal(v1->cons_logic_type.Lunion.template_kind, v2->cons_logic_type.Lunion.template_kind); case LCNAMED: return qualified_name_equal(v1->cons_logic_type.LCnamed.name, v2->cons_logic_type.LCnamed.name); case LVARIABLE: return qualified_name_equal(v1->cons_logic_type.Lvariable.name, v2->cons_logic_type.Lvariable.name); case LNAMED: if (!qualified_name_equal(v1->cons_logic_type.Lnamed.name, v2->cons_logic_type.Lnamed.name)) return false; { list l1 = v1->cons_logic_type.Lnamed.template_arguments, l2 = v2->cons_logic_type.Lnamed.template_arguments; while (l1 != NULL || l2 != NULL) { if (l1 == NULL || l2 == NULL) return false; if (!logic_type_equal((logic_type)l1->element.container, (logic_type)l2->element.container)) return false; l1 = l1 -> next; l2 = l2 -> next; } } return true; case LARROW: { list l1 = v1->cons_logic_type.Larrow.left, l2 = v2->cons_logic_type.Larrow.left; while (l1 != NULL || l2 != NULL) { if (l1 == NULL || l2 == NULL) return false; if (!logic_type_compatible( (logic_type)l1->element.container, (logic_type)l2->element.container, context)) return false; l1 = l1 -> next; l2 = l2 -> next; } } return logic_type_compatible( v1->cons_logic_type.Larrow.right, v2->cons_logic_type.Larrow.right, context); default: return false; } return false; } using namespace DLexer; #if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-label" #endif bool is_type_synonym(logic_type_info info) { option def = info->definition; return def->is_some && ((logic_type_def)def->content.container)->tag_logic_type_def == LTSYN; } logic_type make_set_type(logic_type base_type) { return logic_type_Lnamed( qualified_name_cons(NULL,strdup("set")),true, cons_container(base_type,NULL)); } logic_type remove_set_type(logic_type set_type) { assert(TermOrPredicate::isSetType(set_type)); return (logic_type) set_type->cons_logic_type.Lnamed.template_arguments->element.container; } // Extract a definition. is_type_synonym(info) must be true. logic_type extract_synonym_def(logic_type_info info) { option def = info -> definition; assert(def->is_some); logic_type_def ltdef = (logic_type_def)def->content.container; assert(ltdef->tag_logic_type_def == LTSYN); return ltdef->cons_logic_type_def.LTsyn.def; } logic_type from_c_named_type( const clang::NamedDecl* type, const Clang_utils* utils) { clang::Decl::Kind kind = type->getKind(); assert (kind >= clang::Decl::firstType && kind <= clang::Decl::lastType); if (kind >= clang::Decl::firstTypedefName && kind <= clang::Decl::lastTypedefName) { const clang::TypedefNameDecl* tidentifier = static_cast<const clang::TypedefNameDecl*>(type); return logic_type_LCnamed( utils-> makeQualifiedName(*tidentifier), utils->isExternCContext(tidentifier->getDeclContext())); } else { return utils->makeLogicType( type->getLocation(), static_cast<const clang::TypeDecl*>(type)->getTypeForDecl()); } } Parser::ReadResult LogicType::readToken(Parser::State& state, Parser::Arguments& arguments) { enum Delimiters { DBegin, DAfterLogicIdentifier, DAfterLogicAndCContextIdentifier, DAfterQualifiedLogicIdentifier, DAfterQualifiedLogicAndCContextIdentifier, DAfterCContextIdentifier, DAfterQualifiedCContextIdentifier, DCTypeSignedSuffix, DCTypeUnsignedSuffix, DCTypeSuffix, DTypeSuffix, DLogicTypeSuffix, DTypeSuffixArray, DTypeProduct, DTypeRecord, DTypeRecordItem, DTypeSumIdent, DEndTypeSum, DTypeConstructorParam }; ReadResult result = RRNeedChars; switch (state.point()) { DefineParseCase(Begin) { AbstractToken token = arguments.queryToken(); if (!_loc) absorbLoc(arguments.newTokenLocation()); KeywordToken::Type prefixedToken = KeywordToken::TUndefined; if (token.getType() == AbstractToken::TKeyword) { KeywordToken::Type kw = ((const KeywordToken&) token).getType(); if (kw == KeywordToken::TStruct || kw == KeywordToken::TClass || kw == KeywordToken::TUnion) { // A struct/union/class keyword at the beginning of a type is permitted but not necessary. // So we skip it if the next token is an identifier prefixedToken = kw; result = arguments.lexer().readToken(); if (result == RRFinished) { DefineAddError("unexpected end of input after a struct/union/class token, beginning a type"); return result; } token = arguments.queryToken(); if (!_loc) absorbLoc(arguments.newTokenLocation()); if (token.getType() != AbstractToken::TIdentifier) { DefineAddError("expected an identifier after the struct, union, or class token at the beginning of a type"); return RRFinished; } } } switch (token.getType()) { case AbstractToken::TIdentifier: { std::string identifier = ((const IdentifierToken&) arguments.getContentToken()).content(); bool hasFoundLogicQualification = false; arguments.extendLocationWithToken(_loc); if ((_qualification = arguments.findLogicName(identifier)) != NULL) { if (_qualification->ssons()) hasFoundLogicQualification = true; }; const clang::TemplateArgument* templateArgument=NULL; const clang::NamedDecl* cidentifier = arguments.isCodeName(identifier, &templateArgument); if (cidentifier) { clang::Decl::Kind kind = cidentifier->getKind(); if (kind >= clang::Decl::firstRecord && kind <= clang::Decl::lastRecord) { const clang::RecordDecl* decl = llvm::dyn_cast<clang::RecordDecl>(cidentifier); assert(decl); _declContext = decl; switch (prefixedToken) { case KeywordToken::TUndefined: break; case KeywordToken::TUnion: if (!decl->isUnion()) { DefineAddError(identifier + " is not an union."); return RRFinished; } break; case KeywordToken::TStruct: case KeywordToken::TClass: if (!(decl->isStruct() || decl->isClass())) { DefineAddError( identifier + " is not a struct or class."); return RRFinished; } break; default: break; } if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier) }; if (kind >= clang::Decl::firstType && kind <= clang::Decl::lastType) { if (prefixedToken != KeywordToken::TUndefined) { DefineAddError( identifier + " is not a struct, class, or union."); return RRFinished; } if (hasFoundLogicQualification) /* else issue a conflict */ DefineGotoCase(AfterLogicIdentifier); _typeResult = from_c_named_type(cidentifier,arguments.get_clang_utils()); DefineGotoCase(TypeSuffix) }; if ((kind >= clang::Decl::firstTemplate && kind <= clang::Decl::lastTemplate) || kind == clang::Decl::NonTypeTemplateParm) { DefineAddError( "unsupported: templates in logic annotations"); return RRFinished; // the following is completely false: we have Foo<Bar> in // ACSL++ and translate it as Bar in IR... // assert(templateArgument); // switch (templateArgument->getKind()) { // case clang::TemplateArgument::Type: // _typeResult = // arguments.get_clang_utils()->makeLogicType( // arguments.tokenSourceLocation(), // templateArgument->getAsType().getTypePtr()); // DefineGotoCase(TypeSuffix) // default: // break; // }; }; if (prefixedToken != KeywordToken::TUndefined) { DefineAddError( identifier + " is a namespace."); return RRFinished; } switch (kind) { // case clang::Decl::Label: case clang::Decl::Namespace: assert(llvm::dyn_cast<clang::NamespaceDecl>(cidentifier)); _declContext = static_cast<const clang::NamespaceDecl*>( cidentifier); if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier) case clang::Decl::NamespaceAlias: assert(llvm::dyn_cast<clang::NamespaceAliasDecl>( cidentifier)); _declContext = static_cast<const clang::NamespaceAliasDecl*> (cidentifier)->getNamespace(); if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier); default: break; }; }; if (hasFoundLogicQualification) DefineGotoCase(AfterLogicIdentifier); if (_qualification) { if (_qualification->isLogicType()) { logic_type_info ti = _qualification->asLogicType().type_info(); _typeResult = logic_type_Lnamed( qualified_name_dup(ti->type_name), ti->is_extern_c, NULL); DefineGotoCase(LogicTypeSuffix) }; DefineGotoCase(AfterLogicIdentifier) }; _typeResult = logic_type_Linteger(); DefineAddError(std::string("unknown identifier '") .append(identifier).append("'")); DefineReduce }; break; case AbstractToken::TKeyword: switch (((const KeywordToken&) token).getType()) { case KeywordToken::TBool: _typeResult = logic_type_Lint(IBOOL); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) case KeywordToken::TChar: _typeResult = logic_type_Lint(ICHAR); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TChar16: _typeResult = logic_type_Lint(ICHAR16); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TChar32: _typeResult = logic_type_Lint(ICHAR32); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix); case KeywordToken::TConst: case KeywordToken::TVolatile: // don't know how to translate this in logic_type _typeResult DefineGotoCase(Begin) case KeywordToken::TDouble: _typeResult = logic_type_Lfloat(FDOUBLE); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TFloat: _typeResult = logic_type_Lfloat(FFLOAT); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) case KeywordToken::TInt: _typeResult = logic_type_Lint(IINT); _seenInt = true; arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TLong: _typeResult = logic_type_Lint(ILONG); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TShort: _typeResult = logic_type_Lint(ISHORT); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TSigned: _typeResult = logic_type_Lint(IINT); _seenSigned = true; arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TUnsigned: _typeResult = logic_type_Lint(IUINT); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TVoid: _typeResult = logic_type_Lvoid(); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) case KeywordToken::TInteger: _typeResult = logic_type_Linteger(); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) case KeywordToken::TReal: _typeResult = logic_type_Lreal(); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) case KeywordToken::TBoolean: _typeResult = arguments.boolean_type(); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) case KeywordToken::TWcharT: _typeResult = logic_type_Lint(IWCHAR); arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) default: break; }; { std::ostringstream outToken; arguments.lexer().assumeContentToken(); arguments.getContentToken().write(outToken, AbstractToken::PersistentFormat().setPretty()); DefineAddError(std::string("keyword '") .append(outToken.str()) .append("' encountered when parsing a type")); }; DefineReduce case AbstractToken::TOperatorPunctuator: { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); switch (type) { case OperatorPunctuatorToken::TOpenParen: DefineAddError("product type is not implemented"); { LogicType* rule = new LogicType; state.absorbRuleResult(rule); DefineShift(TypeProduct, *rule, &LogicType::readToken); }; case OperatorPunctuatorToken::TOpenBrace: DefineAddError("record type is not implemented"); { LogicType* rule = new LogicType; state.absorbRuleResult(rule); DefineShift(TypeRecord, *rule, &LogicType::readToken); }; case OperatorPunctuatorToken::TBitOr: if (_superTypeName) { _typedefResult = logic_type_def_LTsum(NULL); DefineGotoCase(TypeSumIdent); }; default: break; }; }; default: break; }; { arguments.lexer().assumeContentToken(); DefineAddError(std::string("unexpected token '") .append(arguments.getContentToken().str()) .append("' when starting to parse a type")); }; DefineReduce; }; DefineParseCase(AfterLogicIdentifier) { AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { if (((const OperatorPunctuatorToken&) token).getType() == OperatorPunctuatorToken::TColonColon) { if (_qualification && !_qualification->ssons()) { std::string errorMessage = "unknown logic qualification "; errorMessage.append(_qualification->getName()); DefineAddError(errorMessage) }; DefineGotoCase(AfterQualifiedLogicIdentifier) }; }; std::string errorMessage = "unable to build a type from qualification "; errorMessage.append(_qualification->getName()); DefineAddError(errorMessage) DefineReduceAndParse }; DefineParseCase(AfterLogicAndCContextIdentifier) { AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { if (((const OperatorPunctuatorToken&) token).getType() == OperatorPunctuatorToken::TColonColon) { if (_qualification && !_qualification->ssons()) { _qualification = NULL; DefineGotoCase(AfterQualifiedCContextIdentifier) } DefineGotoCase(AfterQualifiedLogicAndCContextIdentifier) }; }; if (_declContext->isRecord()) { const clang::RecordDecl* recordDecl = static_cast<const clang::RecordDecl*>(_declContext); _typeResult = arguments.get_clang_utils()->makeLogicType( arguments.tokenSourceLocation(), recordDecl->getTypeForDecl()); DefineGotoCaseAndParse(TypeSuffix) }; std::string errorMessage = "unable to build a type from qualification "; errorMessage.append(_qualification->getName()); DefineAddError(errorMessage) DefineReduceAndParse }; DefineParseCase(AfterQualifiedLogicIdentifier) if (arguments.queryToken().getType() == AbstractToken::TIdentifier) { const std::string& identifier = ((const IdentifierToken&) arguments.getContentToken()).content(); assert(_qualification && _qualification->ssons()); GlobalContext::NestedContext locateSon(identifier); const GlobalContext::NestedContext::SonsSet& sons = *_qualification->ssons(); GlobalContext::NestedContext::SonsSet::const_iterator found = sons.find(&locateSon); if (found != sons.end()) { _qualification = *found; if (_qualification->ssons()) DefineGotoCase(AfterLogicIdentifier) if (_qualification->isLogicType()) { logic_type_info ti = _qualification->asLogicType().type_info(); _typeResult = logic_type_Lnamed( qualified_name_dup(ti->type_name),ti->is_extern_c,NULL); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; }; arguments.extendLocationWithToken(_loc); DefineAddError(std::string("identifier '").append(identifier) .append("' does not appear to be a type")); DefineTReduce }; arguments.extendLocationWithToken(_loc); DefineAddError(std::string("expecting idendifier " "after parsing a qualification '::'")); DefineTReduce DefineParseCase(AfterQualifiedLogicAndCContextIdentifier) if (arguments.queryToken().getType() == AbstractToken::TIdentifier) { const std::string& identifier = ((const IdentifierToken&) arguments.getContentToken()).content(); assert(_qualification && _qualification->ssons() && _declContext); GlobalContext::NestedContext locateSon(identifier); const GlobalContext::NestedContext::SonsSet& sons = *_qualification->ssons(); GlobalContext::NestedContext::SonsSet::const_iterator found = sons.find(&locateSon); bool hasFoundLogicQualification = false; if (found != sons.end()) { _qualification = *found; if (_qualification->ssons()) hasFoundLogicQualification = true; if (_qualification->isLogicType()) { logic_type_info ti = _qualification->asLogicType().type_info(); _typeResult = logic_type_Lnamed( qualified_name_dup(ti->type_name),ti->is_extern_c,NULL); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; }; const clang::NamedDecl* cidentifier = arguments.findQualifiedName( identifier, _declContext); if (cidentifier) { clang::Decl::Kind kind = cidentifier->getKind(); if (kind >= clang::Decl::firstRecord && kind <= clang::Decl::lastRecord) { assert(llvm::dyn_cast<clang::RecordDecl>(cidentifier)); _declContext = static_cast<const clang::RecordDecl*>(cidentifier); arguments.extendLocationWithToken(_loc); if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier); }; if (kind >= clang::Decl::firstType && kind <= clang::Decl::lastType) { if (hasFoundLogicQualification) DefineGotoCase(AfterLogicIdentifier); _typeResult = from_c_named_type(cidentifier,arguments.get_clang_utils()); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) }; switch (kind) { // case clang::Decl::Label: case clang::Decl::Namespace: assert(llvm::dyn_cast<clang::NamespaceDecl>(cidentifier)); _declContext = static_cast<const clang::NamespaceDecl*>( cidentifier); if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier); case clang::Decl::NamespaceAlias: assert(llvm::dyn_cast<clang::NamespaceAliasDecl>(cidentifier)); _declContext = static_cast<const clang::NamespaceAliasDecl*>( cidentifier)->getNamespace(); if (!hasFoundLogicQualification) DefineGotoCase(AfterCContextIdentifier) else DefineGotoCase(AfterLogicAndCContextIdentifier) default: break; }; }; if (hasFoundLogicQualification) DefineGotoCase(AfterLogicIdentifier); arguments.extendLocationWithToken(_loc); DefineAddError(std::string("identifier '").append(identifier) .append("' does not appear to be a type")); DefineTReduce }; arguments.extendLocationWithToken(_loc); DefineAddError(std::string("expecting idendifier " "after parsing a qualification '::'")); DefineTReduce DefineParseCase(AfterCContextIdentifier) { AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { if (((const OperatorPunctuatorToken&) token).getType() == OperatorPunctuatorToken::TColonColon) DefineGotoCase(AfterQualifiedCContextIdentifier) }; if (_declContext->isRecord()) { const clang::RecordDecl* recordDecl = static_cast<const clang::RecordDecl*>(_declContext); _typeResult = arguments.get_clang_utils()->makeLogicType( arguments.tokenSourceLocation(), recordDecl->getTypeForDecl()); DefineGotoCaseAndParse(TypeSuffix) }; std::string errorMessage = "unable to build a C/C++ type " "from qualification "; errorMessage.append(_qualification->getName()); DefineAddError(errorMessage) DefineReduceAndParse }; DefineParseCase(AfterQualifiedCContextIdentifier) if (arguments.queryToken().getType() == AbstractToken::TIdentifier) { const std::string& identifier = ((const IdentifierToken&) arguments.getContentToken()).content(); assert(_declContext); const clang::NamedDecl* cidentifier = arguments.findQualifiedName( identifier, _declContext); if (cidentifier) { clang::Decl::Kind kind = cidentifier->getKind(); if (kind >= clang::Decl::firstRecord && kind <= clang::Decl::lastRecord) { assert(llvm::dyn_cast<clang::RecordDecl>(cidentifier)); _declContext = static_cast<const clang::RecordDecl*>(cidentifier); arguments.extendLocationWithToken(_loc); DefineGotoCase(AfterCContextIdentifier); }; if (kind >= clang::Decl::firstType && kind <= clang::Decl::lastType) { _typeResult = from_c_named_type(cidentifier,arguments.get_clang_utils()); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) }; switch (kind) { // case clang::Decl::Label: case clang::Decl::Namespace: assert(llvm::dyn_cast<clang::NamespaceDecl>(cidentifier)); _declContext = static_cast<const clang::NamespaceDecl*>( cidentifier); DefineGotoCase(AfterCContextIdentifier); case clang::Decl::NamespaceAlias: assert(llvm::dyn_cast<clang::NamespaceAliasDecl>(cidentifier)); _declContext = static_cast<const clang::NamespaceAliasDecl*>( cidentifier)->getNamespace(); DefineGotoCase(AfterCContextIdentifier); default: break; }; }; DefineAddError(std::string("unknown identifier '").append(identifier) .append("'")); DefineReduce }; DefineAddError(std::string("expecting type idendifier " "after parsing a qualification '::'")); DefineReduce DefineParseCase(CTypeSuffix) {// there might be more than one C specifier AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TKeyword) { switch (((const KeywordToken&) token).getType()) { case KeywordToken::TChar: { // can only be mixed with signed and unsigned if (unspecifiedKind()) { if (_typeResult->cons_logic_type.Lint.kind == IINT) _typeResult->cons_logic_type.Lint.kind = _seenSigned?ISCHAR:ICHAR; else _typeResult->cons_logic_type.Lint.kind = IUCHAR; arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) } else { DefineAddError( "conflicting C type specification in logic type: char"); DefineReduceAndParse } } case KeywordToken::TConst: case KeywordToken::TVolatile: // just ignore these arguments, they are irrelevant in the logic arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) case KeywordToken::TInt: { // can be mixed with long, short, unsigned, and signed if (_typeResult->tag_logic_type == LINT) { if (_seenInt) { DefineAddErrorAndParse( "duplicated 'int' type specification") } switch (_typeResult->cons_logic_type.Lint.kind) { case ISHORT: case IUSHORT: case IINT: case IUINT: case ILONG: case IULONG: case ILONGLONG: case IULONGLONG: break; default: DefineAddErrorAndParse( "unexpected 'int' type specification") } _seenInt = true; arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) } else { DefineAddErrorAndParse( "conflicting C type specification in logic type: int") } } case KeywordToken::TUnsigned: { if (_seenSigned) { DefineAddErrorAndParse( "mixing signed and unsigned type specification") } else if (_typeResult->tag_logic_type == LFLOAT) { DefineAddErrorAndParse( "'unsigned' meaningless for floating point") } else { switch (_typeResult->cons_logic_type.Lint.kind) { case IBOOL: DefineAddErrorAndParse("'unsigned' meaningless for boolean") case ICHAR16: DefineAddErrorAndParse("'unsigned' meaningless for char16_t") case ICHAR32: DefineAddErrorAndParse("'unsigned' meaningless for char32_t") case IUCHAR: case IWUCHAR: case IUSHORT: case IUINT: case IULONG: case IULONGLONG: DefineAddErrorAndParse("found 'unsigned' twice") case ICHAR: _typeResult->cons_logic_type.Lint.kind = IUCHAR; break; case IWCHAR: _typeResult->cons_logic_type.Lint.kind = IWUCHAR; break; case ISHORT: _typeResult->cons_logic_type.Lint.kind = IUSHORT; break; case ILONG: _typeResult->cons_logic_type.Lint.kind = IULONG; break; case ILONGLONG: _typeResult->cons_logic_type.Lint.kind = IULONGLONG; break; default: // architecture dependent char types. can't be generated assert(false); } arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) } } case KeywordToken::TSigned: { if (_seenSigned) { DefineAddErrorAndParse("duplicated signed type specification") } else if (_typeResult->tag_logic_type == LFLOAT) { DefineAddErrorAndParse( "'signed' meaningless for floating point") } else { switch (_typeResult->cons_logic_type.Lint.kind) { case IBOOL: DefineAddErrorAndParse("'signed' meaningless for boolean") case ICHAR16: DefineAddErrorAndParse("'signed' meaningless for char16_t") case ICHAR32: DefineAddErrorAndParse("'signed' meaningless for char32_t") case IUCHAR: case IWUCHAR: case IUSHORT: case IUINT: case IULONG: case IULONGLONG: DefineAddErrorAndParse( "mixing 'signed' and 'unsigned' in type specification") case ICHAR: case IWCHAR: case ISHORT: case ILONG: case ILONGLONG: break; default: // architecture dependent char types. can't be generated assert(false); } _seenSigned = true; arguments.extendLocationWithToken(_loc); } DefineGotoCase(CTypeSuffix) } case KeywordToken::TLong: { if (_typeResult->tag_logic_type == LFLOAT) { switch(_typeResult->cons_logic_type.Lfloat.kind) { case FFLOAT: DefineAddErrorAndParse( "unexpected 'long' specification for a float") case FDOUBLE: _typeResult->cons_logic_type.Lfloat.kind = FLONGDOUBLE; DefineGotoCase(TypeSuffix) case FLONGDOUBLE: DefineAddErrorAndParse( "cannot have long long double type in Frama-Clang") } } else { // integer type switch(_typeResult->cons_logic_type.Lint.kind) { case IINT: _typeResult->cons_logic_type.Lint.kind = ILONG; break; case IUINT: _typeResult->cons_logic_type.Lint.kind = IULONG; break; case ILONG: _typeResult->cons_logic_type.Lint.kind = ILONGLONG; break; case IULONG: _typeResult->cons_logic_type.Lint.kind = IULONGLONG; break; case ILONGLONG: case IULONGLONG: DefineAddErrorAndParse( "long long long integer type does not exist") default: DefineAddErrorAndParse( "unexpected 'long' type specification") } arguments.extendLocationWithToken(_loc); } DefineGotoCase(CTypeSuffix) } case KeywordToken::TShort: { // can only be mixed with signed, unsigned, and int if (unspecifiedKind()) { if (_typeResult->cons_logic_type.Lint.kind == IUINT) _typeResult->cons_logic_type.Lint.kind = IUSHORT; else _typeResult->cons_logic_type.Lint.kind = ISHORT; arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) } else DefineAddErrorAndParse( "unexpected 'short' type specification") } case KeywordToken::TDouble: { // only possibility is 'long double' if (_typeResult->tag_logic_type == LINT && _typeResult->cons_logic_type.Lint.kind == ILONG) { free_logic_type(_typeResult); _typeResult=logic_type_Lfloat(FLONGDOUBLE); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) } else { DefineAddErrorAndParse( "unexpected 'double' type specification") } } default: DefineGotoCaseAndParse(TypeSuffix) } } else DefineGotoCaseAndParse(TypeSuffix) } DefineParseCase(TypeSuffix) { AbstractToken token = arguments.queryToken(); if (!_doesStopSuffix && token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TStar) { _typeResult = logic_type_Lpointer(_typeResult); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) }; if (type == OperatorPunctuatorToken::TAmpersand) { _typeResult = logic_type_Lreference(_typeResult); arguments.extendLocationWithToken(_loc); DefineGotoCase(TypeSuffix) }; if (type == OperatorPunctuatorToken::TOpenBracket) { TermOrPredicate* subTerm = new TermOrPredicate; state.absorbRuleResult(&subTerm->setTerm()); arguments.extendLocationWithToken(_loc); DefineShift(TypeSuffixArray, *subTerm, &TermOrPredicate::readToken) }; } else if (token.getType() == AbstractToken::TKeyword) { KeywordToken::Type type = ((const KeywordToken&) token).getType(); if (type == KeywordToken::TConst || type == KeywordToken::TVolatile) { arguments.extendLocationWithToken(_loc); DefineGotoCase(CTypeSuffix) }; }; } DefineReduceAndParse DefineParseCase(LogicTypeSuffix) { AbstractToken token = arguments.queryToken(); if (!_doesStopSuffix && token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TStar) { DefineAddError("pointers cannot point onto a logic type"); _typeResult = logic_type_Lpointer(_typeResult); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; if (type == OperatorPunctuatorToken::TAmpersand) { DefineAddError("references cannot reference a logic type"); _typeResult = logic_type_Lreference(_typeResult); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; if (type == OperatorPunctuatorToken::TOpenBracket) { TermOrPredicate* subTerm = new TermOrPredicate; state.absorbRuleResult(&subTerm->setTerm()); arguments.extendLocationWithToken(_loc); DefineShift(TypeSuffixArray, *subTerm, &TermOrPredicate::readToken) }; } else if (token.getType() == AbstractToken::TKeyword) { KeywordToken::Type type = ((const KeywordToken&) token).getType(); if (type == KeywordToken::TConst || type == KeywordToken::TVolatile) { arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; }; } DefineReduceAndParse DefineParseCase(TypeSuffixArray) { AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator && (((const OperatorPunctuatorToken&) token).getType() == OperatorPunctuatorToken::TCloseBracket)) { // int intResult = 0; double doubleResult = 0; logic_type ltype = NULL; term termNode = NULL; std::string dimension = "0"; // bool isRValue = false; { Parser::State::RuleAccess::TCastFromRule<TermOrPredicate> subterm(state.getRuleResult()); // bool isConstant = false; // [TODO] activate it! termNode = subterm->extractTerm(arguments, ltype); state.freeRuleResult(); }; // if (!isRValue) { // // term_lval_cons ... // // term_node = term_node_TLval(lval) // }; if (!ltype || !termNode) { if (arguments.doesStopAfterTooManyErrors()) return RRFinished; DefineReduceAndParse }; assert(false); // unimplemented _typeResult = logic_type_Larray( _typeResult, opt_some_container(logic_constant_LCInt(dimension.c_str()))); DefineGotoCase(CTypeSuffix) DefineTReduce }; }; { std::ostringstream outToken; arguments.lexer().assumeContentToken(); arguments.getContentToken().write(outToken, AbstractToken::PersistentFormat().setPretty()); DefineAddError(std::string("unexpected token '") .append(outToken.str()) .append("' when parsing the dimension of a cast " "with a logic array type")); }; DefineGotoCase(TypeSuffixArray) DefineParseCase(TypeProduct) // TODO only the parsing rules are correct. Type products are not // implemented in the IR. { Parser::State::RuleAccess::TCastFromRule<LogicType> subtype(state.getRuleResult()); // ... state.freeRuleResult(); AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TCloseParen) { _typeResult = logic_type_Linteger(); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; if (type == OperatorPunctuatorToken::TComma) { state.absorbRuleResult(new LogicType); DefineGotoCase(TypeProduct) }; }; std::ostringstream outToken; arguments.lexer().assumeContentToken(); arguments.getContentToken().write(outToken, AbstractToken::PersistentFormat().setPretty()); DefineAddError(std::string("unexpected token '") .append(outToken.str()) .append("' when parsing a product type")); }; DefineReduceAndParse DefineParseCase(TypeRecord) //TODO only the parsing rules are correct. Records are not implemented //in the IR { Parser::State::RuleAccess::TCastFromRule<LogicType> subtype(state.getRuleResult()); // ... state.freeRuleResult(); if (arguments.queryToken().getType() == AbstractToken::TIdentifier) { //const std::string& identifier = // ((const IdentifierToken&) arguments.getContentToken()).content(); DefineGotoCase(TypeRecordItem) }; DefineAddError("expecting identifier when parsing a record type"); }; DefineGotoCaseAndParse(TypeRecordItem) DefineParseCase(TypeRecordItem) { AbstractToken token = arguments.queryToken(); token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TCloseBrace) { _typeResult = logic_type_Linteger(); arguments.extendLocationWithToken(_loc); DefineGotoCase(LogicTypeSuffix) }; if (type == OperatorPunctuatorToken::TSemiColon) { state.absorbRuleResult(new LogicType); DefineGotoCase(TypeProduct) }; }; std::ostringstream outToken; arguments.lexer().assumeContentToken(); arguments.getContentToken().write(outToken, AbstractToken::PersistentFormat().setPretty()); DefineAddError(std::string("unexpected token '") .append(outToken.str()) .append("' when parsing a record type")); }; DefineGotoCase(TypeRecordItem) DefineParseCase(TypeSumIdent) if (arguments.queryToken().getType() == AbstractToken::TIdentifier) { _sumIdentifier = ((const IdentifierToken&) arguments.getContentToken()).content(); DefineGotoCase(EndTypeSum) }; DefineAddError("expecting identifier when parsing a sum type"); DefineGotoCaseAndParse(EndTypeSum) DefineParseCase(EndTypeSum) { assert(_superTypeName); bool is_extern_c = arguments.isExternCContext(); /* logic_ctor_info */ list* endSum = &_typedefResult ->cons_logic_type_def.LTsum.arguments; while (*endSum) endSum = &(*endSum)->next; *endSum = cons_container( logic_ctor_info_cons( qualified_name_cons(NULL, strdup(_sumIdentifier.c_str())), is_extern_c, qualified_name_dup(_superTypeName), NULL), NULL); _sumIdentifier = ""; AbstractToken token = arguments.queryToken(); token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TOpenParen) { LogicType* rule = new LogicType; state.absorbRuleResult(rule); DefineShift(TypeConstructorParam, *rule, &LogicType::readToken); }; if (type == OperatorPunctuatorToken::TBitOr) DefineGotoCase(TypeSumIdent) }; }; DefineReduceAndParse DefineParseCase(TypeConstructorParam) { Parser::State::RuleAccess::TCastFromRule<LogicType> definition(state.getRuleResult()); /* logic_ctor_info */ list* endSum = &_typedefResult->cons_logic_type_def.LTsum.arguments; while (*endSum) endSum = &(*endSum)->next; /* logic_type */ list* locationParams = &((logic_ctor_info) (*endSum)->element.container)->ctor_params; while (*locationParams) locationParams = &(*locationParams)->next; (*locationParams) = cons_container(definition->extractType(), NULL); state.freeRuleResult(); AbstractToken token = arguments.queryToken(); if (token.getType() == AbstractToken::TOperatorPunctuator) { OperatorPunctuatorToken::Type type = ((const OperatorPunctuatorToken&) token).getType(); if (type == OperatorPunctuatorToken::TComma) { LogicType* rule = new LogicType; state.absorbRuleResult(rule); DefineShift(TypeConstructorParam, *rule, &LogicType::readToken); }; if (type == OperatorPunctuatorToken::TCloseParen) DefineGotoCase(EndTypeSum) }; }; DefineAddError("expecting ',' or ')' in the parameters of a sum type"); DefineGotoCase(TypeConstructorParam) }; return result; } std::string str(logic_type typ) { std::string n; tag_logic_type tag = typ->tag_logic_type; if (tag == LINTEGER) return "integer"; if (tag == LREAL) return "real"; if (tag == LINT) return "int#"; if (tag == LFLOAT) { auto tagf = typ->cons_logic_type.Lfloat.kind; if (tagf == FDOUBLE) return "double"; if (tagf == FFLOAT) return "float"; } return "????"; } std::string str(list typs) { std::ostringstream s; while (typs) { logic_type curr_type = ((logic_arg_decl)typs->element.container)->la_type; s << str(curr_type) << " "; typs = typs->next; } return s.str(); } #if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic pop #endif } // end of namespace Acsl