bool ASTPrinter::visit(FunctionDefinition const& _node) { writeLine("FunctionDefinition \"" + _node.getName() + "\"" + (_node.isPublic() ? " - public" : "") + (_node.isDeclaredConst() ? " - const" : "")); printSourcePart(_node); return goDeeper(); }
bool Why3Translator::visit(FunctionDefinition const& _function) { if (!_function.isImplemented()) { error(_function, "Unimplemented functions not supported."); return false; } if (_function.name().empty()) { error(_function, "Fallback functions not supported."); return false; } if (!_function.modifiers().empty()) { error(_function, "Modifiers not supported."); return false; } m_localVariables.clear(); for (auto const& var: _function.parameters()) m_localVariables[var->name()] = var.get(); for (auto const& var: _function.returnParameters()) m_localVariables[var->name()] = var.get(); for (auto const& var: _function.localVariables()) m_localVariables[var->name()] = var; add("let rec _" + _function.name()); add(" (this: account)"); for (auto const& param: _function.parameters()) { string paramType; try { paramType = toFormalType(*param->annotation().type); } catch (NoFormalType &err) { string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err); error(*param, "Parameter type \"" + (typeName ? *typeName : "") + "\" not supported."); } if (param->name().empty()) error(*param, "Anonymous function parameters not supported."); add(" (arg_" + param->name() + ": " + paramType + ")"); } add(":"); indent(); indent(); string retString = "("; for (auto const& retParam: _function.returnParameters()) { string paramType; try { paramType = toFormalType(*retParam->annotation().type); } catch (NoFormalType &err) { string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err); error(*retParam, "Parameter type " + (typeName ? *typeName : "") + " not supported."); } if (retString.size() != 1) retString += ", "; retString += paramType; } add(retString + ")"); unindent(); addSourceFromDocStrings(_function.annotation()); if (!m_currentContract.contract) error(_function, "Only functions inside contracts allowed."); addSourceFromDocStrings(m_currentContract.contract->annotation()); if (_function.isDeclaredConst()) addLine("ensures { (old this) = this }"); else addLine("writes { this }"); addLine("="); // store the prestate in the case we need to revert addLine("let prestate = {balance = this.balance; storage = " + copyOfStorage() + "} in "); // initialise local variables for (auto const& variable: _function.parameters()) addLine("let _" + variable->name() + " = ref arg_" + variable->name() + " in"); for (auto const& variable: _function.returnParameters()) { if (variable->name().empty()) error(*variable, "Unnamed return variables not yet supported."); string varType; try { varType = toFormalType(*variable->annotation().type); } catch (NoFormalType &err) { string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err); error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in return parameter not yet supported."); } addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in"); } for (VariableDeclaration const* variable: _function.localVariables()) { if (variable->name().empty()) error(*variable, "Unnamed variables not yet supported."); string varType; try { varType = toFormalType(*variable->annotation().type); } catch (NoFormalType &err) { string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err); error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in variable declaration not yet supported."); } addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in"); } addLine("try"); _function.body().accept(*this); add(";"); addLine("raise Return"); string retVals; for (auto const& variable: _function.returnParameters()) { if (!retVals.empty()) retVals += ", "; retVals += "!_" + variable->name(); } addLine("with Return -> (" + retVals + ") |"); string reversion = " Revert -> this.balance <- prestate.balance; "; for (auto const* variable: m_currentContract.stateVariables) reversion += "this.storage._" + variable->name() + " <- prestate.storage._" + variable->name() + "; "; //@TODO in case of reversion the return values are wrong - we need to change the // return type to include a bool to signify if an exception was thrown. reversion += "(" + retVals + ")"; addLine(reversion); unindent(); addLine("end"); addLine(""); return false; }