Last active
January 24, 2016 08:08
-
-
Save VoR0220/1727c479d93f33c2f327 to your computer and use it in GitHub Desktop.
Documentation for Solidity Default Parameters in Function and Return calls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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