mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
tmp
This commit is contained in:
parent
4357b0316b
commit
bd0e0fcdbe
@ -268,8 +268,10 @@ namespace solidity::langutil
|
||||
/* Yul-specific tokens, but not keywords. */ \
|
||||
T(Leave, "leave", 0) \
|
||||
\
|
||||
T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \
|
||||
/* Experimental Solidity specific keywords. */ \
|
||||
K(Word, "word", 0) \
|
||||
K(Void, "void", 0) \
|
||||
K(StaticAssert, "static_assert", 0) \
|
||||
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
|
||||
\
|
||||
@ -295,7 +297,10 @@ namespace TokenTraits
|
||||
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
|
||||
|
||||
// Predicates
|
||||
constexpr bool isElementaryTypeName(Token tok) { return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word; }
|
||||
constexpr bool isElementaryTypeName(Token tok)
|
||||
{
|
||||
return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word || tok == Token::Void;
|
||||
}
|
||||
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
|
||||
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
|
||||
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
|
||||
@ -331,11 +336,11 @@ namespace TokenTraits
|
||||
{
|
||||
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||
|
||||
tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let ||
|
||||
(tok >= Token::Word && tok < Token::ExperimentalEnd);
|
||||
(tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
|
||||
}
|
||||
constexpr bool isExperimentalSolidityOnlyKeyword(Token tok)
|
||||
{
|
||||
return tok >= Token::Word && tok < Token::ExperimentalEnd;
|
||||
return tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd;
|
||||
}
|
||||
|
||||
bool isYulKeyword(std::string const& _literal);
|
||||
|
@ -98,6 +98,7 @@ set(sources
|
||||
codegen/ReturnInfo.cpp
|
||||
codegen/YulUtilFunctions.h
|
||||
codegen/YulUtilFunctions.cpp
|
||||
codegen/experimental/IRGenerationContext.h
|
||||
codegen/experimental/IRGenerator.cpp
|
||||
codegen/experimental/IRGenerator.h
|
||||
codegen/experimental/IRGeneratorForStatements.cpp
|
||||
|
@ -36,7 +36,7 @@ bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
|
||||
for (auto source: _sourceUnits)
|
||||
if (!syntaxRestrictor.check(*source))
|
||||
return false;
|
||||
TypeInference typeInference{m_errorReporter};
|
||||
TypeInference typeInference{*this};
|
||||
for (auto source: _sourceUnits)
|
||||
if (!typeInference.analyze(*source))
|
||||
return false;
|
||||
|
@ -38,7 +38,11 @@ class Analysis
|
||||
{
|
||||
public:
|
||||
Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId);
|
||||
Analysis(Analysis const&) = delete;
|
||||
Analysis const& operator=(Analysis const&) = delete;
|
||||
bool check(std::vector<std::shared_ptr<SourceUnit const>> const& _sourceUnits);
|
||||
langutil::ErrorReporter& errorReporter() { return m_errorReporter; }
|
||||
uint64_t maxAstId() const { return m_maxAstId; }
|
||||
private:
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
uint64_t m_maxAstId = 0;
|
||||
|
@ -43,6 +43,7 @@ private:
|
||||
bool visit(ContractDefinition const& _contractDefinition) override;
|
||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
bool visit(ExpressionStatement const&) override { return true; }
|
||||
bool visit(FunctionCall const&) override { return true; }
|
||||
bool visit(Assignment const&) override { return true; }
|
||||
bool visit(Block const&) override { return true; }
|
||||
bool visit(InlineAssembly const&) override { return true; }
|
||||
|
@ -18,17 +18,39 @@
|
||||
|
||||
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
TypeInference::TypeInference(Analysis& _analysis):
|
||||
m_analysis(_analysis),
|
||||
m_errorReporter(_analysis.errorReporter())
|
||||
{
|
||||
for (auto [type, name, arity]: std::initializer_list<std::tuple<BuiltinType, const char*, uint64_t>> {
|
||||
{BuiltinType::Void, "void", 0},
|
||||
{BuiltinType::Function, "fun", 2},
|
||||
{BuiltinType::Unit, "unit", 0},
|
||||
{BuiltinType::Pair, "pair", 2},
|
||||
{BuiltinType::Word, "word", 0}
|
||||
})
|
||||
m_typeSystem.declareBuiltinType(type, name, arity);
|
||||
m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {});
|
||||
m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {});
|
||||
m_env = make_unique<TypeEnvironment>(m_typeSystem);
|
||||
|
||||
m_typeAnnotations.resize(_analysis.maxAstId());
|
||||
}
|
||||
|
||||
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
||||
{
|
||||
_sourceUnit.accept(*this);
|
||||
@ -37,12 +59,36 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
||||
|
||||
bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
ScopedSaveAndRestore env{m_env, {}};
|
||||
_functionDefinition.parameterList().accept(*this);
|
||||
if (_functionDefinition.returnParameterList())
|
||||
_functionDefinition.returnParameterList()->accept(*this);
|
||||
auto& functionAnnotation = annotation(_functionDefinition);
|
||||
if (functionAnnotation.type)
|
||||
return false;
|
||||
|
||||
_functionDefinition.body().accept(*this);
|
||||
Type functionType;
|
||||
{
|
||||
_functionDefinition.parameterList().accept(*this);
|
||||
if (_functionDefinition.returnParameterList())
|
||||
_functionDefinition.returnParameterList()->accept(*this);
|
||||
|
||||
_functionDefinition.body().accept(*this);
|
||||
|
||||
auto typeFromParameterList = [&](ParameterList const* _list) {
|
||||
if (!_list)
|
||||
return m_typeSystem.builtinType(BuiltinType::Unit, {});
|
||||
return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::view::transform([&](auto _param) {
|
||||
auto& argAnnotation = annotation(*_param);
|
||||
solAssert(argAnnotation.type);
|
||||
return *argAnnotation.type;
|
||||
}) | ranges::to<std::vector<Type>>);
|
||||
};
|
||||
|
||||
Type argType = typeFromParameterList(&_functionDefinition.parameterList());
|
||||
Type resultType = typeFromParameterList(_functionDefinition.returnParameterList().get());
|
||||
|
||||
functionType = m_typeSystem.builtinType(BuiltinType::Function, {argType, resultType});
|
||||
}
|
||||
functionAnnotation.type = functionType;
|
||||
|
||||
m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(m_typeSystem.resolve(functionType)));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -52,6 +98,20 @@ bool TypeInference::visit(ParameterList const&)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TypeInference::endVisit(ParameterList const& _parameterList)
|
||||
{
|
||||
auto& listAnnotation = annotation(_parameterList);
|
||||
solAssert(!listAnnotation.type);
|
||||
std::vector<Type> argTypes;
|
||||
for(auto arg: _parameterList.parameters())
|
||||
{
|
||||
auto& argAnnotation = annotation(*arg);
|
||||
solAssert(argAnnotation.type);
|
||||
argTypes.emplace_back(*argAnnotation.type);
|
||||
}
|
||||
listAnnotation.type = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||
}
|
||||
|
||||
bool TypeInference::visitNode(ASTNode const& _node)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference.");
|
||||
@ -65,7 +125,9 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
switch(elementaryTypeName->typeName().token())
|
||||
{
|
||||
case Token::Word:
|
||||
return WordType{};
|
||||
return m_wordType;
|
||||
case Token::Void:
|
||||
return m_voidType;
|
||||
default:
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
||||
break;
|
||||
@ -73,7 +135,8 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
||||
return m_env.freshFreeType();
|
||||
// TODO: free type?
|
||||
return m_typeSystem.freshTypeVariable();
|
||||
|
||||
}
|
||||
|
||||
@ -101,7 +164,9 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
Declaration const* declaration = identifierInfo.declaration;
|
||||
solAssert(!!declaration, "");
|
||||
|
||||
m_env.assignType(m_typeSystem, declaration, WordType{});
|
||||
auto& declarationAnnotation = annotation(*declaration);
|
||||
solAssert(declarationAnnotation.type);
|
||||
m_typeSystem.unify(*declarationAnnotation.type, m_wordType);
|
||||
identifierInfo.valueSize = 1;
|
||||
return true;
|
||||
};
|
||||
@ -120,19 +185,83 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
|
||||
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
|
||||
{
|
||||
Type type = _variableDeclaration.hasTypeName() ? fromTypeName(_variableDeclaration.typeName()) : m_typeSystem.freshTypeVariable();
|
||||
m_env.assignType(m_typeSystem, &_variableDeclaration, type);
|
||||
solAssert(!_variableDeclaration.value());
|
||||
auto& variableAnnotation = annotation(_variableDeclaration);
|
||||
solAssert(!variableAnnotation.type);
|
||||
variableAnnotation.type = [&] {
|
||||
if (_variableDeclaration.hasTypeName())
|
||||
return fromTypeName(_variableDeclaration.typeName());
|
||||
else
|
||||
return m_typeSystem.freshTypeVariable();
|
||||
}();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(Assignment const& _assignment)
|
||||
bool TypeInference::visit(Assignment const&)
|
||||
{
|
||||
(void)_assignment;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TypeInference::endVisit(Assignment const& _assignment)
|
||||
{
|
||||
auto& assignmentAnnotation = annotation(_assignment);
|
||||
solAssert(!assignmentAnnotation.type);
|
||||
|
||||
auto& lhsAnnotation = annotation(_assignment.leftHandSide());
|
||||
solAssert(lhsAnnotation.type);
|
||||
auto& rhsAnnotation = annotation(_assignment.rightHandSide());
|
||||
solAssert(rhsAnnotation.type);
|
||||
m_typeSystem.unify(*lhsAnnotation.type, *rhsAnnotation.type);
|
||||
assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type);
|
||||
}
|
||||
|
||||
TypeInference::TypeAnnotation& TypeInference::annotation(ASTNode const& _node)
|
||||
{
|
||||
auto& annotation = m_typeAnnotations.at(static_cast<size_t>(_node.id()));
|
||||
if (!annotation)
|
||||
annotation = make_unique<TypeAnnotation>();
|
||||
return *annotation;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(Identifier const& _identifier)
|
||||
{
|
||||
(void)_identifier;
|
||||
auto& identifierAnnotation = annotation(_identifier);
|
||||
solAssert(!identifierAnnotation.type);
|
||||
|
||||
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
|
||||
solAssert(referencedDeclaration);
|
||||
|
||||
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||
if (!declarationAnnotation.type)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(FunctionCall const&) { return true; }
|
||||
void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
auto& functionCallAnnotation = annotation(_functionCall);
|
||||
solAssert(!functionCallAnnotation.type);
|
||||
|
||||
auto& expressionAnnotation = annotation(_functionCall.expression());
|
||||
solAssert(expressionAnnotation.type);
|
||||
|
||||
Type functionType = m_typeSystem.fresh(*expressionAnnotation.type);
|
||||
|
||||
std::vector<Type> argTypes;
|
||||
for(auto arg: _functionCall.arguments())
|
||||
{
|
||||
auto& argAnnotation = annotation(*arg);
|
||||
solAssert(argAnnotation.type);
|
||||
argTypes.emplace_back(*argAnnotation.type);
|
||||
}
|
||||
Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable());
|
||||
m_typeSystem.unify(genericFunctionType, functionType);
|
||||
|
||||
functionCallAnnotation.type = m_typeSystem.resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_typeSystem.resolve(genericFunctionType))));
|
||||
}
|
||||
|
@ -27,10 +27,12 @@
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class Analysis;
|
||||
|
||||
class TypeInference: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
||||
TypeInference(Analysis& _analysis);
|
||||
|
||||
bool analyze(SourceUnit const& _sourceUnit);
|
||||
private:
|
||||
@ -40,6 +42,7 @@ private:
|
||||
|
||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
bool visit(ParameterList const& _parameterList) override;
|
||||
void endVisit(ParameterList const& _parameterList) override;
|
||||
bool visit(SourceUnit const&) override { return true; }
|
||||
bool visit(ContractDefinition const&) override { return true; }
|
||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||
@ -47,14 +50,29 @@ private:
|
||||
|
||||
bool visit(ExpressionStatement const&) override { return true; }
|
||||
bool visit(Assignment const&) override;
|
||||
void endVisit(Assignment const&) override;
|
||||
bool visit(Identifier const&) override;
|
||||
bool visit(FunctionCall const& _functionCall) override;
|
||||
void endVisit(FunctionCall const& _functionCall) override;
|
||||
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
|
||||
Type fromTypeName(TypeName const& _typeName);
|
||||
TypeSystem m_typeSystem;
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
TypeEnvironment m_env;
|
||||
TypeSystem m_typeSystem;
|
||||
std::unique_ptr<TypeEnvironment> m_env;
|
||||
Type m_voidType;
|
||||
Type m_wordType;
|
||||
|
||||
struct TypeAnnotation
|
||||
{
|
||||
std::optional<Type> type;
|
||||
};
|
||||
|
||||
TypeAnnotation& annotation(ASTNode const& _node);
|
||||
|
||||
std::vector<std::unique_ptr<TypeAnnotation>> m_typeAnnotations;
|
||||
};
|
||||
|
||||
}
|
@ -18,47 +18,225 @@
|
||||
|
||||
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <range/v3/to_container.hpp>
|
||||
#include <range/v3/view/drop_exactly.hpp>
|
||||
#include <range/v3/view/drop_last.hpp>
|
||||
#include <range/v3/view/reverse.hpp>
|
||||
#include <range/v3/view/zip.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::experimental;
|
||||
|
||||
void TypeEnvironment::assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment)
|
||||
std::string TypeSystem::typeToString(Type const& _type) const
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](AtomicType const& _type) {
|
||||
std::stringstream stream;
|
||||
if (!_type.arguments.empty())
|
||||
{
|
||||
stream << "(";
|
||||
for (auto type: _type.arguments | ranges::views::drop_last(1))
|
||||
stream << typeToString(type) << ", ";
|
||||
stream << typeToString(_type.arguments.back());
|
||||
stream << ") ";
|
||||
}
|
||||
stream << std::visit(util::GenericVisitor{
|
||||
[&](Declaration const* _declaration) {
|
||||
return _declaration->name();
|
||||
},
|
||||
[&](BuiltinType _builtinType) -> string {
|
||||
return builtinTypeName(_builtinType);
|
||||
}
|
||||
}, _type.constructor);
|
||||
return stream.str();
|
||||
},
|
||||
[](FreeTypeVariable const& _type) {
|
||||
return fmt::format("free[{}]", _type.index());
|
||||
},
|
||||
[](GenericTypeVariable const& _type) {
|
||||
return fmt::format("var[{}]", _type.index());
|
||||
},
|
||||
}, resolve(_type));
|
||||
}
|
||||
|
||||
void TypeEnvironment::assignType(Declaration const* _declaration, Type _typeAssignment)
|
||||
{
|
||||
auto&& [type, newlyInserted] = m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment)));
|
||||
if (!newlyInserted)
|
||||
{
|
||||
unify(_typeSystem, type->second, _typeAssignment);
|
||||
m_parent.unify(type->second, _typeAssignment);
|
||||
}
|
||||
}
|
||||
Type TypeEnvironment::lookup(TypeSystem& _typeSystem, Declaration const* _declaration)
|
||||
std::optional<experimental::Type> TypeEnvironment::lookup(Declaration const* _declaration)
|
||||
{
|
||||
if (m_types.count(_declaration))
|
||||
return m_types[_declaration];
|
||||
Type result = _typeSystem.freshTypeVariable();
|
||||
m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(result));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
void TypeSystem::unify(Type _a, Type _b)
|
||||
{
|
||||
auto unificationFailure = [&]() {
|
||||
solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b)));
|
||||
};
|
||||
_a = resolve(_a);
|
||||
_b = resolve(_b);
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](GenericTypeVariable _left, GenericTypeVariable _right) {
|
||||
validate(_left);
|
||||
validate(_right);
|
||||
if (_left.index() != _right.index())
|
||||
instantiate(_left, _right);
|
||||
},
|
||||
[&](GenericTypeVariable _var, auto) {
|
||||
instantiate(_var, _b);
|
||||
},
|
||||
[&](auto, GenericTypeVariable _var) {
|
||||
instantiate(_var, _a);
|
||||
},
|
||||
[&](AtomicType _atomicLeft, AtomicType _atomicRight) {
|
||||
if(_atomicLeft.constructor != _atomicRight.constructor)
|
||||
unificationFailure();
|
||||
if (_atomicLeft.arguments.size() != _atomicRight.arguments.size())
|
||||
unificationFailure();
|
||||
for (auto&& [left, right]: ranges::zip_view(_atomicLeft.arguments, _atomicRight.arguments))
|
||||
unify(left, right);
|
||||
},
|
||||
[&](auto, auto) {
|
||||
unificationFailure();
|
||||
}
|
||||
}, _a, _b);
|
||||
}
|
||||
|
||||
unique_ptr<TypeEnvironment> TypeEnvironment::fresh() const
|
||||
{
|
||||
auto newEnv = make_unique<TypeEnvironment>(m_parent);
|
||||
for(auto [decl,type]: m_types)
|
||||
newEnv->m_types.emplace(decl, type);
|
||||
return newEnv;
|
||||
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::freshTypeVariable()
|
||||
{
|
||||
uint64_t index = m_typeVariables.size();
|
||||
m_typeVariables.emplace_back(std::nullopt);
|
||||
return GenericTypeVariable(*this, index);
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::freshFreeType()
|
||||
{
|
||||
uint64_t index = m_freeTypes.size();
|
||||
m_freeTypes.emplace_back(std::nullopt);
|
||||
return FreeTypeVariable(*this, index);
|
||||
}
|
||||
|
||||
void TypeSystem::instantiate(GenericTypeVariable _variable, Type _type)
|
||||
{
|
||||
validate(_variable);
|
||||
solAssert(!m_typeVariables.at(_variable.index()).has_value());
|
||||
solAssert(_variable.m_parent == this);
|
||||
m_typeVariables[_variable.index()] = _type;
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::resolve(Type _type) const
|
||||
{
|
||||
Type result = _type;
|
||||
while(auto const* var = std::get_if<GenericTypeVariable>(&result))
|
||||
if (auto value = m_typeVariables.at(var->index()))
|
||||
result = *value;
|
||||
else
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
Type TypeEnvironment::freshFreeType()
|
||||
|
||||
void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity)
|
||||
{
|
||||
return FreeType{m_numFreeTypes++};
|
||||
solAssert(m_builtinTypes.count(_builtinType) == 0, "Builtin type already declared.");
|
||||
m_builtinTypes[_builtinType] = TypeConstructorInfo{
|
||||
_name,
|
||||
_arity
|
||||
};
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const
|
||||
{
|
||||
auto const& info = m_builtinTypes.at(_builtinType);
|
||||
solAssert(info.arity == _arguments.size(), "Invalid arity.");
|
||||
return AtomicType{_builtinType, _arguments};
|
||||
}
|
||||
|
||||
void TypeSystem::validate(GenericTypeVariable _variable) const
|
||||
{
|
||||
solAssert(_variable.m_parent == this);
|
||||
solAssert(_variable.index() < m_typeVariables.size());
|
||||
}
|
||||
|
||||
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
|
||||
{
|
||||
if (_elements.empty())
|
||||
return typeSystem.builtinType(BuiltinType::Unit, {});
|
||||
if (_elements.size() == 1)
|
||||
return _elements.front();
|
||||
Type result = _elements.back();
|
||||
for (Type type: _elements | ranges::view::reverse | ranges::view::drop_exactly(1))
|
||||
result = typeSystem.builtinType(BuiltinType::Pair, {type, result});
|
||||
return result;
|
||||
}
|
||||
|
||||
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
|
||||
{
|
||||
return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType});
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::fresh(Type _type)
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](AtomicType const& _type) -> Type {
|
||||
return AtomicType{
|
||||
_type.constructor,
|
||||
_type.arguments | ranges::view::transform([&](Type _argType) {
|
||||
return fresh(_argType);
|
||||
}) | ranges::to<vector<Type>>
|
||||
};
|
||||
},
|
||||
[&](FreeTypeVariable const&) {
|
||||
return _type;
|
||||
},
|
||||
[&](GenericTypeVariable const&) {
|
||||
return freshTypeVariable();
|
||||
},
|
||||
}, resolve(_type));
|
||||
}
|
||||
|
||||
tuple<AtomicType::Constructor, vector<experimental::Type>> TypeSystemHelpers::destAtomicType(Type _functionType) const
|
||||
{
|
||||
using ResultType = tuple<AtomicType::Constructor, vector<Type>>;
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](AtomicType const& _type) -> ResultType {
|
||||
return std::make_tuple(_type.constructor, _type.arguments);
|
||||
},
|
||||
[](auto) -> ResultType {
|
||||
solAssert(false);
|
||||
}
|
||||
}, _functionType);
|
||||
}
|
||||
|
||||
|
||||
void TypeEnvironment::unify(TypeSystem& _context, Type _a, Type _b)
|
||||
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
|
||||
{
|
||||
_a = _context.resolve(_a);
|
||||
_b = _context.resolve(_b);
|
||||
if (auto* varA = get_if<TypeVariable>(&_a))
|
||||
_context.instantiate(*varA, _b);
|
||||
else if (holds_alternative<WordType>(_a))
|
||||
{
|
||||
if (holds_alternative<WordType>(_b))
|
||||
return;
|
||||
else
|
||||
solAssert(false, "unification failed");
|
||||
}
|
||||
|
||||
solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b)));
|
||||
auto [constructor, arguments] = destAtomicType(_functionType);
|
||||
auto const* builtinType = get_if<BuiltinType>(&constructor);
|
||||
solAssert(builtinType && *builtinType == BuiltinType::Function);
|
||||
solAssert(arguments.size() == 2);
|
||||
return make_tuple(arguments.front(), arguments.back());
|
||||
}
|
@ -19,8 +19,6 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
@ -35,99 +33,64 @@ namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class TypeSystem;
|
||||
class TypeEnvironment;
|
||||
|
||||
struct SumType;
|
||||
struct TupleType;
|
||||
struct FunctionType;
|
||||
struct WordType;
|
||||
struct UserDefinedType;
|
||||
struct TypeVariable;
|
||||
struct FreeType;
|
||||
struct AtomicType;
|
||||
struct GenericTypeVariable;
|
||||
struct FreeTypeVariable;
|
||||
|
||||
using Type = std::variant<SumType, TupleType, FunctionType, WordType, UserDefinedType, TypeVariable, FreeType>;
|
||||
using Type = std::variant<AtomicType, GenericTypeVariable, FreeTypeVariable>;
|
||||
|
||||
struct SumType
|
||||
enum class BuiltinType
|
||||
{
|
||||
std::vector<Type const*> alternatives;
|
||||
std::string toString() const
|
||||
{
|
||||
return "sum";
|
||||
}
|
||||
Void,
|
||||
Function,
|
||||
Unit,
|
||||
Pair,
|
||||
Word
|
||||
};
|
||||
|
||||
struct TupleType
|
||||
struct AtomicType
|
||||
{
|
||||
std::vector<Type const*> components;
|
||||
std::string toString() const
|
||||
{
|
||||
return "tuple";
|
||||
}
|
||||
using Constructor = std::variant<BuiltinType, Declaration const*>;
|
||||
Constructor constructor;
|
||||
std::vector<Type> arguments;
|
||||
|
||||
};
|
||||
|
||||
struct FunctionType
|
||||
struct FreeTypeVariable
|
||||
{
|
||||
Type const* codomain = nullptr;
|
||||
Type const* domain = nullptr;
|
||||
std::string toString() const
|
||||
{
|
||||
return "fun";
|
||||
}
|
||||
};
|
||||
|
||||
struct WordType
|
||||
{
|
||||
std::string toString() const
|
||||
{
|
||||
return "word";
|
||||
}
|
||||
};
|
||||
|
||||
struct UserDefinedType
|
||||
{
|
||||
Declaration const* declaration = nullptr;
|
||||
std::vector<Type const*> arguments;
|
||||
std::string toString() const
|
||||
{
|
||||
return "user_defined_type";
|
||||
}
|
||||
};
|
||||
|
||||
struct TypeVariable
|
||||
{
|
||||
std::string toString() const
|
||||
{
|
||||
return fmt::format("var<{}>", m_index);
|
||||
}
|
||||
private:
|
||||
TypeSystem const& parent() const { return *m_parent; }
|
||||
uint64_t index() const { return m_index; }
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
TypeSystem const* m_parent = nullptr;
|
||||
uint64_t m_index = 0;
|
||||
TypeVariable(uint64_t _index): m_index(_index) {}
|
||||
FreeTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {}
|
||||
};
|
||||
|
||||
struct FreeType
|
||||
struct GenericTypeVariable
|
||||
{
|
||||
uint64_t index = 0;
|
||||
std::string toString() const
|
||||
{
|
||||
return fmt::format("free<{}>", index);
|
||||
}
|
||||
TypeSystem const& parent() const { return *m_parent; }
|
||||
uint64_t index() const { return m_index; }
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
TypeSystem const* m_parent = nullptr;
|
||||
uint64_t m_index = 0;
|
||||
GenericTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {}
|
||||
};
|
||||
|
||||
inline std::string typeToString(Type const& _type)
|
||||
{
|
||||
return std::visit([](auto _type) { return _type.toString(); }, _type);
|
||||
}
|
||||
|
||||
class TypeEnvironment
|
||||
{
|
||||
public:
|
||||
void assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment);
|
||||
Type lookup(TypeSystem& _typeSystem, Declaration const* _declaration);
|
||||
Type freshFreeType();
|
||||
void unify(TypeSystem& _typeSystem, Type _a, Type _b);
|
||||
TypeEnvironment(TypeSystem& _parent): m_parent(_parent) {}
|
||||
TypeEnvironment(TypeEnvironment const& _env) = delete;
|
||||
TypeEnvironment& operator=(TypeEnvironment const& _env) = delete;
|
||||
std::unique_ptr<TypeEnvironment> fresh() const;
|
||||
void assignType(Declaration const* _declaration, Type _typeAssignment);
|
||||
std::optional<Type> lookup(Declaration const* _declaration);
|
||||
private:
|
||||
uint64_t m_numFreeTypes = 0;
|
||||
TypeSystem& m_parent;
|
||||
std::map<Declaration const*, Type> m_types;
|
||||
};
|
||||
|
||||
@ -138,30 +101,38 @@ public:
|
||||
TypeSystem() {}
|
||||
TypeSystem(TypeSystem const&) = delete;
|
||||
TypeSystem const& operator=(TypeSystem const&) = delete;
|
||||
Type freshTypeVariable()
|
||||
void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity);
|
||||
Type builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const;
|
||||
std::string builtinTypeName(BuiltinType _builtinType) const
|
||||
{
|
||||
uint64_t index = m_typeVariables.size();
|
||||
m_typeVariables.emplace_back(std::nullopt);
|
||||
return TypeVariable(index);
|
||||
}
|
||||
void instantiate(TypeVariable _variable, Type _type)
|
||||
{
|
||||
solAssert(_variable.index() < m_typeVariables.size());
|
||||
solAssert(!m_typeVariables.at(_variable.index()).has_value());
|
||||
m_typeVariables[_variable.index()] = _type;
|
||||
}
|
||||
Type resolve(Type _type)
|
||||
{
|
||||
Type result = _type;
|
||||
while(auto const* var = std::get_if<TypeVariable>(&result))
|
||||
if (auto value = m_typeVariables.at(var->index()))
|
||||
result = *value;
|
||||
else
|
||||
break;
|
||||
return result;
|
||||
return m_builtinTypes.at(_builtinType).name;
|
||||
}
|
||||
Type freshFreeType();
|
||||
void instantiate(GenericTypeVariable _variable, Type _type);
|
||||
void validate(GenericTypeVariable _variable) const;
|
||||
Type resolve(Type _type) const;
|
||||
std::string typeToString(Type const& _type) const;
|
||||
Type freshTypeVariable();
|
||||
Type fresh(Type _type);
|
||||
void unify(Type _a, Type _b);
|
||||
private:
|
||||
std::vector<std::optional<Type>> m_freeTypes;
|
||||
struct TypeConstructorInfo
|
||||
{
|
||||
std::string name;
|
||||
uint64_t arity = 0;
|
||||
};
|
||||
std::map<BuiltinType, TypeConstructorInfo> m_builtinTypes;
|
||||
std::vector<std::optional<Type>> m_typeVariables;
|
||||
};
|
||||
|
||||
struct TypeSystemHelpers
|
||||
{
|
||||
TypeSystem& typeSystem;
|
||||
Type tupleType(std::vector<Type> _elements) const;
|
||||
Type functionType(Type _argType, Type _resultType) const;
|
||||
std::tuple<AtomicType::Constructor, std::vector<Type>> destAtomicType(Type _functionType) const;
|
||||
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
|
||||
};
|
||||
|
||||
}
|
43
libsolidity/codegen/experimental/IRGenerationContext.h
Normal file
43
libsolidity/codegen/experimental/IRGenerationContext.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class Analysis;
|
||||
|
||||
struct IRGenerationContext
|
||||
{
|
||||
Analysis const& analysis;
|
||||
std::list<FunctionDefinition const*> functionQueue;
|
||||
std::set<FunctionDefinition const*> generatedFunctions;
|
||||
void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition)
|
||||
{
|
||||
if (!generatedFunctions.count(_functionDefinition))
|
||||
functionQueue.emplace_back(_functionDefinition);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerator.h>
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/Common.h>
|
||||
@ -31,6 +32,8 @@
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <range/v3/view/drop_last.hpp>
|
||||
|
||||
#include <variant>
|
||||
|
||||
using namespace std;
|
||||
@ -43,7 +46,7 @@ string IRGenerator::run(
|
||||
ContractDefinition const& _contract,
|
||||
bytes const& /*_cborMetadata*/,
|
||||
map<ContractDefinition const*, string_view const> const& /*_otherYulSources*/
|
||||
) const
|
||||
)
|
||||
{
|
||||
|
||||
Whiskers t(R"(
|
||||
@ -66,30 +69,52 @@ string IRGenerator::run(
|
||||
return t.render();
|
||||
}
|
||||
|
||||
string IRGenerator::generate(ContractDefinition const& _contract) const
|
||||
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
{
|
||||
std::stringstream code;
|
||||
code << "{\n";
|
||||
if (_contract.fallbackFunction())
|
||||
{
|
||||
code << IRNames::function(*_contract.fallbackFunction()) << "()\n";
|
||||
m_context.functionQueue.emplace_front(_contract.fallbackFunction());
|
||||
}
|
||||
code << "revert(0,0)\n";
|
||||
code << "}\n";
|
||||
|
||||
for (FunctionDefinition const* f: _contract.definedFunctions())
|
||||
code << generate(*f);
|
||||
while (!m_context.functionQueue.empty())
|
||||
{
|
||||
FunctionDefinition const* function = m_context.functionQueue.front();
|
||||
m_context.functionQueue.pop_front();
|
||||
m_context.generatedFunctions.insert(function);
|
||||
code << generate(*function);
|
||||
}
|
||||
|
||||
return code.str();
|
||||
}
|
||||
|
||||
string IRGenerator::generate(FunctionDefinition const& _function) const
|
||||
string IRGenerator::generate(FunctionDefinition const& _function)
|
||||
{
|
||||
std::stringstream code;
|
||||
code << "function " << IRNames::function(_function) << "() {\n";
|
||||
code << "function " << IRNames::function(_function) << "(";
|
||||
if (_function.parameters().size() > 1)
|
||||
for (auto const& arg: _function.parameters() | ranges::view::drop_last(1))
|
||||
code << IRNames::localVariable(*arg) << ", ";
|
||||
if (!_function.parameters().empty())
|
||||
code << IRNames::localVariable(*_function.parameters().back());
|
||||
code << ")";
|
||||
if (_function.returnParameterList() && !_function.returnParameters().empty())
|
||||
{
|
||||
code << " -> ";
|
||||
if (_function.returnParameters().size() > 1)
|
||||
for (auto const& arg: _function.returnParameters() | ranges::view::drop_last(1))
|
||||
code << IRNames::localVariable(*arg) << ", ";
|
||||
if (!_function.returnParameters().empty())
|
||||
code << IRNames::localVariable(*_function.returnParameters().back());
|
||||
}
|
||||
code << "{\n";
|
||||
for (auto _statement: _function.body().statements())
|
||||
{
|
||||
IRGeneratorForStatements statementGenerator{m_analysis};
|
||||
IRGeneratorForStatements statementGenerator{m_context};
|
||||
code << statementGenerator.generate(*_statement);
|
||||
}
|
||||
code << "}\n";
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||
#include <libsolidity/interface/DebugSettings.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
@ -53,24 +54,24 @@ public:
|
||||
m_eofVersion(_eofVersion),
|
||||
m_debugInfoSelection(_debugInfoSelection),
|
||||
m_soliditySourceProvider(_soliditySourceProvider),
|
||||
m_analysis(_analysis)
|
||||
m_context{_analysis, {}, {}}
|
||||
{}
|
||||
|
||||
std::string run(
|
||||
ContractDefinition const& _contract,
|
||||
bytes const& _cborMetadata,
|
||||
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||
) const;
|
||||
);
|
||||
|
||||
std::string generate(ContractDefinition const& _contract) const;
|
||||
std::string generate(FunctionDefinition const& _function) const;
|
||||
std::string generate(ContractDefinition const& _contract);
|
||||
std::string generate(FunctionDefinition const& _function);
|
||||
private:
|
||||
langutil::EVMVersion const m_evmVersion;
|
||||
std::optional<uint8_t> const m_eofVersion;
|
||||
OptimiserSettings const m_optimiserSettings;
|
||||
langutil::DebugInfoSelection m_debugInfoSelection = {};
|
||||
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
||||
Analysis const& m_analysis;
|
||||
IRGenerationContext m_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include <libsolidity/codegen/ir/Common.h>
|
||||
|
||||
#include <range/v3/view/drop_last.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@ -104,7 +106,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly)
|
||||
CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences};
|
||||
yul::Statement modified = bodyCopier(_assembly.operations());
|
||||
solAssert(holds_alternative<yul::Block>(modified));
|
||||
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified));
|
||||
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -119,6 +121,55 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(ExpressionStatement const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto const* rhsVar = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
|
||||
solAssert(rhsVar, "Can only reference identifiers referring to variables.");
|
||||
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*rhsVar) << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
for(auto arg: _functionCall.arguments())
|
||||
arg->accept(*this);
|
||||
|
||||
auto const* identifier = dynamic_cast<Identifier const*>(&_functionCall.expression());
|
||||
solAssert(identifier, "Complex function call expressions not supported.");
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration);
|
||||
solAssert(functionDefinition, "Function call expression must refer to a function definition.");
|
||||
m_context.enqueueFunctionDefinition(functionDefinition);
|
||||
|
||||
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition) << "(";
|
||||
auto const& arguments = _functionCall.arguments();
|
||||
if (arguments.size() > 1)
|
||||
for (auto arg: arguments | ranges::view::drop_last(1))
|
||||
m_code << IRNames::localVariable(*arg) << ", ";
|
||||
if (!arguments.empty())
|
||||
m_code << IRNames::localVariable(*arguments.back());
|
||||
m_code << ")\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
{
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
auto const* lhs = dynamic_cast<Identifier const*>(&_assignment.leftHandSide());
|
||||
solAssert(lhs, "Can only assign to identifiers.");
|
||||
auto const* lhsVar = dynamic_cast<VariableDeclaration const*>(lhs->annotation().referencedDeclaration);
|
||||
solAssert(lhsVar, "Can only assign to identifiers referring to variables.");
|
||||
m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n";
|
||||
|
||||
m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IRGeneratorForStatements::visitNode(ASTNode const&)
|
||||
{
|
||||
solAssert(false, "Unsupported AST node during statement code generation.");
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <functional>
|
||||
@ -30,15 +31,19 @@ class Analysis;
|
||||
class IRGeneratorForStatements: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
IRGeneratorForStatements(Analysis const& _analysis): m_analysis(_analysis) {}
|
||||
IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {}
|
||||
|
||||
std::string generate(ASTNode const& _node);
|
||||
private:
|
||||
bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
bool visit(FunctionCall const&) override;
|
||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||
/// Default visit will reject all AST nodes that are not explicitly supported.
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
Analysis const& m_analysis;
|
||||
IRGenerationContext& m_context;
|
||||
std::stringstream m_code;
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
pragma experimental solidity;
|
||||
|
||||
function f(a:word) -> (b:word) {
|
||||
assembly {
|
||||
b := a
|
||||
}
|
||||
function f(a) -> (b) {
|
||||
b = a;
|
||||
}
|
||||
|
||||
contract C {
|
||||
@ -13,7 +11,7 @@ contract C {
|
||||
assembly {
|
||||
x := 0x42
|
||||
}
|
||||
y = x;
|
||||
y = f(x);
|
||||
assembly {
|
||||
mstore(0, y)
|
||||
return(0, 32)
|
||||
@ -23,4 +21,4 @@ contract C {
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// () -> 0x42
|
||||
// () -> 21
|
||||
|
Loading…
Reference in New Issue
Block a user