Skip to content

Instantly share code, notes, and snippets.

@VoR0220
Last active January 24, 2016 08:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VoR0220/1727c479d93f33c2f327 to your computer and use it in GitHub Desktop.
Save VoR0220/1727c479d93f33c2f327 to your computer and use it in GitHub Desktop.
Documentation for Solidity Default Parameters in Function and Return calls
How do we accomplish this? The biggest problem comes in the form of the function call and automatically typechecking different functions
(see default_args_automatic_type_checking). While it is bad form to write default arguments at the beginning of your function statement
and non defaults in betweeen them, we of course want to be consistent. For this reason we need a place to keep track of the position of default
arguments for a function call, and then pass them inside the regular function. I think that if a function is declared with default parameters
that we can automatically push a pointer of the argument on the stack. Furthermore we need to change the declarations of these parameters
to expressions rather than variable declarations. Perhaps we could do something like a mapping of positions and whether or not they are variable
declarations or expressions?
We will also need to check to make sure that the member is not calling itself or any of the other parameters in the function call as they will
not be defined yet. We also cannot allow left hand storage parameters inside the default types because that...just doesn't make sense (storage has already been declared) (UPDATE: we could treat left hand storage variables as static variables whenever called in other functions...could make for some very interesting use cases...).
Not to mention it would make it quite expensive. However right hand storage parameters should be fine, you can then default to a certain storage member
if the type isn't called.
One other thing to discuss is whether we want to extend this functionality to events (I don't think we should...they are complicated enough
as is).
So I think in the TypeChecker these functions are going to need to be changed (there may be more but this is initial analysis):
void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(FunctionCall const& _functionCall)
In ASTAnnotations.h
struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
this might need to be changed to check members against...perhaps it isn't necessary here
struct FunctionCallAnnotation: ExpressionAnnotation
this I feel is going to be more needed...just need a way to reliably check against member types
Current changes already made for parsing:
Note the options field.
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
expectToken(Token::Function);
ASTPointer<ASTString> name;
if (m_scanner->currentToken() == Token::LParen)
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
options.allowInitialValue = true;
ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
{
Token::Value token = m_scanner->currentToken();
if (token == Token::Const)
{
isDeclaredConst = true;
m_scanner->next();
}
else if (token == Token::Identifier)
modifiers.push_back(parseModifierInvocation());
else if (Token::isVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
fatalParserError(string("Multiple visibility specifiers."));
visibility = parseVisibilitySpecifier(token);
}
else
break;
}
ASTPointer<ParameterList> returnParameters;
if (m_scanner->currentToken() == Token::Returns)
{
bool const permitEmptyParameterList = false;
m_scanner->next();
returnParameters = parseParameterList(options, permitEmptyParameterList);
}
else
returnParameters = createEmptyParameterList();
ASTPointer<Block> block = ASTPointer<Block>();
nodeFactory.markEndPosition();
if (m_scanner->currentToken() != Token::Semicolon)
{
block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
}
else
m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(
name,
visibility,
c_isConstructor,
docstring,
parameters,
isDeclaredConst,
modifiers,
returnParameters,
block
);
}
//These are the current name and type resolution tests and what we should be checking for whenever default arguments are invoked
BOOST_AUTO_TEST_CASE(default_args_calling_function)
{
char const* text = R"(
contract C {
function def(uint x = 123, string b = "Hello") returns (uint, string, string a = "World") {
return (x, b, a); //maybe make it so we can leave a off? That might be getting ahead of ourselves
}
function check() {
var (a, y) = def();
(a, y) = def(12, "Anthony");
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(default_args_calling_return_statement)
{
char const* text = R"(
contract C {
function def() returns (uint x = 1, string y = "blah") {}
function check() {
var (a, y) = def();
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(default_args_automatic_type_checking)
{
char const* text = R"(
contract C {
function def(uint x = 123, uint y, uint8[3] z, string zz = "lala") returns (uint, string, uint8[3]) {
if (x = 123)
return (x, zz, z);
return (y, zz, z);
}
function f() {
var (x, y, z) = def(1, [1, 2, 3], "fuhbuh");
(x, y, z) = def(1, 2, [1, 2, 3]);
(x, y, z) = def(1, [1,2,3]);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(invalid_types_calling_default_args)
{
char const* text = R"(
contract C {
function def(uint8 x = [1, 2, 3], string foo = "bar") returns (bool) {
return true;
}
function wrong() {
def([4, 5, 6, 7], 24);
}
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(invalid_types_in_default_args)
{
char const* text = R"(
contract C {
function def(string x = [1, 2, 3], int foo = "bar") returns (bool) {
return true;
}
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(no_storage_in_default_args)
{
char const* text = R"(
contract C {
function def(string storage x = "Hello", int y) returns (bool) {
return true;
}
function check() {
string x = "Hello";
def("Hello", 1);
def(1);
}
}
)";
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(multiple_function_declaration_default_args)
{
char const* text = R"(
contract C {
function def(uint x, uint y, uint z = 2) {}
function def(uint x, uint y) {}
}
)";
BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment