aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp17
-rw-r--r--AST.h22
-rw-r--r--ASTForward.h1
-rw-r--r--Compiler.cpp15
-rw-r--r--Compiler.h5
-rw-r--r--CompilerContext.cpp5
-rw-r--r--CompilerContext.h4
-rw-r--r--CompilerStack.cpp2
-rw-r--r--ExpressionCompiler.cpp85
-rw-r--r--ExpressionCompiler.h13
-rw-r--r--GlobalContext.cpp32
-rw-r--r--GlobalContext.h11
-rw-r--r--Types.cpp2
-rw-r--r--Types.h10
14 files changed, 150 insertions, 74 deletions
diff --git a/AST.cpp b/AST.cpp
index 22a658d4..168a095c 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -372,8 +372,6 @@ void VariableDefinition::checkTypeRequirements()
m_variable->setType(m_value->getType());
}
}
- if (m_variable->getType() && !m_variable->getType()->canLiveOutsideStorage())
- BOOST_THROW_EXCEPTION(m_variable->createTypeError("Type is required to live outside storage."));
}
void Assignment::checkTypeRequirements()
@@ -466,8 +464,6 @@ void FunctionCall::checkTypeRequirements()
}
else
{
- m_expression->requireLValue();
-
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
@@ -495,25 +491,23 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
- m_expression->requireLValue();
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
- m_isLvalue = true;
+ m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING);
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
- m_base->requireLValue();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
- m_isLvalue = true;
+ m_isLvalue = m_type->getCategory() != Type::Category::MAPPING;
}
void Identifier::checkTypeRequirements()
@@ -545,7 +539,6 @@ void Identifier::checkTypeRequirements()
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
- m_isLvalue = true;
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
@@ -554,6 +547,12 @@ void Identifier::checkTypeRequirements()
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return;
}
+ MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration);
+ if (magicVariable)
+ {
+ m_type = magicVariable->getType();
+ return;
+ }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
}
diff --git a/AST.h b/AST.h
index 01e1b54f..f8ff5274 100644
--- a/AST.h
+++ b/AST.h
@@ -230,6 +230,28 @@ private:
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
+/**
+ * Pseudo AST node that is used as declaration for "this", "msg", "tx" and "block" when the
+ * identifier is encountered. Will never have a valid location in the source code.
+ */
+class MagicVariableDeclaration: public Declaration
+{
+public:
+ enum class VariableKind { THIS, MSG, TX, BLOCK };
+ MagicVariableDeclaration(VariableKind _kind, ASTString const& _name,
+ std::shared_ptr<Type const> const& _type):
+ Declaration(Location(), std::make_shared<ASTString>(_name)), m_kind(_kind), m_type(_type) {}
+ virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
+ << errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
+
+ std::shared_ptr<Type const> const& getType() const { return m_type; }
+ VariableKind getKind() const { return m_kind; }
+
+private:
+ VariableKind m_kind;
+ std::shared_ptr<Type const> m_type;
+};
+
/// Types
/// @{
diff --git a/ASTForward.h b/ASTForward.h
index 2b0bd886..a369c8a7 100644
--- a/ASTForward.h
+++ b/ASTForward.h
@@ -40,6 +40,7 @@ class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
+class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;
diff --git a/Compiler.cpp b/Compiler.cpp
index a3865bc3..a82ecd95 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -32,17 +32,13 @@ using namespace std;
namespace dev {
namespace solidity {
-bytes Compiler::compile(ContractDefinition& _contract, bool _optimize)
-{
- Compiler compiler;
- compiler.compileContract(_contract);
- return compiler.m_context.getAssembledBytecode(_optimize);
-}
-
-void Compiler::compileContract(ContractDefinition& _contract)
+void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
m_context = CompilerContext(); // clear it just in case
+ for (MagicVariableDeclaration const* variable: _magicGlobals)
+ m_context.addMagicGlobal(*variable);
+
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
@@ -328,7 +324,8 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
- if (expression.getType()->getCategory() != Type::Category::VOID)
+ Type::Category category = expression.getType()->getCategory();
+ if (category != Type::Category::VOID && category != Type::Category::MAGIC)
m_context << eth::Instruction::POP;
return false;
}
diff --git a/Compiler.h b/Compiler.h
index 3887d3b5..70e6c44f 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -32,13 +32,10 @@ class Compiler: private ASTVisitor
public:
Compiler(): m_returnTag(m_context.newTag()) {}
- void compileContract(ContractDefinition& _contract);
+ void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
- /// Compile the given contract and return the EVM bytecode.
- static bytes compile(ContractDefinition& _contract, bool _optimize);
-
private:
/// Creates a new compiler context / assembly, packs the current code into the data part and
/// adds the constructor code.
diff --git a/CompilerContext.cpp b/CompilerContext.cpp
index 3c1acdfa..b89a8e5b 100644
--- a/CompilerContext.cpp
+++ b/CompilerContext.cpp
@@ -30,6 +30,11 @@ using namespace std;
namespace dev {
namespace solidity {
+void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
+{
+ m_magicGlobals.insert(&_declaration);
+}
+
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
{
m_stateVariables[&_declaration] = m_stateVariablesSize;
diff --git a/CompilerContext.h b/CompilerContext.h
index e624222d..6a48e148 100644
--- a/CompilerContext.h
+++ b/CompilerContext.h
@@ -40,6 +40,7 @@ class CompilerContext
public:
CompilerContext(): m_stateVariablesSize(0) {}
+ void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables);
@@ -48,6 +49,7 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
+ bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
@@ -90,6 +92,8 @@ public:
private:
eth::Assembly m_asm;
+ /// Magic global variables like msg, tx or this, distinguished by type.
+ std::set<Declaration const*> m_magicGlobals;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
/// Storage offsets of state variables
diff --git a/CompilerStack.cpp b/CompilerStack.cpp
index f051d58f..6535e00d 100644
--- a/CompilerStack.cpp
+++ b/CompilerStack.cpp
@@ -64,7 +64,7 @@ bytes const& CompilerStack::compile(bool _optimize)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear();
m_compiler = make_shared<Compiler>();
- m_compiler->compileContract(*m_contractASTNode);
+ m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables());
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
}
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 4d175527..b1a49457 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -162,7 +162,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
- //@todo we only have integers and bools for now which cannot be explicitly converted
+ //@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front();
@@ -179,6 +179,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
}
else
{
+ //@todo: check for "external call" (to be stored in type)
+
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
@@ -193,9 +195,6 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
}
_functionCall.getExpression().accept(*this);
- if (asserts(m_currentLValue.isInCode()))
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
- m_currentLValue.reset();
m_context.appendJump();
m_context << returnLabel;
@@ -213,19 +212,36 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{
- if (asserts(m_currentLValue.isInStorage()))
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value."));
- StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
- m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD;
- m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
+ switch (_memberAccess.getExpression().getType()->getCategory())
+ {
+ case Type::Category::INTEGER:
+ if (asserts(_memberAccess.getMemberName() == "balance"))
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
+ m_context << eth::Instruction::BALANCE;
+ break;
+ case Type::Category::CONTRACT:
+ // call function
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
+ break;
+ case Type::Category::MAGIC:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Magic variables not yet implemented."));
+ break;
+ case Type::Category::STRUCT:
+ {
+ StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
+ m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD;
+ m_currentLValue = LValue(m_context, LValue::STORAGE);
+ m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
+ break;
+ }
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
+ }
}
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
{
- m_currentLValue.reset();
_indexAccess.getBaseExpression().accept(*this);
- if (asserts(m_currentLValue.isInStorage()))
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index access to a non-storage value."));
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
@@ -242,8 +258,25 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
- m_currentLValue.fromDeclaration(_identifier, *_identifier.getReferencedDeclaration());
- m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
+ Declaration* declaration = _identifier.getReferencedDeclaration();
+ if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration))
+ {
+ if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS)
+ m_context << eth::Instruction::ADDRESS;
+ return;
+ }
+ if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration))
+ {
+ m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
+ return;
+ }
+ if (VariableDeclaration* varDef = dynamic_cast<VariableDeclaration*>(declaration))
+ {
+ m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration());
+ m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
+ return;
+ }
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
void ExpressionCompiler::endVisit(Literal& _literal)
@@ -410,9 +443,6 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{
switch (m_type)
{
- case CODE:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Tried to retrieve value of a function."));
- break;
case STACK:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
@@ -423,11 +453,15 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break;
}
case STORAGE:
+ if (!_expression.getType()->isValueType())
+ break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
*m_context << eth::Instruction::SLOAD;
break;
case MEMORY:
+ if (!_expression.getType()->isValueType())
+ break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@@ -455,15 +489,15 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break;
}
case LValue::STORAGE:
+ if (!_expression.getType()->isValueType())
+ break; // no distinction between value and reference for non-value types
if (!_move)
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
*m_context << eth::Instruction::SSTORE;
break;
- case LValue::CODE:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
- << errinfo_comment("Location type does not support assignment."));
- break;
case LValue::MEMORY:
+ if (!_expression.getType()->isValueType())
+ break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@@ -483,7 +517,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
}
}
-void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression, Declaration const& _declaration)
+void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
{
@@ -495,13 +529,8 @@ void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression,
m_type = STORAGE;
*m_context << m_context->getStorageLocationOfVariable(_declaration);
}
- else if (m_context->isFunctionDefinition(&_declaration))
- {
- m_type = CODE;
- *m_context << m_context->getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(_declaration)).pushTag();
- }
else
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index f52da29e..3ed7848b 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -83,31 +83,30 @@ private:
/**
* Helper class to store and retrieve lvalues to and from various locations.
- * All types except STACK store a reference in a slot on the stack, STACK just stores the
- * base stack offset of the variable in @a m_baseStackOffset.
+ * All types except STACK store a reference in a slot on the stack, STACK just
+ * stores the base stack offset of the variable in @a m_baseStackOffset.
*/
class LValue
{
public:
- enum LValueType { NONE, CODE, STACK, MEMORY, STORAGE };
+ enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference.
- /// @a _expression is the current expression, used for error reporting.
- void fromDeclaration(Expression const& _expression, Declaration const& _declaration);
+ /// @a _expression is the current expression
+ void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; }
bool isValid() const { return m_type != NONE; }
- bool isInCode() const { return m_type == CODE; }
bool isInOnStack() const { return m_type == STACK; }
bool isInMemory() const { return m_type == MEMORY; }
bool isInStorage() const { return m_type == STORAGE; }
/// @returns true if this lvalue reference type occupies a slot on the stack.
- bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY || m_type == CODE; }
+ bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
diff --git a/GlobalContext.cpp b/GlobalContext.cpp
index 6179722b..f2e6d9ce 100644
--- a/GlobalContext.cpp
+++ b/GlobalContext.cpp
@@ -45,25 +45,37 @@ GlobalContext::GlobalContext()
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
{
- m_this = createVariable("this", make_shared<ContractType>(_contract));
+ m_currentContract = &_contract;
}
vector<Declaration*> GlobalContext::getDeclarations() const
{
vector<Declaration*> declarations;
- declarations.reserve(m_objects.size() + 1);
- for (ASTPointer<Declaration> const& declaration: m_objects)
- declarations.push_back(declaration.get());
- declarations.push_back(m_this.get());
+ declarations.reserve(m_magicVariables.size() + 1);
+ for (ASTPointer<Declaration> const& variable: m_magicVariables)
+ declarations.push_back(variable.get());
+ declarations.push_back(getCurrentThis());
return declarations;
}
-ASTPointer<VariableDeclaration> GlobalContext::createVariable(const string& _name, shared_ptr<const Type> const& _type)
+MagicVariableDeclaration*GlobalContext::getCurrentThis() const
{
- ASTPointer<VariableDeclaration> variable = make_shared<VariableDeclaration>(Location(), ASTPointer<TypeName>(),
- make_shared<ASTString>(_name));
- variable->setType(_type);
- return variable;
+ if (!m_thisPointer[m_currentContract])
+ m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ MagicVariableDeclaration::VariableKind::THIS,
+ "this", make_shared<ContractType>(*m_currentContract));
+ return m_thisPointer[m_currentContract].get();
+
+}
+
+vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
+{
+ vector<MagicVariableDeclaration const*> declarations;
+ declarations.reserve(m_magicVariables.size() + 1);
+ for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
+ declarations.push_back(variable.get());
+ declarations.push_back(getCurrentThis());
+ return declarations;
}
}
diff --git a/GlobalContext.h b/GlobalContext.h
index b6dea7d5..0166734c 100644
--- a/GlobalContext.h
+++ b/GlobalContext.h
@@ -24,6 +24,7 @@
#include <string>
#include <vector>
+#include <map>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h>
@@ -47,14 +48,14 @@ public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
+ std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
std::vector<Declaration*> getDeclarations() const;
private:
- /// Creates a virtual variable declaration with the given name and type.
- static ASTPointer<VariableDeclaration> createVariable(std::string const& _name, std::shared_ptr<Type const> const& _type);
-
- std::vector<ASTPointer<Declaration>> m_objects;
- ASTPointer<Declaration> m_this;
+ MagicVariableDeclaration* getCurrentThis() const;
+ std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
+ ContractDefinition const* m_currentContract;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;
};
}
diff --git a/Types.cpp b/Types.cpp
index f9d3d90f..8ded428f 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -189,6 +189,8 @@ u256 IntegerType::literalValue(Literal const& _literal) const
return u256(value);
}
+const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared<IntegerType const>(256)}});
+
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address
diff --git a/Types.h b/Types.h
index 297284ba..a8caf715 100644
--- a/Types.h
+++ b/Types.h
@@ -73,7 +73,7 @@ class Type: private boost::noncopyable
public:
enum class Category
{
- INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
+ INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
};
///@{
@@ -110,6 +110,9 @@ public:
virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
virtual bool canLiveOutsideStorage() const { return true; }
+ /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
+ /// i.e. it behaves differently in lvalue context and in value context.
+ virtual bool isValueType() const { return false; }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
@@ -154,6 +157,9 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; }
+ virtual bool isValueType() const override { return true; }
+
+ virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
@@ -166,6 +172,7 @@ public:
private:
int m_bits;
Modifier m_modifier;
+ static const MemberList AddressMemberList;
};
/**
@@ -186,6 +193,7 @@ public:
}
virtual unsigned getCalldataEncodedSize() const { return 1; }
+ virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; }
virtual u256 literalValue(Literal const& _literal) const override;