Syntax for meta type information.

This commit is contained in:
chriseth 2019-01-10 16:28:39 +01:00
parent 44237211d1
commit 2fcfb216b5
11 changed files with 116 additions and 14 deletions

View File

@ -180,6 +180,7 @@ namespace langutil
K(CallData, "calldata", 0) \ K(CallData, "calldata", 0) \
K(Struct, "struct", 0) \ K(Struct, "struct", 0) \
K(Throw, "throw", 0) \ K(Throw, "throw", 0) \
K(Type, "type", 0) \
K(Using, "using", 0) \ K(Using, "using", 0) \
K(Var, "var", 0) \ K(Var, "var", 0) \
K(View, "view", 0) \ K(View, "view", 0) \
@ -256,7 +257,6 @@ namespace langutil
K(Supports, "supports", 0) \ K(Supports, "supports", 0) \
K(Switch, "switch", 0) \ K(Switch, "switch", 0) \
K(Try, "try", 0) \ K(Try, "try", 0) \
K(Type, "type", 0) \
K(Typedef, "typedef", 0) \ K(Typedef, "typedef", 0) \
K(TypeOf, "typeof", 0) \ K(TypeOf, "typeof", 0) \
K(Unchecked, "unchecked", 0) \ K(Unchecked, "unchecked", 0) \

View File

@ -61,7 +61,14 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("type", make_shared<FunctionType>(
strings{"address"} /* accepts any contract type, handled by the type checker */,
strings{} /* returns a MagicType, handled by the type checker */,
FunctionType::Kind::MetaType,
false,
StateMutability::Pure
)),
}) })
{ {
} }

View File

@ -199,6 +199,38 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
return components; return components;
} }
TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall)
{
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
if (arguments.size() != 1)
{
m_errorReporter.typeError(
_functionCall.location(),
"This function takes one argument, but " +
toString(arguments.size()) +
" were provided."
);
return {};
}
TypePointer firstArgType = type(*arguments.front());
if (
firstArgType->category() != Type::Category::TypeType ||
dynamic_cast<TypeType const&>(*firstArgType).actualType()->category() != TypeType::Category::Contract
)
{
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
"Contract type required, but " +
type(*arguments.front())->toString(true) +
" provided."
);
return {};
}
return {MagicType::metaType(dynamic_cast<TypeType const&>(*firstArgType).actualType())};
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{ {
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@ -1822,6 +1854,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
returnTypes = functionType->returnParameterTypes(); returnTypes = functionType->returnParameterTypes();
break; break;
} }
case FunctionType::Kind::MetaType:
returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall);
break;
default: default:
{ {
typeCheckFunctionCall(_functionCall, functionType); typeCheckFunctionCall(_functionCall, functionType);
@ -2062,8 +2097,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (tt->actualType()->category() == Type::Category::Enum) if (tt->actualType()->category() == Type::Category::Enum)
annotation.isPure = true; annotation.isPure = true;
if (auto magicType = dynamic_cast<MagicType const*>(exprType.get())) if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
{
if (magicType->kind() == MagicType::Kind::ABI) if (magicType->kind() == MagicType::Kind::ABI)
annotation.isPure = true; annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::MetaType)
if (memberName == "creationCode" || memberName == "runtimeCode")
annotation.isPure = true;
}
return false; return false;
} }

View File

@ -81,6 +81,8 @@ private:
bool _abiEncoderV2 bool _abiEncoderV2
); );
TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall);
/// Performs type checks and determines result types for type conversion FunctionCall nodes. /// Performs type checks and determines result types for type conversion FunctionCall nodes.
TypePointer typeCheckTypeConversionAndRetrieveReturnType( TypePointer typeCheckTypeConversionAndRetrieveReturnType(
FunctionCall const& _functionCall FunctionCall const& _functionCall

View File

@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::ABI, "encodeWithSignature"}, {MagicType::Kind::ABI, "encodeWithSignature"},
{MagicType::Kind::Block, "blockhash"}, {MagicType::Kind::Block, "blockhash"},
{MagicType::Kind::Message, "data"}, {MagicType::Kind::Message, "data"},
{MagicType::Kind::Message, "sig"} {MagicType::Kind::Message, "sig"},
{MagicType::Kind::MetaType, "creationCode"},
{MagicType::Kind::MetaType, "runtimeCode"}
}; };
set<MagicMember> static const payableMembers{ set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"} {MagicType::Kind::Message, "value"}

View File

@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break; case Kind::ABIDecode: id += "abidecode"; break;
case Kind::MetaType: id += "metatype"; break;
} }
id += "_" + stateMutabilityToString(m_stateMutability); id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncodePacked || m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector || m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature || m_kind == Kind::ABIEncodeWithSignature ||
m_kind == Kind::ABIDecode; m_kind == Kind::ABIDecode ||
m_kind == Kind::MetaType;
} }
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const
return string("module \"") + m_sourceUnit.annotation().path + string("\""); return string("module \"") + m_sourceUnit.annotation().path + string("\"");
} }
shared_ptr<MagicType> MagicType::metaType(TypePointer _type)
{
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
auto t = make_shared<MagicType>(Kind::MetaType);
t->m_typeArgument = std::move(_type);
return t;
}
string MagicType::richIdentifier() const string MagicType::richIdentifier() const
{ {
switch (m_kind) switch (m_kind)
@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const
return "t_magic_transaction"; return "t_magic_transaction";
case Kind::ABI: case Kind::ABI:
return "t_magic_abi"; return "t_magic_abi";
case Kind::MetaType:
solAssert(m_typeArgument, "");
return "t_magic_meta_type_" + m_typeArgument->richIdentifier();
} }
return ""; return "";
} }
@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
StateMutability::Pure StateMutability::Pure
)} )}
}); });
default: case Kind::MetaType:
solAssert(false, "Unknown kind of magic."); {
solAssert(
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
"Only contracts supported for now"
);
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.annotation().unimplementedFunctions.empty() && contract.constructorIsPublic())
return MemberList::MemberMap({
{"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)},
{"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)}
});
else
return {};
} }
}
solAssert(false, "Unknown kind of magic.");
return {};
} }
string MagicType::toString(bool) const string MagicType::toString(bool _short) const
{ {
switch (m_kind) switch (m_kind)
{ {
@ -3420,7 +3448,10 @@ string MagicType::toString(bool) const
return "tx"; return "tx";
case Kind::ABI: case Kind::ABI:
return "abi"; return "abi";
default: case Kind::MetaType:
solAssert(false, "Unknown kind of magic."); solAssert(m_typeArgument, "");
return "type(" + m_typeArgument->toString(_short) + ")";
} }
solAssert(false, "Unknown kind of magic.");
return {};
} }

View File

@ -989,6 +989,7 @@ public:
ABIEncodeWithSignature, ABIEncodeWithSignature,
ABIDecode, ABIDecode,
GasLeft, ///< gasleft() GasLeft, ///< gasleft()
MetaType ///< type(...)
}; };
Category category() const override { return Category::Function; } Category category() const override { return Category::Function; }
@ -1299,16 +1300,23 @@ private:
}; };
/** /**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference * Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference.
* (it always references a global singleton by name).
*/ */
class MagicType: public Type class MagicType: public Type
{ {
public: public:
enum class Kind { Block, Message, Transaction, ABI }; enum class Kind {
Block, ///< "block"
Message, ///< "msg"
Transaction, ///< "tx"
ABI, ///< "abi"
MetaType ///< "type(...)"
};
Category category() const override { return Category::Magic; } Category category() const override { return Category::Magic; }
explicit MagicType(Kind _kind): m_kind(_kind) {} explicit MagicType(Kind _kind): m_kind(_kind) {}
/// Factory function for meta type
static std::shared_ptr<MagicType> metaType(TypePointer _type);
TypeResult binaryOperatorResult(Token, TypePointer const&) const override TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{ {
@ -1329,6 +1337,9 @@ public:
private: private:
Kind m_kind; Kind m_kind;
/// Contract type used for contract metadata magic.
TypePointer m_typeArgument;
}; };
/** /**

View File

@ -1107,6 +1107,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::GasLeft: case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS; m_context << Instruction::GAS;
break; break;
case FunctionType::Kind::MetaType:
// No code to generate.
break;
} }
} }
return false; return false;

View File

@ -1551,6 +1551,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break; break;
case Token::Type:
// Inside expressions "type" is the name of a special, globally-available function.
nodeFactory.markEndPosition();
m_scanner->next();
expression = nodeFactory.createNode<Identifier>(make_shared<ASTString>("type"));
break;
case Token::LParen: case Token::LParen:
case Token::LBrack: case Token::LBrack:
{ {

View File

@ -277,7 +277,7 @@
"name" : "this", "name" : "this",
"nodeType" : "Identifier", "nodeType" : "Identifier",
"overloadedDeclarations" : [], "overloadedDeclarations" : [],
"referencedDeclaration" : 65, "referencedDeclaration" : 66,
"src" : "217:4:1", "src" : "217:4:1",
"typeDescriptions" : "typeDescriptions" :
{ {

View File

@ -424,7 +424,7 @@
[ [
null null
], ],
"referencedDeclaration" : 65, "referencedDeclaration" : 66,
"type" : "contract C", "type" : "contract C",
"value" : "this" "value" : "this"
}, },