-
Virgile Prevosto authoredVirgile Prevosto authored
ACSLFunctionContract.cpp 26.84 KiB
/**************************************************************************/
/* */
/* This file is part of Frama-Clang */
/* */
/* Copyright (C) 2012-2021 */
/* 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 function contracts.
//
#include "ACSLComponent.h"
namespace Acsl {
using namespace DLexer;
#if defined(__clang__) || defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-label"
#endif
const std::string default_behavior_name("default!");
Parser::ReadResult
FunctionContract::error_empty_behavior(Parser::Arguments& arguments) {
DefineAddError (
strcat(strdup("Empty behavior "), ((behavior)
_spec->behavior->element.container)->beh_name))
return RRNeedChars;
}
Parser::ReadResult
FunctionContract::check_bhv_position(Parser::Arguments& arguments) {
switch (_state) {
case BHV:
return error_empty_behavior(arguments);
case COMPLETE:
DefineAddError ("behavior after complete/disjoint clauses")
return RRNeedChars;
default:
return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_terminates_position(Parser::Arguments& arguments) {
switch (_state) {
case BHV:
DefineAddError("terminates clause in behavior")
return RRNeedChars;
case ASSUMES:
DefineAddError("terminates clause after assumes")
return RRNeedChars;
case POST_COND:
DefineAddError("terminates clause after post-condition")
return RRNeedChars;
case TERMINATES:
DefineAddError("at most one terminates clause is allowed")
return RRNeedChars;
case VARIANT:
DefineAddError("terminates clause after decreases clause")
return RRNeedChars;
case COMPLETE:
DefineAddError("terminates clause after complete/disjoint clause")
return RRNeedChars;
case REQUIRES:
if (_spec->behavior && strcmp( ((behavior) ((list)_spec->behavior)->element.plain )->beh_name, default_behavior_name.c_str()) != 0) {
DefineAddError("terminates clause in behavior")
}
return RRNeedChars;
default:
return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_variant_position(Parser::Arguments& arguments) {
switch (_state) {
case BHV:
DefineAddError("decreases in behavior")
return RRNeedChars;
case ASSUMES:
DefineAddError("decreases after assumes")
return RRNeedChars;
case POST_COND:
DefineAddError("decreases after post-condition")
return RRNeedChars;
case VARIANT:
DefineAddError("at most one decreases clause is allowed")
return RRNeedChars;
case COMPLETE:
DefineAddError("decreases after complete/disjoint clause")
return RRNeedChars;
case REQUIRES:
if (_spec->behavior) {
DefineAddError("decreases in behavior")
}
return RRNeedChars;
default: return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_assumes_position(Parser::Arguments& arguments) {
switch(_state) {
case START:
case VARIANT:
case TERMINATES:
DefineAddError("assumes clause in default behavior")
return RRNeedChars;
case REQUIRES:
DefineAddError("assumes clause after requires clause")
return RRNeedChars;
case POST_COND:
DefineAddError("assumes clause after post-condition")
return RRNeedChars;
case COMPLETE:
DefineAddError("assumes clause after complete/disjoint clauses")
return RRNeedChars;
default:
return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_requires_position(Parser::Arguments& arguments) {
switch(_state) {
case TERMINATES:
DefineAddError("requires clause after terminates")
return RRNeedChars;
case POST_COND:
DefineAddError("requires clause after post-condition")
return RRNeedChars;
case VARIANT:
DefineAddError("requires clause after variant")
return RRNeedChars;
case COMPLETE:
DefineAddError("requires clause after complete/disjoint clauses")
return RRNeedChars;
default:
return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_post_cond_position(Parser::Arguments& arguments) {
switch(_state) {
case COMPLETE:
DefineAddError("post-condition after complete/disjoint clause")
return RRNeedChars;
default:
return RRNeedChars;
}
}
Parser::ReadResult
FunctionContract::check_complete_position(Parser::Arguments& arguments) {
switch(_state) {
case BHV:
return error_empty_behavior (arguments);
default:
return RRNeedChars;
}
}
void
FunctionContract::add_behavior(const std::string& name) {
behavior bhv = behavior_cons(copy_string(name),NULL,NULL,NULL,
assigns_WritesAny(), allocation_FreeAllocAny(), NULL);
_currentBehavior.insertContainer(bhv);
_currentRequires = bhv->requires;
_currentAssumes = bhv->assumes;
_currentPostCond = bhv->post_cond;
_currentAssigns = ForwardList();
_currentFrees = ForwardList ();
_currentAllocates = ForwardList ();
}
Parser::ReadResult
FunctionContract::parseSemiColumn(Parser::Arguments& arguments,
const std::string& msg) {
AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TOperatorPunctuator) {
OperatorPunctuatorToken::Type type =
((const OperatorPunctuatorToken&) token).getType();
if (type == OperatorPunctuatorToken::TSemiColon)
return RRNeedChars;
else
DefineAddError(std::string("Expecting ';' after ").append(msg))
}
else
DefineAddError(std::string("Expecting ';' after ").append(msg))
return RRContinueParsing;
}
Parser::ReadResult
FunctionContract::readToken(Parser::State& state, Parser::Arguments& arguments)
{ ReadResult result = RRNeedChars;
enum Delimiters
{ DBegin, DBehaviorName, DBehavior, DRequires, DRequiresEnd, DTerminates,
DTerminatesEnd, DDecreases, DDecreasesFor, DDecreasesForIdent,
DAssumes, DAssumesEnd, DPostCond, DPostCondEnd, DAssigns, DAssignsEnd,
DComplete, DCompleteBehavior, DAfterComplete, DDisjoint, DDisjointBehavior,
DAfterDisjoint, DBeforeSemiColon, DAllocatesFrees, DAllocatesFreesEnd
};
switch (state.point()) {
DefineParseCase(Begin)
arguments.setFunSpecContext();
// Logic label Pre, Here and Old are always present,
// Post only exists in post-condition, assigns and allocates
arguments.addLogicLabel("Here");
arguments.addLogicLabel("Pre");
arguments.addLogicLabel("Old");
{ AbstractToken token = arguments.queryToken();
std::string s = token.str();
if (token.getType() == AbstractToken::TKeyword) {
KeywordToken::Type type = ((const KeywordToken&) token).getType();
switch(type) {
case KeywordToken::TBehavior:
{ result = check_bhv_position (arguments);
if (result == RRFinished) return result;
DefineGotoCase(BehaviorName)
}
case KeywordToken::TRequires:
{ result = check_requires_position(arguments);
if(result == RRFinished) return result;
DefineGotoCase(Requires)
}
case KeywordToken::TTerminates:
{ result = check_terminates_position(arguments);
if(result == RRFinished) return result;
DefineGotoCase(Terminates)
}
case KeywordToken::TDecreases:
{ result = check_variant_position(arguments);
if(result == RRFinished) return result;
DefineGotoCase(Decreases)
}
case KeywordToken::TAssumes:
{ result = check_assumes_position(arguments);
if(result == RRFinished) return result;
DefineGotoCase(Assumes)
}
case KeywordToken::TEnsures:
{ result = check_post_cond_position(arguments);
if(result == RRFinished) return result;
_postCondKind = NORMAL;
arguments.set_result_context(!_isStatementContract);
DefineGotoCase(PostCond)
}
case KeywordToken::TAssigns:
{ result = check_post_cond_position(arguments);
if (result == RRFinished) return result;
arguments.set_result_context(!_isStatementContract);
DefineGotoCase(Assigns)
}
case KeywordToken::TAllocates:
case KeywordToken::TFrees:
{
result = check_post_cond_position(arguments);
if (result == RRFinished) return result;
arguments.set_result_context(
!_isStatementContract && (type == KeywordToken::TAllocates));
_allocClauseKind =
type==KeywordToken::TAllocates?ALLOCATES:FREES;
DefineGotoCase(AllocatesFrees);
}
case KeywordToken::TExits:
{ result = check_post_cond_position(arguments);
if(result == RRFinished) return result;
_postCondKind = EXITS;
DefineGotoCase(PostCond)
}
case KeywordToken::TDisjoint:
DefineGotoCase(Disjoint)
case KeywordToken::TComplete:
DefineGotoCase(Complete)
default:
arguments.resetAnnotContext();
DefineAddErrorAndParse(std::string("unexpected keyword ")
.append(token.str()).append(" in function spec"))
}
}
else {
arguments.resetAnnotContext();
DefineAddErrorAndParse("expecting function spec")
}
}
DefineParseCase(BehaviorName)
{ AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TKeyword) {
// Allow keywords as behavior names, converting them to strings
_behaviorName = arguments.getContentToken().text();
DefineGotoCase(Behavior)
}
if (token.getType() == AbstractToken::TIdentifier) {
_behaviorName = ((const IdentifierToken&)arguments.getContentToken())
.content();
DefineGotoCase(Behavior)
};
arguments.lexer().assumeContentToken();
arguments.resetAnnotContext();
DefineAddError(std::string("unexpected token '")
.append(arguments.getContentToken().str())
.append("' expecting name of behavior"));
DefineReduce;
}
DefineParseCase(Behavior)
{ AbstractToken token = arguments.queryToken();
token = arguments.queryToken();
if (token.getType() != AbstractToken::TOperatorPunctuator
|| ((const OperatorPunctuatorToken&) token).getType ()
!= OperatorPunctuatorToken::TColon) {
arguments.lexer().assumeContentToken();
arguments.resetAnnotContext();
DefineAddError(std::string("':' expected after behavior name, got '")
.append(arguments.getContentToken().str())
.append("'"));
DefineReduce;
}
add_behavior(_behaviorName);
_behaviorName = "";
_state = BHV;
DefineGotoCase(Begin);
}
DefineParseCase(Requires) {
logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
TermOrPredicate* pred = new TermOrPredicate;
state.absorbRuleResult(&pred->setPredicate());
DefineShiftAndParse(RequiresEnd,*pred,&TermOrPredicate::readToken);
}
DefineParseCase(RequiresEnd)
{ Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
node(state.getRuleResult());
predicate_named pred = node->extractPredicate(arguments);
state.freeRuleResult();
if (!pred) {
arguments.resetAnnotContext();
DefineAddError("expecting a predicate");
DefineGotoCaseAndParse(BeforeSemiColon)
};
result = parseSemiColumn(arguments, "requires clause");
if (result == RRFinished)
return result;
if (result == RRContinueParsing) {
result = RRNeedChars;
DefineGotoCaseAndParse(BeforeSemiColon)
};
if (!_spec->behavior) {
// Adding new default behavior
add_behavior(default_behavior_name);
}
_currentRequires.insertContainer(pred);
_state = REQUIRES;
DefineGotoCase(Begin)
}
DefineParseCase(Terminates) {
logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
TermOrPredicate* pred = new TermOrPredicate;
state.absorbRuleResult(&pred->setPredicate());
DefineShiftAndParse(TerminatesEnd,*pred,&TermOrPredicate::readToken);
}
DefineParseCase(TerminatesEnd)
{ Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
node(state.getRuleResult());
predicate_named pred = node->extractPredicate(arguments);
state.freeRuleResult();
if (!pred) {
arguments.resetAnnotContext();
DefineAddError("expecting a predicate");
DefineGotoCaseAndParse(BeforeSemiColon)
};
result = parseSemiColumn(arguments, "terminates clause");
if (result == RRFinished)
return result;
if (_spec->terminates->is_some) {
free_predicate_named(pred);
pred = NULL;
arguments.resetAnnotContext();
DefineAddError("a contract can only have one `terminates' clause");
DefineGotoCaseAndParse(BeforeSemiColon)
};
free(_spec->terminates);
_spec->terminates = opt_some_container(pred);
if (result == RRContinueParsing) {
result = RRNeedChars;
DefineGotoCaseAndParse(BeforeSemiColon)
};
_state = TERMINATES;
DefineGotoCase(Begin)
}
DefineParseCase(Decreases) {
logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
TermOrPredicate* decreaseTerm = new TermOrPredicate;
state.absorbRuleResult(&decreaseTerm->setTerm());
DefineShiftAndParse(DecreasesFor, *decreaseTerm,
&TermOrPredicate::readToken);
}
DefineParseCase(DecreasesFor)
{ Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
node(state.getRuleResult());
term decreaseTerm = node->extractTerm(arguments);
state.freeRuleResult();
if (!decreaseTerm) {
arguments.resetAnnotContext();
DefineAddError("expecting a term");
DefineGotoCaseAndParse(BeforeSemiColon)
};
if (_spec->variant->is_some) {
free_term(decreaseTerm);
decreaseTerm = NULL;
arguments.resetAnnotContext();
DefineAddError("a contract can only have one `decreases' clause");
DefineGotoCaseAndParse(BeforeSemiColon)
};
free(_spec->variant);
_spec->variant = opt_some_container(variant_cons(decreaseTerm,
opt_none()));
AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TOperatorPunctuator) {
if (((const OperatorPunctuatorToken&) token).getType()
== OperatorPunctuatorToken::TSemiColon) {
_state = TERMINATES;
DefineGotoCase(Begin)
};
arguments.resetAnnotContext();
DefineAddError("Expecting ';' after decreases clause");
}
else if (token.getType() == AbstractToken::TKeyword) {
if (((const KeywordToken&) token).getType() == KeywordToken::TFor) {
_state = TERMINATES;
DefineGotoCase(DecreasesForIdent)
};
arguments.resetAnnotContext();
DefineAddError("Expecting \"for\" after decreases clause");
};
DefineGotoCaseAndParse(BeforeSemiColon)
}
DefineParseCase(DecreasesForIdent)
if (arguments.queryToken().getType() == AbstractToken::TIdentifier) {
variant decreases = (variant) _spec->variant->content.container;
assert(!decreases->vname->is_some);
free(decreases->vname);
decreases->vname = opt_some_container(strdup(((const IdentifierToken&)
arguments.getContentToken()).content().c_str()));
DefineGotoCase(BeforeSemiColon)
}
arguments.resetAnnotContext();
DefineAddError("Expecting an identifier after decreases-for clause");
DefineGotoCaseAndParse(BeforeSemiColon)
DefineParseCase(Assumes)
{ logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
TermOrPredicate* pred = new TermOrPredicate;
state.absorbRuleResult(&pred->setPredicate());
DefineShiftAndParse(AssumesEnd,*pred,&TermOrPredicate::readToken)
}
DefineParseCase(AssumesEnd)
{ Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
node(state.getRuleResult());
predicate_named pred = node->extractPredicate(arguments);
state.freeRuleResult();
if (!pred) {
DefineAddError("expecting a predicate");
DefineReduceAndParse
};
result = parseSemiColumn(arguments, "assumes clause");
if (result == RRFinished) return result;
if (!_spec->behavior) {
// Adding new default behavior
add_behavior(default_behavior_name);
}
_currentAssumes.insertContainer(pred);
_state = ASSUMES;
DefineGotoCase(Begin)
}
DefineParseCase(PostCond)
{ logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
TermOrPredicate* pred = new TermOrPredicate;
state.absorbRuleResult(&pred->setPredicate());
arguments.addLogicLabel("Post");
DefineShiftAndParse(PostCondEnd,*pred,&TermOrPredicate::readToken)
}
DefineParseCase(PostCondEnd)
{ Parser::State::RuleAccess::TCastFromRule<TermOrPredicate>
node(state.getRuleResult());
predicate_named pred = node->extractPredicate(arguments);
state.freeRuleResult();
if (!pred) {
arguments.resetAnnotContext();
DefineAddError("the body of the post-condition is not a predicate");
_additionalMsg = "postcondition";
DefineGotoCaseAndParse(BeforeSemiColon);
};
arguments.set_result_context(false);
arguments.removeLogicLabel("Post");
result = parseSemiColumn(arguments, "post-condition");
if (result == RRFinished) return result;
if (!_spec->behavior) {
// Adding new default behavior
add_behavior(default_behavior_name);
}
post_condition post = post_condition_cons(_postCondKind,pred);
_currentPostCond.insertContainer(post);
_state = POST_COND;
DefineGotoCase(Begin)
}
DefineParseCase(Assigns)
{
logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
AssignsClause* assigns
= new AssignsClause(arguments.newTokenLocation());
state.absorbRuleResult(assigns);
arguments.addLogicLabel("Post");
DefineShiftAndParse(AssignsEnd,*assigns,&AssignsClause::readToken);
}
DefineParseCase(AssignsEnd)
{ Parser::State::RuleAccess::TCastFromRule<AssignsClause>
node(state.getRuleResult());
ForwardList assigns = node->getResult();
state.freeRuleResult();
result = parseSemiColumn(arguments, "assigns");
if (result == RRFinished) return result;
if (!_spec->behavior) add_behavior(default_behavior_name);
_currentAssigns.append(assigns);
list current = _spec->behavior;
while (current->next) current = current->next;
behavior bhv = (behavior)current->element.container;
if (bhv->assignements) free(bhv->assignements);
bhv->assignements = assigns_Writes(_currentAssigns.getFront());
DefineGotoCase(Begin)
}
DefineParseCase(AllocatesFrees)
{
logic_type thisRecordType = arguments.queryThisType();
if (thisRecordType)
arguments.addLogicFormal("this", thisRecordType);
AllocFreeClause* allocFree
= new AllocFreeClause(arguments.newTokenLocation());
state.absorbRuleResult(allocFree);
arguments.addLogicLabel("Post");
DefineShiftAndParse(AllocatesFreesEnd,*allocFree,
&AllocFreeClause::readToken);
}
DefineParseCase(AllocatesFreesEnd)
{
Parser::State::RuleAccess::TCastFromRule<AllocFreeClause>
node(state.getRuleResult());
ForwardList allocFrees = node->getResult();
state.freeRuleResult();
result =
parseSemiColumn(
arguments,
_allocClauseKind == ALLOCATES ? "allocates" : "frees");
if (result == RRFinished) return result;
if (!_spec->behavior) add_behavior(default_behavior_name);
if (_allocClauseKind == ALLOCATES) {
_currentAllocates.append(allocFrees);
} else {
_currentFrees.append(allocFrees);
}
list current = _spec->behavior;
while (current->next) current = current->next;
behavior bhv = (behavior)current->element.container;
if (bhv->alloc) free(bhv->alloc);
bhv->alloc =
allocation_FreeAlloc(
_currentFrees.getFront(), _currentAllocates.getFront());
DefineGotoCase(Begin)
}
DefineParseCase(Complete)
DefineParseCase(Disjoint)
{ AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TKeyword) {
if (((const KeywordToken&) token).getType()
== KeywordToken::TBehaviors) {
set_of_behaviors behaviorsSet = set_of_behaviors_cons(NULL);
if (state.point() == DComplete)
_complete.insertContainer(behaviorsSet);
else
_disjoint.insertContainer(behaviorsSet);
_currentSetOfBehaviors
= ForwardReferenceList(behaviorsSet->behaviors);
DefineGotoCaseWithIncrement(DCompleteBehavior-DComplete,
LCompleteBehavior);
};
};
arguments.resetAnnotContext();
if (state.point() == DComplete)
DefineAddError("Expecting \"behaviors\" after complete construct")
else
DefineAddError("Expecting \"behaviors\" after disjoint construct")
DefineGotoCaseAndParse(BeforeSemiColon)
}
DefineParseCase(CompleteBehavior)
DefineParseCase(DisjointBehavior)
{ AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TIdentifier) {
_currentSetOfBehaviors.insertContainer(
strdup(((const IdentifierToken&) arguments.getContentToken())
.content().c_str()));
DefineGotoCaseWithIncrement(DAfterComplete-DCompleteBehavior,
LAfterComplete);
};
if (token.getType() == AbstractToken::TKeyword) {
std::string name = arguments.getContentToken().text();
_currentSetOfBehaviors.insertContainer(strdup(name.c_str()));
DefineGotoCaseWithIncrement(DAfterComplete-DCompleteBehavior,
LAfterComplete);
};
// Allow for empty lists: if no identifier has been added, we
// accept a ';' and start parsing a new clause
if (!_currentSetOfBehaviors.getFront() &&
token.getType() == AbstractToken::TOperatorPunctuator &&
((const OperatorPunctuatorToken&)token).getType() ==
OperatorPunctuatorToken::TSemiColon)
DefineGotoCase(Begin);
arguments.resetAnnotContext();
if (state.point() == DCompleteBehavior)
DefineAddError("Expecting an identifier after \"complete behaviors\" "
"construct")
else
DefineAddError("Expecting an identifier after \"disjoint behaviors\" "
"construct")
DefineGotoCaseAndParse(BeforeSemiColon)
}
DefineParseCase(AfterComplete)
DefineParseCase(AfterDisjoint)
{ AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TOperatorPunctuator) {
OperatorPunctuatorToken::Type
type = ((const OperatorPunctuatorToken&) token).getType();
if (type == OperatorPunctuatorToken::TSemiColon)
DefineGotoCase(Begin)
if (type == OperatorPunctuatorToken::TComma)
DefineGotoCaseWithIncrement(DCompleteBehavior-DAfterComplete,
LCompleteBehavior);
};
}
arguments.resetAnnotContext();
if (state.point() == DAfterComplete)
DefineAddError("Expecting ',' or ';' after complete construct")
else
DefineAddError("Expecting ',' or ';' after disjoint construct")
DefineGotoCase(BeforeSemiColon)
DefineParseCase(BeforeSemiColon)
{ AbstractToken token = arguments.queryToken();
if (token.getType() == AbstractToken::TOperatorPunctuator) {
OperatorPunctuatorToken::Type
type = ((const OperatorPunctuatorToken&) token).getType();
if (type == OperatorPunctuatorToken::TSemiColon)
DefineGotoCase(Begin)
};
arguments.resetAnnotContext();
DefineAddError(std::string("Expecting ';' after ")
.append(_additionalMsg))
DefineGotoCase(Begin)
}
} // End switch
return result;
} // End readToken
#if defined(__clang__) || defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
} // end of namespace Acsl