aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-06-05 17:07:50 +0800
committerchriseth <c@ethdev.com>2015-06-05 20:44:05 +0800
commitf4d1acc563a972ee4f5a44c690cd3fdd1783ae97 (patch)
treeb2814613dff69ffe8f1e14ef6160dcc0902eb3c4
parent4987eec3d1e87868e091850d31af58e054ab5ee5 (diff)
downloaddexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar.gz
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar.bz2
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar.lz
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar.xz
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.tar.zst
dexon-solidity-f4d1acc563a972ee4f5a44c690cd3fdd1783ae97.zip
Ability to specify the storage location of a reference type.
-rw-r--r--AST.cpp19
-rw-r--r--AST.h17
-rw-r--r--ArrayUtils.cpp50
-rw-r--r--CompilerUtils.cpp4
-rw-r--r--ExpressionCompiler.cpp16
-rw-r--r--NameAndTypeResolver.cpp47
-rw-r--r--Parser.cpp94
-rw-r--r--Parser.h9
-rw-r--r--Token.h3
-rw-r--r--Types.cpp25
-rw-r--r--Types.h41
11 files changed, 229 insertions, 96 deletions
diff --git a/AST.cpp b/AST.cpp
index c6aebd4f..4c7168af 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -528,6 +528,17 @@ void VariableDeclaration::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
}
+bool VariableDeclaration::isFunctionParameter() const
+{
+ auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
+ if (!function)
+ return false;
+ for (auto const& variable: function->getParameters() + function->getReturnParameters())
+ if (variable.get() == this)
+ return true;
+ return false;
+}
+
bool VariableDeclaration::isExternalFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@@ -879,7 +890,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
{
auto const& arrayType(dynamic_cast<ArrayType const&>(type));
m_isLValue = (*m_memberName == "length" &&
- arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized());
+ arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
}
else
m_isLValue = false;
@@ -902,7 +913,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
m_type = make_shared<FixedBytesType>(1);
else
m_type = type.getBaseType();
- m_isLValue = type.getLocation() != ArrayType::Location::CallData;
+ m_isLValue = type.location() != ReferenceType::Location::CallData;
break;
}
case Type::Category::Mapping:
@@ -919,7 +930,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
{
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index)
- m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
+ m_type = make_shared<TypeType>(make_shared<ArrayType>(ReferenceType::Location::Memory, type.getActualType()));
else
{
m_index->checkTypeRequirements(nullptr);
@@ -927,7 +938,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>(
- ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
+ ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
}
break;
}
diff --git a/AST.h b/AST.h
index be5c9a6c..d7534712 100644
--- a/AST.h
+++ b/AST.h
@@ -474,22 +474,26 @@ private:
class VariableDeclaration: public Declaration
{
public:
+ enum Location { Default, Storage, Memory };
+
VariableDeclaration(
- SourceLocation const& _location,
+ SourceLocation const& _sourceLocation,
ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value,
Visibility _visibility,
bool _isStateVar = false,
bool _isIndexed = false,
- bool _isConstant = false
+ bool _isConstant = false,
+ Location _referenceLocation = Location::Default
):
- Declaration(_location, _name, _visibility),
+ Declaration(_sourceLocation, _name, _visibility),
m_typeName(_type),
m_value(_value),
m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed),
- m_isConstant(_isConstant){}
+ m_isConstant(_isConstant),
+ m_location(_referenceLocation) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@@ -507,10 +511,14 @@ public:
void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
+ /// @returns true if this variable is a parameter or return parameter of a function.
+ bool isFunctionParameter() const;
+ /// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
+ Location referenceLocation() const { return m_location; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
@@ -521,6 +529,7 @@ private:
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
+ Location m_location; ///< Location of the variable if it is of reference type.
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp
index 79ea4953..531ab8af 100644
--- a/ArrayUtils.cpp
+++ b/ArrayUtils.cpp
@@ -38,10 +38,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// need to leave "target_ref target_byte_off" on the stack at the end
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
- solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
+ solAssert(_targetType.location() == ReferenceType::Location::Storage, "");
solAssert(
- _sourceType.getLocation() == ArrayType::Location::CallData ||
- _sourceType.getLocation() == ArrayType::Location::Storage,
+ _sourceType.location() == ReferenceType::Location::CallData ||
+ _sourceType.location() == ReferenceType::Location::Storage,
"Given array location not implemented."
);
@@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes
- bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage;
+ bool sourceIsStorage = _sourceType.location() == ReferenceType::Location::Storage;
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
@@ -69,7 +69,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length]
// retrieve source length
- if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
+ if (_sourceType.location() != ReferenceType::Location::CallData || !_sourceType.isDynamicallySized())
retrieveLength(_sourceType); // otherwise, length is already there
// stack: target_ref source_ref source_length
m_context << eth::Instruction::DUP3;
@@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
if (sourceBaseType->getCategory() == Type::Category::Mapping)
{
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
- solAssert(_sourceType.getLocation() == ArrayType::Location::Storage, "");
+ solAssert(_sourceType.location() == ReferenceType::Location::Storage, "");
// nothing to copy
m_context
<< eth::Instruction::POP << eth::Instruction::POP
@@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
- if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
+ if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic();
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
m_context << eth::Instruction::SWAP2;
@@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
m_context << eth::dupInstruction(3 + byteOffsetSize);
- if (_sourceType.getLocation() == ArrayType::Location::Storage)
+ if (_sourceType.location() == ReferenceType::Location::Storage)
{
if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2;
@@ -228,7 +228,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
void ArrayUtils::clearArray(ArrayType const& _type) const
{
unsigned stackHeightStart = m_context.getStackHeight();
- solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
+ solAssert(_type.location() == ReferenceType::Location::Storage, "");
if (_type.getBaseType()->getStorageBytes() < 32)
{
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@@ -283,7 +283,7 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
{
- solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
+ solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), "");
unsigned stackHeightStart = m_context.getStackHeight();
@@ -311,7 +311,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{
- solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
+ solAssert(_type.location() == ReferenceType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
@@ -396,7 +396,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
{
- if (_arrayType.getLocation() == ArrayType::Location::Storage)
+ if (_arrayType.location() == ReferenceType::Location::Storage)
{
if (_arrayType.getBaseType()->getStorageSize() <= 1)
{
@@ -432,15 +432,15 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
else
{
m_context << eth::Instruction::DUP1;
- switch (_arrayType.getLocation())
+ switch (_arrayType.location())
{
- case ArrayType::Location::CallData:
+ case ReferenceType::Location::CallData:
// length is stored on the stack
break;
- case ArrayType::Location::Memory:
+ case ReferenceType::Location::Memory:
m_context << eth::Instruction::MLOAD;
break;
- case ArrayType::Location::Storage:
+ case ReferenceType::Location::Storage:
m_context << eth::Instruction::SLOAD;
break;
}
@@ -449,16 +449,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
{
- ArrayType::Location location = _arrayType.getLocation();
+ ReferenceType::Location location = _arrayType.location();
eth::Instruction load =
- location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
- location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
+ location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
+ location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD;
// retrieve length
if (!_arrayType.isDynamicallySized())
m_context << _arrayType.getLength();
- else if (location == ArrayType::Location::CallData)
+ else if (location == ReferenceType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else
@@ -473,15 +473,15 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::SWAP1;
if (_arrayType.isDynamicallySized())
{
- if (location == ArrayType::Location::Storage)
+ if (location == ReferenceType::Location::Storage)
CompilerUtils(m_context).computeHashStatic();
- else if (location == ArrayType::Location::Memory)
+ else if (location == ReferenceType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
// stack: <index> <data_ref>
switch (location)
{
- case ArrayType::Location::CallData:
+ case ReferenceType::Location::CallData:
if (!_arrayType.isByteArray())
m_context
<< eth::Instruction::SWAP1
@@ -496,7 +496,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
false
);
break;
- case ArrayType::Location::Storage:
+ case ReferenceType::Location::Storage:
m_context << eth::Instruction::SWAP1;
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
{
@@ -524,7 +524,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
m_context << eth::Instruction::ADD << u256(0);
}
break;
- case ArrayType::Location::Memory:
+ case ReferenceType::Location::Memory:
solAssert(false, "Memory lvalues not yet implemented.");
}
}
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp
index 07bc3cda..e3d8f7f9 100644
--- a/CompilerUtils.cpp
+++ b/CompilerUtils.cpp
@@ -81,7 +81,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
- if (type.getLocation() == ArrayType::Location::CallData)
+ if (type.location() == ReferenceType::Location::CallData)
{
// stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@@ -92,7 +92,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
else
{
- solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented.");
+ solAssert(type.location() == ReferenceType::Location::Storage, "Memory arrays not yet implemented.");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 51bdfbc4..62df9205 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -770,12 +770,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << type.getLength();
}
else
- switch (type.getLocation())
+ switch (type.location())
{
- case ArrayType::Location::CallData:
+ case ReferenceType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break;
- case ArrayType::Location::Storage:
+ case ReferenceType::Location::Storage:
setLValue<StorageArrayLength>(_memberAccess, type);
break;
default:
@@ -816,13 +816,13 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
// remove storage byte offset
- if (arrayType.getLocation() == ArrayType::Location::Storage)
+ if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::POP;
_indexAccess.getIndexExpression()->accept(*this);
// stack layout: <base_ref> [<length>] <index>
ArrayUtils(m_context).accessIndex(arrayType);
- if (arrayType.getLocation() == ArrayType::Location::Storage)
+ if (arrayType.location() == ReferenceType::Location::Storage)
{
if (arrayType.isByteArray())
{
@@ -1169,13 +1169,13 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
// move memory reference to top of stack
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
- if (arrayType.getLocation() == ArrayType::Location::CallData)
+ if (arrayType.location() == ReferenceType::Location::CallData)
m_context << eth::Instruction::DUP2; // length is on stack
- else if (arrayType.getLocation() == ArrayType::Location::Storage)
+ else if (arrayType.location() == ReferenceType::Location::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
else
{
- solAssert(arrayType.getLocation() == ArrayType::Location::Memory, "");
+ solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
}
appendTypeMoveToMemory(IntegerType(256), true);
diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp
index 5ef14f60..22232014 100644
--- a/NameAndTypeResolver.cpp
+++ b/NameAndTypeResolver.cpp
@@ -424,10 +424,49 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName())
{
TypePointer type = _variable.getTypeName()->toType();
- // All array parameter types should point to call data
- if (_variable.isExternalFunctionParameter())
- if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
- type = arrayType->copyForLocation(ArrayType::Location::CallData);
+ using Location = VariableDeclaration::Location;
+ Location loc = _variable.referenceLocation();
+ // References are forced to calldata for external function parameters (not return)
+ // and memory for parameters (also return) of publicly visible functions.
+ // They default to memory for function parameters and storage for local variables.
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+ {
+ if (_variable.isExternalFunctionParameter())
+ {
+ // force location of external function parameters (not return) to calldata
+ if (loc != Location::Default)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be calldata for external functions "
+ "(remove the \"memory\" or \"storage\" keyword)."
+ ));
+ type = ref->copyForLocation(ReferenceType::Location::CallData);
+ }
+ else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic())
+ {
+ // force locations of public or external function (return) parameters to memory
+ if (loc == VariableDeclaration::Location::Storage)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be memory for publicly visible functions "
+ "(remove the \"storage\" keyword)."
+ ));
+ type = ref->copyForLocation(ReferenceType::Location::Memory);
+ }
+ else
+ {
+ if (loc == Location::Default)
+ loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage;
+ type = ref->copyForLocation(
+ loc == Location::Memory ?
+ ReferenceType::Location::Memory :
+ ReferenceType::Location::Storage
+ );
+ }
+ }
+ else if (loc != Location::Default && !ref)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Storage location can only be given for array or struct types."
+ ));
+
_variable.setType(type);
if (!_variable.getType())
diff --git a/Parser.cpp b/Parser.cpp
index 37c358d3..851f1669 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -224,7 +224,9 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
- ASTPointer<ParameterList> parameters(parseParameterList());
+ VarDeclParserOptions options;
+ options.allowLocationSpecifier = true;
+ ASTPointer<ParameterList> parameters(parseParameterList(options));
bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
vector<ASTPointer<ModifierInvocation>> modifiers;
@@ -252,7 +254,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
{
bool const permitEmptyParameterList = false;
m_scanner->next();
- returnParameters = parseParameterList(permitEmptyParameterList);
+ returnParameters = parseParameterList(options, permitEmptyParameterList);
}
else
returnParameters = createEmptyParameterList();
@@ -319,7 +321,9 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
- VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
+ VarDeclParserOptions const& _options,
+ ASTPointer<TypeName> const& _lookAheadArrayType
+)
{
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
@@ -334,20 +338,41 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
bool isIndexed = false;
bool isDeclaredConst = false;
- ASTPointer<ASTString> identifier;
- Token::Value token = m_scanner->getCurrentToken();
Declaration::Visibility visibility(Declaration::Visibility::Default);
- if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
- visibility = parseVisibilitySpecifier(token);
- if (_options.allowIndexed && token == Token::Indexed)
- {
- isIndexed = true;
- m_scanner->next();
- }
- if (token == Token::Const)
+ VariableDeclaration::Location location = VariableDeclaration::Location::Default;
+ ASTPointer<ASTString> identifier;
+
+ while (true)
{
- isDeclaredConst = true;
- m_scanner->next();
+ Token::Value token = m_scanner->getCurrentToken();
+ if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
+ {
+ if (visibility != Declaration::Visibility::Default)
+ BOOST_THROW_EXCEPTION(createParserError("Visibility already specified."));
+ visibility = parseVisibilitySpecifier(token);
+ }
+ else
+ {
+ if (_options.allowIndexed && token == Token::Indexed)
+ isIndexed = true;
+ else if (token == Token::Const)
+ isDeclaredConst = true;
+ else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
+ {
+ if (location != VariableDeclaration::Location::Default)
+ BOOST_THROW_EXCEPTION(createParserError("Location already specified."));
+ if (!type)
+ BOOST_THROW_EXCEPTION(createParserError("Location specifier needs explicit type name."));
+ location = (
+ token == Token::Memory ?
+ VariableDeclaration::Location::Memory :
+ VariableDeclaration::Location::Storage
+ );
+ }
+ else
+ break;
+ m_scanner->next();
+ }
}
nodeFactory.markEndPosition();
@@ -371,7 +396,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
visibility, _options.isStateVariable,
- isIndexed, isDeclaredConst);
+ isIndexed, isDeclaredConst, location);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@@ -388,7 +413,12 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen)
- parameters = parseParameterList();
+ {
+ VarDeclParserOptions options;
+ options.allowIndexed = true;
+ options.allowLocationSpecifier = true;
+ parameters = parseParameterList(options);
+ }
else
parameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
@@ -407,7 +437,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LParen)
- parameters = parseParameterList(true, true);
+ {
+ VarDeclParserOptions options;
+ options.allowIndexed = true;
+ parameters = parseParameterList(options);
+ }
else
parameters = createEmptyParameterList();
bool anonymous = false;
@@ -505,12 +539,14 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
-ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
+ASTPointer<ParameterList> Parser::parseParameterList(
+ VarDeclParserOptions const& _options,
+ bool _allowEmpty
+)
{
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
- VarDeclParserOptions options;
- options.allowIndexed = _allowIndexed;
+ VarDeclParserOptions options(_options);
options.allowEmptyName = true;
expectToken(Token::LParen);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
@@ -691,7 +727,7 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
}
while (m_scanner->getCurrentToken() == Token::LBrack);
- if (m_scanner->getCurrentToken() == Token::Identifier)
+ if (m_scanner->getCurrentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->getCurrentToken()))
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
else
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
@@ -703,6 +739,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
VarDeclParserOptions options;
options.allowVar = true;
options.allowInitialValue = true;
+ options.allowLocationSpecifier = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
@@ -944,11 +981,16 @@ Parser::LookAheadInfo Parser::peekStatementType() const
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
- if (token == Token::Mapping || token == Token::Var ||
- (mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
+ if (token == Token::Mapping || token == Token::Var)
return LookAheadInfo::VariableDeclarationStatement;
- if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
- return LookAheadInfo::IndexAccessStructure;
+ if (mightBeTypeName)
+ {
+ Token::Value next = m_scanner->peekNextToken();
+ if (next == Token::Identifier || Token::isLocationSpecifier(next))
+ return LookAheadInfo::VariableDeclarationStatement;
+ if (m_scanner->peekNextToken() == Token::LBrack)
+ return LookAheadInfo::IndexAccessStructure;
+ }
return LookAheadInfo::ExpressionStatement;
}
diff --git a/Parser.h b/Parser.h
index 08c47c25..d667aa3e 100644
--- a/Parser.h
+++ b/Parser.h
@@ -47,13 +47,15 @@ private:
/// End position of the current token
int getEndPosition() const;
- struct VarDeclParserOptions {
+ struct VarDeclParserOptions
+ {
VarDeclParserOptions() {}
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
bool allowEmptyName = false;
bool allowInitialValue = false;
+ bool allowLocationSpecifier = false;
};
///@{
@@ -74,7 +76,10 @@ private:
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
- ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
+ ASTPointer<ParameterList> parseParameterList(
+ VarDeclParserOptions const& _options,
+ bool _allowEmpty = true
+ );
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
diff --git a/Token.h b/Token.h
index bce16ed1..3e8c1c1d 100644
--- a/Token.h
+++ b/Token.h
@@ -161,12 +161,14 @@ namespace solidity
K(Import, "import", 0) \
K(Is, "is", 0) \
K(Mapping, "mapping", 0) \
+ K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
K(Public, "public", 0) \
K(Private, "private", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
+ K(Storage, "storage", 0) \
K(Struct, "struct", 0) \
K(Var, "var", 0) \
K(While, "while", 0) \
@@ -370,6 +372,7 @@ public:
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
+ static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
diff --git a/Types.cpp b/Types.cpp
index 33eafb15..03e8e4ee 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -144,9 +144,9 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::Bool)
return make_shared<BoolType>();
else if (_typeToken == Token::Bytes)
- return make_shared<ArrayType>(ArrayType::Location::Storage);
+ return make_shared<ArrayType>(ReferenceType::Location::Storage);
else if (_typeToken == Token::String)
- return make_shared<ArrayType>(ArrayType::Location::Storage, true);
+ return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@@ -196,10 +196,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
- return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
+ return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType, length->literalValue(nullptr));
}
else
- return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
+ return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
}
TypePointer Type::forLiteral(Literal const& _literal)
@@ -674,7 +674,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
return false;
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
// let us not allow assignment to memory arrays for now
- if (convertTo.getLocation() != Location::Storage)
+ if (convertTo.location() != Location::Storage)
return false;
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
return false;
@@ -778,12 +778,12 @@ TypePointer ArrayType::externalType() const
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
}
-shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
+TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const
{
auto copy = make_shared<ArrayType>(_location);
copy->m_arrayKind = m_arrayKind;
- if (m_baseType->getCategory() == Type::Category::Array)
- copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
+ if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get()))
+ copy->m_baseType = ref->copyForLocation(_location);
else
copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength;
@@ -934,6 +934,13 @@ MemberList const& StructType::getMembers() const
return *m_members;
}
+TypePointer StructType::copyForLocation(ReferenceType::Location _location) const
+{
+ auto copy = make_shared<StructType>(m_struct);
+ copy->m_location = _location;
+ return copy;
+}
+
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
{
auto const* offsets = getMembers().getMemberStorageOffset(_name);
@@ -1466,7 +1473,7 @@ MagicType::MagicType(MagicType::Kind _kind):
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
- {"data", make_shared<ArrayType>(ArrayType::Location::CallData)},
+ {"data", make_shared<ArrayType>(ReferenceType::Location::CallData)},
{"sig", make_shared<FixedBytesType>(4)}
}));
break;
diff --git a/Types.h b/Types.h
index 65f6e447..3ec92539 100644
--- a/Types.h
+++ b/Types.h
@@ -354,33 +354,50 @@ public:
};
/**
+ * Trait used by types which are not value types and can be stored either in storage, memory
+ * or calldata. This is currently used by arrays and structs.
+ */
+class ReferenceType
+{
+public:
+ enum class Location { Storage, CallData, Memory };
+ explicit ReferenceType(Location _location): m_location(_location) {}
+ Location location() const { return m_location; }
+
+ /// @returns a copy of this type with location (recursively) changed to @a _location.
+ virtual TypePointer copyForLocation(Location _location) const = 0;
+
+protected:
+ Location m_location = Location::Storage;
+};
+
+/**
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
* and dynamically-sized array (<type>[]).
* In storage, all arrays are packed tightly (as long as more than one elementary type fits in
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
* thus start on their own slot.
*/
-class ArrayType: public Type
+class ArrayType: public Type, public ReferenceType
{
public:
- enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string.
explicit ArrayType(Location _location, bool _isString = false):
- m_location(_location),
+ ReferenceType(_location),
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
m_baseType(std::make_shared<FixedBytesType>(1))
{}
/// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType):
- m_location(_location),
+ ReferenceType(_location),
m_baseType(_baseType)
{}
/// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
- m_location(_location),
+ ReferenceType(_location),
m_baseType(_baseType),
m_hasDynamicLength(false),
m_length(_length)
@@ -400,7 +417,6 @@ public:
}
virtual TypePointer externalType() const override;
- Location getLocation() const { return m_location; }
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
/// @returns true if this is a string
@@ -408,15 +424,12 @@ public:
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
- /// @returns a copy of this type with location changed to @a _location
- /// @todo this might move as far up as Type later
- std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
+ TypePointer copyForLocation(Location _location) const override;
private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
- Location m_location;
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
ArrayKind m_arrayKind = ArrayKind::Ordinary;
TypePointer m_baseType;
@@ -484,11 +497,13 @@ private:
/**
* The type of a struct instance, there is one distinct type per struct definition.
*/
-class StructType: public Type
+class StructType: public Type, public ReferenceType
{
public:
virtual Category getCategory() const override { return Category::Struct; }
- explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
+ explicit StructType(StructDefinition const& _struct):
+ //@todo only storage until we have non-storage structs
+ ReferenceType(Location::Storage), m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
@@ -498,6 +513,8 @@ public:
virtual MemberList const& getMembers() const override;
+ TypePointer copyForLocation(Location _location) const override;
+
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
private: