diff options
| author | chriseth <c@ethdev.com> | 2015-06-05 17:07:50 +0800 | 
|---|---|---|
| committer | chriseth <c@ethdev.com> | 2015-06-05 20:44:05 +0800 | 
| commit | f4d1acc563a972ee4f5a44c690cd3fdd1783ae97 (patch) | |
| tree | b2814613dff69ffe8f1e14ef6160dcc0902eb3c4 | |
| parent | 4987eec3d1e87868e091850d31af58e054ab5ee5 (diff) | |
| download | dexon-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.cpp | 19 | ||||
| -rw-r--r-- | AST.h | 17 | ||||
| -rw-r--r-- | ArrayUtils.cpp | 50 | ||||
| -rw-r--r-- | CompilerUtils.cpp | 4 | ||||
| -rw-r--r-- | ExpressionCompiler.cpp | 16 | ||||
| -rw-r--r-- | NameAndTypeResolver.cpp | 47 | ||||
| -rw-r--r-- | Parser.cpp | 94 | ||||
| -rw-r--r-- | Parser.h | 9 | ||||
| -rw-r--r-- | Token.h | 3 | ||||
| -rw-r--r-- | Types.cpp | 25 | ||||
| -rw-r--r-- | Types.h | 41 | 
11 files changed, 229 insertions, 96 deletions
| @@ -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;  	} @@ -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()) @@ -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;  } @@ -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(); @@ -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; } @@ -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; @@ -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: | 
