mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	tmp
This commit is contained in:
		
							parent
							
								
									71f7bf7206
								
							
						
					
					
						commit
						4357b0316b
					
				| @ -330,7 +330,7 @@ namespace TokenTraits | |||||||
| 	constexpr bool isExperimentalSolidityKeyword(Token tok) | 	constexpr bool isExperimentalSolidityKeyword(Token tok) | ||||||
| 	{ | 	{ | ||||||
| 		return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback || | 		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::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let || | ||||||
| 			(tok >= Token::Word && tok < Token::ExperimentalEnd); | 			(tok >= Token::Word && tok < Token::ExperimentalEnd); | ||||||
| 	} | 	} | ||||||
| 	constexpr bool isExperimentalSolidityOnlyKeyword(Token tok) | 	constexpr bool isExperimentalSolidityOnlyKeyword(Token tok) | ||||||
|  | |||||||
| @ -74,6 +74,8 @@ set(sources | |||||||
| 	ast/Types.h | 	ast/Types.h | ||||||
| 	ast/TypeProvider.cpp | 	ast/TypeProvider.cpp | ||||||
| 	ast/TypeProvider.h | 	ast/TypeProvider.h | ||||||
|  | 	ast/experimental/TypeSystem.cpp | ||||||
|  | 	ast/experimental/TypeSystem.h | ||||||
| 	codegen/ABIFunctions.cpp | 	codegen/ABIFunctions.cpp | ||||||
| 	codegen/ABIFunctions.h | 	codegen/ABIFunctions.h | ||||||
| 	codegen/ArrayUtils.cpp | 	codegen/ArrayUtils.cpp | ||||||
|  | |||||||
| @ -18,14 +18,27 @@ | |||||||
| #include <libsolidity/analysis/experimental/Analysis.h> | #include <libsolidity/analysis/experimental/Analysis.h> | ||||||
| 
 | 
 | ||||||
| #include <libsolidity/analysis/experimental/SyntaxRestrictor.h> | #include <libsolidity/analysis/experimental/SyntaxRestrictor.h> | ||||||
|  | #include <libsolidity/analysis/experimental/TypeInference.h> | ||||||
| 
 | 
 | ||||||
|  | using namespace std; | ||||||
| using namespace solidity::langutil; | using namespace solidity::langutil; | ||||||
| using namespace solidity::frontend::experimental; | using namespace solidity::frontend::experimental; | ||||||
| 
 | 
 | ||||||
| bool Analysis::check(ASTNode const& _node) | Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): | ||||||
|  | 	m_errorReporter(_errorReporter), | ||||||
|  | 	m_maxAstId(_maxAstId) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits) | ||||||
| { | { | ||||||
| 	SyntaxRestrictor syntaxRestrictor{m_errorReporter}; | 	SyntaxRestrictor syntaxRestrictor{m_errorReporter}; | ||||||
| 	if (!syntaxRestrictor.check(_node)) | 	for (auto source: _sourceUnits) | ||||||
|  | 		if (!syntaxRestrictor.check(*source)) | ||||||
|  | 			return false; | ||||||
|  | 	TypeInference typeInference{m_errorReporter}; | ||||||
|  | 	for (auto source: _sourceUnits) | ||||||
|  | 		if (!typeInference.analyze(*source)) | ||||||
| 			return false; | 			return false; | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,10 +18,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
| 
 | 
 | ||||||
| namespace solidity::frontend | namespace solidity::frontend | ||||||
| { | { | ||||||
| class ASTNode; | class SourceUnit; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace solidity::langutil | namespace solidity::langutil | ||||||
| @ -35,11 +37,8 @@ namespace solidity::frontend::experimental | |||||||
| class Analysis | class Analysis | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): | 	Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId); | ||||||
| 	m_errorReporter(_errorReporter), | 	bool check(std::vector<std::shared_ptr<SourceUnit const>> const& _sourceUnits); | ||||||
| 	m_maxAstId(_maxAstId) |  | ||||||
| 	{} |  | ||||||
| 	bool check(ASTNode const& _ast); |  | ||||||
| private: | private: | ||||||
| 	langutil::ErrorReporter& m_errorReporter; | 	langutil::ErrorReporter& m_errorReporter; | ||||||
| 	uint64_t m_maxAstId = 0; | 	uint64_t m_maxAstId = 0; | ||||||
|  | |||||||
| @ -49,10 +49,10 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) | |||||||
| { | { | ||||||
| 	if (!_functionDefinition.isImplemented()) | 	if (!_functionDefinition.isImplemented()) | ||||||
| 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Functions must be implemented."); | 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Functions must be implemented."); | ||||||
| 	if (!_functionDefinition.parameterList().parameters().empty()) | 	if (!_functionDefinition.modifiers().empty()) | ||||||
| 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have arguments."); | 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have modifiers."); | ||||||
| 	if (_functionDefinition.returnParameterList() && !_functionDefinition.returnParameterList()->parameters().empty()) | 	if (_functionDefinition.overrides()) | ||||||
| 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have return variables."); | 		m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Function may not have override specifiers."); | ||||||
| 	if (_functionDefinition.isFree()) | 	if (_functionDefinition.isFree()) | ||||||
| 	{ | 	{ | ||||||
| 		if (_functionDefinition.stateMutability() != StateMutability::NonPayable) | 		if (_functionDefinition.stateMutability() != StateMutability::NonPayable) | ||||||
| @ -69,9 +69,7 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) | |||||||
| 			m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); | 			m_errorReporter.syntaxError(0000_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_functionDefinition.body().accept(*this); | 	return true; | ||||||
| 
 |  | ||||||
| 	return false; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement) | bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement) | ||||||
|  | |||||||
| @ -42,12 +42,15 @@ private: | |||||||
| 	bool visit(ImportDirective const&) override { return true; } | 	bool visit(ImportDirective const&) override { return true; } | ||||||
| 	bool visit(ContractDefinition const& _contractDefinition) override; | 	bool visit(ContractDefinition const& _contractDefinition) override; | ||||||
| 	bool visit(FunctionDefinition const& _functionDefinition) override; | 	bool visit(FunctionDefinition const& _functionDefinition) override; | ||||||
|  | 	bool visit(ExpressionStatement const&) override { return true; } | ||||||
|  | 	bool visit(Assignment const&) override { return true; } | ||||||
| 	bool visit(Block const&) override { return true; } | 	bool visit(Block const&) override { return true; } | ||||||
| 	bool visit(InlineAssembly const&) override { return true; } | 	bool visit(InlineAssembly const&) override { return true; } | ||||||
| 	bool visit(Identifier const&) override { return true; } | 	bool visit(Identifier const&) override { return true; } | ||||||
| 	bool visit(VariableDeclarationStatement const&) override; | 	bool visit(VariableDeclarationStatement const&) override; | ||||||
| 	bool visit(VariableDeclaration const&) override; | 	bool visit(VariableDeclaration const&) override; | ||||||
| 	bool visit(ElementaryTypeName const&) override { return true; } | 	bool visit(ElementaryTypeName const&) override { return true; } | ||||||
|  | 	bool visit(ParameterList const&) override { return true; } | ||||||
| 
 | 
 | ||||||
| 	langutil::ErrorReporter& m_errorReporter; | 	langutil::ErrorReporter& m_errorReporter; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -20,18 +20,119 @@ | |||||||
| #include <libsolidity/analysis/experimental/TypeInference.h> | #include <libsolidity/analysis/experimental/TypeInference.h> | ||||||
| #include <liblangutil/Exceptions.h> | #include <liblangutil/Exceptions.h> | ||||||
| 
 | 
 | ||||||
|  | #include <libyul/AsmAnalysis.h> | ||||||
|  | #include <libyul/AsmAnalysisInfo.h> | ||||||
|  | #include <libyul/AST.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
| using namespace solidity::frontend; | using namespace solidity::frontend; | ||||||
| using namespace solidity::frontend::experimental; | using namespace solidity::frontend::experimental; | ||||||
| using namespace solidity::langutil; | using namespace solidity::langutil; | ||||||
| 
 | 
 | ||||||
|  | bool TypeInference::analyze(SourceUnit const& _sourceUnit) | ||||||
|  | { | ||||||
|  | 	_sourceUnit.accept(*this); | ||||||
|  | 	return !m_errorReporter.hasErrors(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(FunctionDefinition const& _functionDefinition) | ||||||
|  | { | ||||||
|  | 	ScopedSaveAndRestore env{m_env, {}}; | ||||||
|  | 	_functionDefinition.parameterList().accept(*this); | ||||||
|  | 	if (_functionDefinition.returnParameterList()) | ||||||
|  | 		_functionDefinition.returnParameterList()->accept(*this); | ||||||
|  | 
 | ||||||
|  | 	_functionDefinition.body().accept(*this); | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(ParameterList const&) | ||||||
|  | { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool TypeInference::visitNode(ASTNode const& _node) | bool TypeInference::visitNode(ASTNode const& _node) | ||||||
| { | { | ||||||
| 	m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference."); | 	m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference."); | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TypeInference::visit(VariableDeclaration const&) | experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) | ||||||
| { | { | ||||||
| //	m_env.assignType(&_varialeDeclaration, m_env.lookupType(_varialeDeclaration.typeName()));
 | 	if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName)) | ||||||
|  | 	{ | ||||||
|  | 		switch(elementaryTypeName->typeName().token()) | ||||||
|  | 		{ | ||||||
|  | 		case Token::Word: | ||||||
|  | 			return WordType{}; | ||||||
|  | 		default: | ||||||
|  | 			m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); | ||||||
|  | 	return m_env.freshFreeType(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(InlineAssembly const& _inlineAssembly) | ||||||
|  | { | ||||||
|  | 	// External references have already been resolved in a prior stage and stored in the annotation.
 | ||||||
|  | 	// We run the resolve step again regardless.
 | ||||||
|  | 	yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( | ||||||
|  | 		yul::Identifier const& _identifier, | ||||||
|  | 		yul::IdentifierContext _context, | ||||||
|  | 		bool | ||||||
|  | 	) -> bool | ||||||
|  | 	{ | ||||||
|  | 		if (_context == yul::IdentifierContext::NonExternal) | ||||||
|  | 		{ | ||||||
|  | 			// Hack until we can disallow any shadowing: If we found an internal reference,
 | ||||||
|  | 			// clear the external references, so that codegen does not use it.
 | ||||||
|  | 			_inlineAssembly.annotation().externalReferences.erase(& _identifier); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); | ||||||
|  | 		if (ref == _inlineAssembly.annotation().externalReferences.end()) | ||||||
|  | 			return false; | ||||||
|  | 		InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; | ||||||
|  | 		Declaration const* declaration = identifierInfo.declaration; | ||||||
|  | 		solAssert(!!declaration, ""); | ||||||
|  | 
 | ||||||
|  | 		m_env.assignType(m_typeSystem, declaration, WordType{}); | ||||||
|  | 		identifierInfo.valueSize = 1; | ||||||
|  | 		return true; | ||||||
|  | 	}; | ||||||
|  | 	solAssert(!_inlineAssembly.annotation().analysisInfo, ""); | ||||||
|  | 	_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>(); | ||||||
|  | 	yul::AsmAnalyzer analyzer( | ||||||
|  | 		*_inlineAssembly.annotation().analysisInfo, | ||||||
|  | 		m_errorReporter, | ||||||
|  | 		_inlineAssembly.dialect(), | ||||||
|  | 		identifierAccess | ||||||
|  | 	); | ||||||
|  | 	if (!analyzer.analyze(_inlineAssembly.operations())) | ||||||
|  | 		solAssert(m_errorReporter.hasErrors()); | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) | ||||||
|  | { | ||||||
|  | 	Type type = _variableDeclaration.hasTypeName() ? fromTypeName(_variableDeclaration.typeName()) : m_typeSystem.freshTypeVariable(); | ||||||
|  | 	m_env.assignType(m_typeSystem, &_variableDeclaration, type); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(Assignment const& _assignment) | ||||||
|  | { | ||||||
|  | 	(void)_assignment; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TypeInference::visit(Identifier const& _identifier) | ||||||
|  | { | ||||||
|  | 	(void)_identifier; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <libsolidity/ast/ASTVisitor.h> | #include <libsolidity/ast/ASTVisitor.h> | ||||||
|  | #include <libsolidity/ast/experimental/TypeSystem.h> | ||||||
| 
 | 
 | ||||||
| #include <liblangutil/ErrorReporter.h> | #include <liblangutil/ErrorReporter.h> | ||||||
| 
 | 
 | ||||||
| @ -26,84 +27,32 @@ | |||||||
| namespace solidity::frontend::experimental | namespace solidity::frontend::experimental | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| class GlobalTypeContext; |  | ||||||
| 
 |  | ||||||
| struct SumType; |  | ||||||
| struct TupleType; |  | ||||||
| struct FunctionType; |  | ||||||
| struct WordType; |  | ||||||
| struct UserDefinedType; |  | ||||||
| struct TypeVariable; |  | ||||||
| struct FreeType; |  | ||||||
| 
 |  | ||||||
| using Type = std::variant<SumType, TupleType, FunctionType, WordType, UserDefinedType, TypeVariable, FreeType>; |  | ||||||
| 
 |  | ||||||
| struct SumType |  | ||||||
| { |  | ||||||
| 	std::vector<Type const*> alternatives; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct TupleType |  | ||||||
| { |  | ||||||
| 	std::vector<Type const*> components; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct FunctionType |  | ||||||
| { |  | ||||||
| 	Type const* codomain = nullptr; |  | ||||||
| 	Type const* domain = nullptr; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct WordType |  | ||||||
| { |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct UserDefinedType |  | ||||||
| { |  | ||||||
| 	Declaration const* declaration = nullptr; |  | ||||||
| 	std::vector<Type const*> arguments; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct TypeVariable |  | ||||||
| { |  | ||||||
| 	uint64_t index = 0; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct FreeType |  | ||||||
| { |  | ||||||
| 	uint64_t index = 0; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| Type unify(Type _a, Type _b) |  | ||||||
| { |  | ||||||
| 	 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class TypeEnvironment |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	TypeEnvironment() {} |  | ||||||
| 	void assignType(Declaration const* _declaration, Type _typeAssignment) |  | ||||||
| 	{ |  | ||||||
| 		m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment))); |  | ||||||
| 	} |  | ||||||
| private: |  | ||||||
| 	uint64_t m_numTypeVariables = 0; |  | ||||||
| 	std::map<Declaration const*, Type> m_types; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TypeInference: public ASTConstVisitor | class TypeInference: public ASTConstVisitor | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} | 	TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} | ||||||
|  | 
 | ||||||
|  | 	bool analyze(SourceUnit const& _sourceUnit); | ||||||
| private: | private: | ||||||
| 	bool visit(Block const&) override { return true; } | 	bool visit(Block const&) override { return true; } | ||||||
| 	bool visit(VariableDeclarationStatement const&) override { return true; } | 	bool visit(VariableDeclarationStatement const&) override { return true; } | ||||||
| 	bool visit(VariableDeclaration const& _variableDeclaration) override; | 	bool visit(VariableDeclaration const& _variableDeclaration) override; | ||||||
| 
 | 
 | ||||||
|  | 	bool visit(FunctionDefinition const& _functionDefinition) override; | ||||||
|  | 	bool visit(ParameterList const& _parameterList) override; | ||||||
|  | 	bool visit(SourceUnit const&) override { return true; } | ||||||
|  | 	bool visit(ContractDefinition const&) override { return true; } | ||||||
|  | 	bool visit(InlineAssembly const& _inlineAssembly) override; | ||||||
|  | 	bool visit(PragmaDirective const&) override { return false; } | ||||||
|  | 
 | ||||||
|  | 	bool visit(ExpressionStatement const&) override { return true; } | ||||||
|  | 	bool visit(Assignment const&) override; | ||||||
|  | 	bool visit(Identifier const&) override; | ||||||
|  | 
 | ||||||
| 	bool visitNode(ASTNode const& _node) override; | 	bool visitNode(ASTNode const& _node) override; | ||||||
| 
 | 
 | ||||||
| private: | 	Type fromTypeName(TypeName const& _typeName); | ||||||
|  | 	TypeSystem m_typeSystem; | ||||||
| 	langutil::ErrorReporter& m_errorReporter; | 	langutil::ErrorReporter& m_errorReporter; | ||||||
| 	TypeEnvironment m_env; | 	TypeEnvironment m_env; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1077,13 +1077,15 @@ public: | |||||||
| 		m_overrides(std::move(_overrides)), | 		m_overrides(std::move(_overrides)), | ||||||
| 		m_location(_referenceLocation) | 		m_location(_referenceLocation) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(m_typeName, ""); | 		// TODO: consider still asserting unless we are in experimental solidity.
 | ||||||
|  | 		// solAssert(m_typeName, "");
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	void accept(ASTVisitor& _visitor) override; | 	void accept(ASTVisitor& _visitor) override; | ||||||
| 	void accept(ASTConstVisitor& _visitor) const override; | 	void accept(ASTConstVisitor& _visitor) const override; | ||||||
| 
 | 
 | ||||||
|  | 	bool hasTypeName() const { return m_typeName != nullptr; } | ||||||
| 	TypeName const& typeName() const { return *m_typeName; } | 	TypeName const& typeName() const { return *m_typeName; } | ||||||
| 	ASTPointer<Expression> const& value() const { return m_value; } | 	ASTPointer<Expression> const& value() const { return m_value; } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										64
									
								
								libsolidity/ast/experimental/TypeSystem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								libsolidity/ast/experimental/TypeSystem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | /*
 | ||||||
|  | 	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
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/ast/experimental/TypeSystem.h> | ||||||
|  | #include <liblangutil/Exceptions.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace solidity::frontend; | ||||||
|  | using namespace solidity::frontend::experimental; | ||||||
|  | 
 | ||||||
|  | void TypeEnvironment::assignType(TypeSystem& _typeSystem, 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); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | Type TypeEnvironment::lookup(TypeSystem& _typeSystem, 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 result; | ||||||
|  | } | ||||||
|  | Type TypeEnvironment::freshFreeType() | ||||||
|  | { | ||||||
|  | 	return FreeType{m_numFreeTypes++}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void TypeEnvironment::unify(TypeSystem& _context, Type _a, Type _b) | ||||||
|  | { | ||||||
|  | 	_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))); | ||||||
|  | } | ||||||
							
								
								
									
										167
									
								
								libsolidity/ast/experimental/TypeSystem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								libsolidity/ast/experimental/TypeSystem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | |||||||
|  | /*
 | ||||||
|  | 	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 <liblangutil/Exceptions.h> | ||||||
|  | 
 | ||||||
|  | #include <fmt/format.h> | ||||||
|  | 
 | ||||||
|  | #include <optional> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | namespace solidity::frontend | ||||||
|  | { | ||||||
|  | class Declaration; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace solidity::frontend::experimental | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class TypeSystem; | ||||||
|  | 
 | ||||||
|  | struct SumType; | ||||||
|  | struct TupleType; | ||||||
|  | struct FunctionType; | ||||||
|  | struct WordType; | ||||||
|  | struct UserDefinedType; | ||||||
|  | struct TypeVariable; | ||||||
|  | struct FreeType; | ||||||
|  | 
 | ||||||
|  | using Type = std::variant<SumType, TupleType, FunctionType, WordType, UserDefinedType, TypeVariable, FreeType>; | ||||||
|  | 
 | ||||||
|  | struct SumType | ||||||
|  | { | ||||||
|  | 	std::vector<Type const*> alternatives; | ||||||
|  | 	std::string toString() const | ||||||
|  | 	{ | ||||||
|  | 		return "sum"; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct TupleType | ||||||
|  | { | ||||||
|  | 	std::vector<Type const*> components; | ||||||
|  | 	std::string toString() const | ||||||
|  | 	{ | ||||||
|  | 		return "tuple"; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct FunctionType | ||||||
|  | { | ||||||
|  | 	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: | ||||||
|  | 	uint64_t index() const { return m_index; } | ||||||
|  | 	friend class TypeSystem; | ||||||
|  | 	uint64_t m_index = 0; | ||||||
|  | 	TypeVariable(uint64_t _index): m_index(_index) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct FreeType | ||||||
|  | { | ||||||
|  | 	uint64_t index = 0; | ||||||
|  | 	std::string toString() const | ||||||
|  | 	{ | ||||||
|  | 		return fmt::format("free<{}>", 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); | ||||||
|  | private: | ||||||
|  | 	uint64_t m_numFreeTypes = 0; | ||||||
|  | 	std::map<Declaration const*, Type> m_types; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TypeSystem | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	TypeSystem() {} | ||||||
|  | 	TypeSystem(TypeSystem const&) = delete; | ||||||
|  | 	TypeSystem const& operator=(TypeSystem const&) = delete; | ||||||
|  | 	Type freshTypeVariable() | ||||||
|  | 	{ | ||||||
|  | 		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; | ||||||
|  | 	} | ||||||
|  | private: | ||||||
|  | 	std::vector<std::optional<Type>> m_typeVariables; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -18,6 +18,8 @@ | |||||||
| 
 | 
 | ||||||
| #include <libsolidity/codegen/experimental/IRGenerator.h> | #include <libsolidity/codegen/experimental/IRGenerator.h> | ||||||
| 
 | 
 | ||||||
|  | #include <libsolidity/codegen/experimental/IRGeneratorForStatements.h> | ||||||
|  | 
 | ||||||
| #include <libsolidity/codegen/ir/Common.h> | #include <libsolidity/codegen/ir/Common.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/YulStack.h> | #include <libyul/YulStack.h> | ||||||
| @ -87,74 +89,9 @@ string IRGenerator::generate(FunctionDefinition const& _function) const | |||||||
| 	code << "function " << IRNames::function(_function) << "() {\n"; | 	code << "function " << IRNames::function(_function) << "() {\n"; | ||||||
| 	for (auto _statement: _function.body().statements()) | 	for (auto _statement: _function.body().statements()) | ||||||
| 	{ | 	{ | ||||||
| 		if (auto assembly = dynamic_cast<InlineAssembly const*>(_statement.get())) | 		IRGeneratorForStatements statementGenerator{m_analysis}; | ||||||
| 			code << generate(*assembly) << "\n"; | 		code << statementGenerator.generate(*_statement); | ||||||
| 		else |  | ||||||
| 			solUnimplemented("Unsupported statement type."); |  | ||||||
| 	} | 	} | ||||||
| 	code << "}\n"; | 	code << "}\n"; | ||||||
| 	return code.str(); | 	return code.str(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| namespace { |  | ||||||
| 
 |  | ||||||
| struct CopyTranslate: public yul::ASTCopier |  | ||||||
| { |  | ||||||
| 	CopyTranslate( |  | ||||||
| 		yul::Dialect const& _dialect, |  | ||||||
| 		map<yul::Identifier const*, void*> _references |  | ||||||
| 	): m_dialect(_dialect), m_references(std::move(_references)) {} |  | ||||||
| 
 |  | ||||||
| 	using ASTCopier::operator(); |  | ||||||
| 
 |  | ||||||
| 	yul::Expression operator()(yul::Identifier const& _identifier) override |  | ||||||
| 	{ |  | ||||||
| 		// The operator() function is only called in lvalue context. In rvalue context,
 |  | ||||||
| 		// only translate(yul::Identifier) is called.
 |  | ||||||
| 		if (m_references.count(&_identifier)) |  | ||||||
| 			return translateReference(_identifier); |  | ||||||
| 		else |  | ||||||
| 			return ASTCopier::operator()(_identifier); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	yul::YulString translateIdentifier(yul::YulString _name) override |  | ||||||
| 	{ |  | ||||||
| 		if (m_dialect.builtin(_name)) |  | ||||||
| 			return _name; |  | ||||||
| 		else |  | ||||||
| 			return yul::YulString{"usr$" + _name.str()}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	yul::Identifier translate(yul::Identifier const& _identifier) override |  | ||||||
| 	{ |  | ||||||
| 		if (!m_references.count(&_identifier)) |  | ||||||
| 			return ASTCopier::translate(_identifier); |  | ||||||
| 
 |  | ||||||
| 		yul::Expression translated = translateReference(_identifier); |  | ||||||
| 		solAssert(holds_alternative<yul::Identifier>(translated)); |  | ||||||
| 		return get<yul::Identifier>(std::move(translated)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 
 |  | ||||||
| 	/// Translates a reference to a local variable, potentially including
 |  | ||||||
| 	/// a suffix. Might return a literal, which causes this to be invalid in
 |  | ||||||
| 	/// lvalue-context.
 |  | ||||||
| 	yul::Expression translateReference(yul::Identifier const&) |  | ||||||
| 	{ |  | ||||||
| 		solUnimplemented("External references in inline assembly not implemented."); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	yul::Dialect const& m_dialect; |  | ||||||
| 	map<yul::Identifier const*, void*> m_references; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| string IRGenerator::generate(InlineAssembly const& _assembly) const |  | ||||||
| { |  | ||||||
| 	CopyTranslate bodyCopier{_assembly.dialect(), {}}; |  | ||||||
| 	yul::Statement modified = bodyCopier(_assembly.operations()); |  | ||||||
| 	solAssert(holds_alternative<yul::Block>(modified)); |  | ||||||
| 	return yul::AsmPrinter()(std::get<yul::Block>(modified)); |  | ||||||
| } |  | ||||||
| @ -35,6 +35,7 @@ namespace solidity::frontend::experimental | |||||||
| { | { | ||||||
| 
 | 
 | ||||||
| class SourceUnit; | class SourceUnit; | ||||||
|  | class Analysis; | ||||||
| 
 | 
 | ||||||
| class IRGenerator | class IRGenerator | ||||||
| { | { | ||||||
| @ -45,12 +46,14 @@ public: | |||||||
| 		RevertStrings /*_revertStrings*/, | 		RevertStrings /*_revertStrings*/, | ||||||
| 		std::map<std::string, unsigned> /*_sourceIndices*/, | 		std::map<std::string, unsigned> /*_sourceIndices*/, | ||||||
| 		langutil::DebugInfoSelection const& _debugInfoSelection, | 		langutil::DebugInfoSelection const& _debugInfoSelection, | ||||||
| 		langutil::CharStreamProvider const* _soliditySourceProvider | 		langutil::CharStreamProvider const* _soliditySourceProvider, | ||||||
|  | 		Analysis const& _analysis | ||||||
| 	): | 	): | ||||||
| 		m_evmVersion(_evmVersion), | 		m_evmVersion(_evmVersion), | ||||||
| 		m_eofVersion(_eofVersion), | 		m_eofVersion(_eofVersion), | ||||||
| 		m_debugInfoSelection(_debugInfoSelection), | 		m_debugInfoSelection(_debugInfoSelection), | ||||||
| 		m_soliditySourceProvider(_soliditySourceProvider) | 		m_soliditySourceProvider(_soliditySourceProvider), | ||||||
|  | 		m_analysis(_analysis) | ||||||
| 	{} | 	{} | ||||||
| 
 | 
 | ||||||
| 	std::string run( | 	std::string run( | ||||||
| @ -61,13 +64,13 @@ public: | |||||||
| 
 | 
 | ||||||
| 	std::string generate(ContractDefinition const& _contract) const; | 	std::string generate(ContractDefinition const& _contract) const; | ||||||
| 	std::string generate(FunctionDefinition const& _function) const; | 	std::string generate(FunctionDefinition const& _function) const; | ||||||
| 	std::string generate(InlineAssembly const& _assembly) const; |  | ||||||
| private: | private: | ||||||
| 	langutil::EVMVersion const m_evmVersion; | 	langutil::EVMVersion const m_evmVersion; | ||||||
| 	std::optional<uint8_t> const m_eofVersion; | 	std::optional<uint8_t> const m_eofVersion; | ||||||
| 	OptimiserSettings const m_optimiserSettings; | 	OptimiserSettings const m_optimiserSettings; | ||||||
| 	langutil::DebugInfoSelection m_debugInfoSelection = {}; | 	langutil::DebugInfoSelection m_debugInfoSelection = {}; | ||||||
| 	langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; | 	langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; | ||||||
|  | 	Analysis const& m_analysis; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,9 +18,108 @@ | |||||||
| 
 | 
 | ||||||
| #include <libsolidity/codegen/experimental/IRGeneratorForStatements.h> | #include <libsolidity/codegen/experimental/IRGeneratorForStatements.h> | ||||||
| 
 | 
 | ||||||
|  | #include <libyul/YulStack.h> | ||||||
|  | #include <libyul/AsmPrinter.h> | ||||||
|  | #include <libyul/AST.h> | ||||||
|  | #include <libyul/optimiser/ASTCopier.h> | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/codegen/ir/Common.h> | ||||||
|  | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| using namespace solidity; | using namespace solidity; | ||||||
| using namespace solidity::util; | using namespace solidity::util; | ||||||
| using namespace solidity::frontend; | using namespace solidity::frontend; | ||||||
| using namespace solidity::frontend::experimental; | using namespace solidity::frontend::experimental; | ||||||
| using namespace std::string_literals; | using namespace std::string_literals; | ||||||
|  | 
 | ||||||
|  | std::string IRGeneratorForStatements::generate(ASTNode const& _node) | ||||||
|  | { | ||||||
|  | 	_node.accept(*this); | ||||||
|  | 	return m_code.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | struct CopyTranslate: public yul::ASTCopier | ||||||
|  | { | ||||||
|  | 	CopyTranslate( | ||||||
|  | 		yul::Dialect const& _dialect, | ||||||
|  | 		map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> _references | ||||||
|  | 		): m_dialect(_dialect), m_references(std::move(_references)) {} | ||||||
|  | 
 | ||||||
|  | 	using ASTCopier::operator(); | ||||||
|  | 
 | ||||||
|  | 	yul::Expression operator()(yul::Identifier const& _identifier) override | ||||||
|  | 	{ | ||||||
|  | 		// The operator() function is only called in lvalue context. In rvalue context,
 | ||||||
|  | 		// only translate(yul::Identifier) is called.
 | ||||||
|  | 		if (m_references.count(&_identifier)) | ||||||
|  | 			return translateReference(_identifier); | ||||||
|  | 		else | ||||||
|  | 			return ASTCopier::operator()(_identifier); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	yul::YulString translateIdentifier(yul::YulString _name) override | ||||||
|  | 	{ | ||||||
|  | 		if (m_dialect.builtin(_name)) | ||||||
|  | 			return _name; | ||||||
|  | 		else | ||||||
|  | 			return yul::YulString{"usr$" + _name.str()}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	yul::Identifier translate(yul::Identifier const& _identifier) override | ||||||
|  | 	{ | ||||||
|  | 		if (!m_references.count(&_identifier)) | ||||||
|  | 			return ASTCopier::translate(_identifier); | ||||||
|  | 
 | ||||||
|  | 		yul::Expression translated = translateReference(_identifier); | ||||||
|  | 		solAssert(holds_alternative<yul::Identifier>(translated)); | ||||||
|  | 		return get<yul::Identifier>(std::move(translated)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 
 | ||||||
|  | 	/// Translates a reference to a local variable, potentially including
 | ||||||
|  | 	/// a suffix. Might return a literal, which causes this to be invalid in
 | ||||||
|  | 	/// lvalue-context.
 | ||||||
|  | 	yul::Expression translateReference(yul::Identifier const& _identifier) | ||||||
|  | 	{ | ||||||
|  | 		auto const& reference = m_references.at(&_identifier); | ||||||
|  | 		auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration); | ||||||
|  | 		solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration."); | ||||||
|  | 		// TODO: validate that variable is known and has word type.
 | ||||||
|  | 		string value = IRNames::localVariable(*varDecl); | ||||||
|  | 		return yul::Identifier{_identifier.debugData, yul::YulString{value}}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	yul::Dialect const& m_dialect; | ||||||
|  | 	map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> m_references; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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)); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variableDeclarationStatement) | ||||||
|  | { | ||||||
|  | 	solAssert(_variableDeclarationStatement.declarations().size() == 1, "multi variable declarations not supported"); | ||||||
|  | 	solAssert(!_variableDeclarationStatement.initialValue(), "initial values not yet supported"); | ||||||
|  | 	VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get(); | ||||||
|  | 	solAssert(variableDeclaration); | ||||||
|  | 	// TODO: check the type of the variable; register local variable; initialize
 | ||||||
|  | 	m_code << "let " << IRNames::localVariable(*variableDeclaration) << "\n"; | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IRGeneratorForStatements::visitNode(ASTNode const&) | ||||||
|  | { | ||||||
|  | 	solAssert(false, "Unsupported AST node during statement code generation."); | ||||||
|  | } | ||||||
| @ -21,18 +21,25 @@ | |||||||
| #include <libsolidity/ast/ASTVisitor.h> | #include <libsolidity/ast/ASTVisitor.h> | ||||||
| 
 | 
 | ||||||
| #include <functional> | #include <functional> | ||||||
|  | #include <sstream> | ||||||
| 
 | 
 | ||||||
| namespace solidity::frontend::experimental | namespace solidity::frontend::experimental | ||||||
| { | { | ||||||
|  | class Analysis; | ||||||
| 
 | 
 | ||||||
| class IRGeneratorForStatements: public ASTConstVisitor | class IRGeneratorForStatements: public ASTConstVisitor | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	IRGeneratorForStatements() {} | 	IRGeneratorForStatements(Analysis const& _analysis): m_analysis(_analysis) {} | ||||||
| 
 | 
 | ||||||
|  | 	std::string generate(ASTNode const& _node); | ||||||
| private: | private: | ||||||
|  | 	bool visit(InlineAssembly const& _inlineAssembly) override; | ||||||
|  | 	bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; | ||||||
| 	/// Default visit will reject all AST nodes that are not explicitly supported.
 | 	/// Default visit will reject all AST nodes that are not explicitly supported.
 | ||||||
| 	bool visitNode(ASTNode const& _node) override; | 	bool visitNode(ASTNode const& _node) override; | ||||||
|  | 	Analysis const& m_analysis; | ||||||
|  | 	std::stringstream m_code; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -670,9 +670,11 @@ bool CompilerStack::analyzeExperimental() | |||||||
| 	bool noErrors = true; | 	bool noErrors = true; | ||||||
| 	solAssert(m_maxAstId && *m_maxAstId >= 0); | 	solAssert(m_maxAstId && *m_maxAstId >= 0); | ||||||
| 	m_experimentalAnalysis = make_unique<experimental::Analysis>(m_errorReporter, static_cast<uint64_t>(*m_maxAstId)); | 	m_experimentalAnalysis = make_unique<experimental::Analysis>(m_errorReporter, static_cast<uint64_t>(*m_maxAstId)); | ||||||
|  | 	vector<shared_ptr<SourceUnit const>> sourceAsts; | ||||||
| 	for (Source const* source: m_sourceOrder) | 	for (Source const* source: m_sourceOrder) | ||||||
| 		if (source->ast) | 		if (source->ast) | ||||||
| 			if (!m_experimentalAnalysis->check(*source->ast)) | 			sourceAsts.emplace_back(source->ast); | ||||||
|  | 	if (!m_experimentalAnalysis->check(sourceAsts)) | ||||||
| 		noErrors = false; | 		noErrors = false; | ||||||
| 	return noErrors; | 	return noErrors; | ||||||
| } | } | ||||||
| @ -1519,7 +1521,8 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) | |||||||
| 			m_revertStrings, | 			m_revertStrings, | ||||||
| 			sourceIndices(), | 			sourceIndices(), | ||||||
| 			m_debugInfoSelection, | 			m_debugInfoSelection, | ||||||
| 			this | 			this, | ||||||
|  | 			*m_experimentalAnalysis | ||||||
| 		); | 		); | ||||||
| 		compiledContract.yulIR = generator.run( | 		compiledContract.yulIR = generator.run( | ||||||
| 			_contract, | 			_contract, | ||||||
|  | |||||||
| @ -610,7 +610,9 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari | |||||||
| 		else | 		else | ||||||
| 			break; | 			break; | ||||||
| 	} | 	} | ||||||
| 	if (m_scanner->currentToken() == Token::Returns) | 	if ( | ||||||
|  | 		m_scanner->currentToken() == (m_experimentalSolidityEnabledInCurrentSourceUnit ? Token::RightArrow : Token::Returns) | ||||||
|  | 	) | ||||||
| 	{ | 	{ | ||||||
| 		bool const permitEmptyParameterList = false; | 		bool const permitEmptyParameterList = false; | ||||||
| 		advance(); | 		advance(); | ||||||
| @ -1269,15 +1271,21 @@ ASTPointer<ParameterList> Parser::parseParameterList( | |||||||
| 	VarDeclParserOptions options(_options); | 	VarDeclParserOptions options(_options); | ||||||
| 	options.allowEmptyName = true; | 	options.allowEmptyName = true; | ||||||
| 	expectToken(Token::LParen); | 	expectToken(Token::LParen); | ||||||
|  | 	auto parseSingleVariableDeclaration = [&]() { | ||||||
|  | 		if (m_experimentalSolidityEnabledInCurrentSourceUnit) | ||||||
|  | 			return parsePostfixVariableDeclaration(); | ||||||
|  | 		else | ||||||
|  | 			return parseVariableDeclaration(options); | ||||||
|  | 	}; | ||||||
| 	if (!_allowEmpty || m_scanner->currentToken() != Token::RParen) | 	if (!_allowEmpty || m_scanner->currentToken() != Token::RParen) | ||||||
| 	{ | 	{ | ||||||
| 		parameters.push_back(parseVariableDeclaration(options)); | 		parameters.push_back(parseSingleVariableDeclaration()); | ||||||
| 		while (m_scanner->currentToken() != Token::RParen) | 		while (m_scanner->currentToken() != Token::RParen) | ||||||
| 		{ | 		{ | ||||||
| 			if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) | 			if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) | ||||||
| 				fatalParserError(7591_error, "Unexpected trailing comma in parameter list."); | 				fatalParserError(7591_error, "Unexpected trailing comma in parameter list."); | ||||||
| 			expectToken(Token::Comma); | 			expectToken(Token::Comma); | ||||||
| 			parameters.push_back(parseVariableDeclaration(options)); | 			parameters.push_back(parseSingleVariableDeclaration()); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	nodeFactory.markEndPosition(); | 	nodeFactory.markEndPosition(); | ||||||
| @ -1658,12 +1666,66 @@ ASTPointer<RevertStatement> Parser::parseRevertStatement(ASTPointer<ASTString> c | |||||||
| 	return nodeFactory.createNode<RevertStatement>(_docString, errorCall); | 	return nodeFactory.createNode<RevertStatement>(_docString, errorCall); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ASTPointer<VariableDeclarationStatement> Parser::parsePostfixVariableDeclarationStatement( | ||||||
|  | 	ASTPointer<ASTString> const& _docString | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	RecursionGuard recursionGuard(*this); | ||||||
|  | 	ASTNodeFactory nodeFactory(*this); | ||||||
|  | 
 | ||||||
|  | 	expectToken(Token::Let); | ||||||
|  | 
 | ||||||
|  | 	vector<ASTPointer<VariableDeclaration>> variables; | ||||||
|  | 	variables.emplace_back(parsePostfixVariableDeclaration()); | ||||||
|  | 	nodeFactory.setEndPositionFromNode(variables.back()); | ||||||
|  | 
 | ||||||
|  | 	ASTPointer<Expression> value; | ||||||
|  | 	if (m_scanner->currentToken() == Token::Assign) | ||||||
|  | 	{ | ||||||
|  | 		advance(); | ||||||
|  | 		value = parseExpression(); | ||||||
|  | 		nodeFactory.setEndPositionFromNode(value); | ||||||
|  | 	} | ||||||
|  | 	return nodeFactory.createNode<VariableDeclarationStatement>(_docString, variables, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration() | ||||||
|  | { | ||||||
|  | 	RecursionGuard recursionGuard(*this); | ||||||
|  | 	ASTNodeFactory nodeFactory(*this); | ||||||
|  | 
 | ||||||
|  | 	ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation(); | ||||||
|  | 
 | ||||||
|  | 	nodeFactory.markEndPosition(); | ||||||
|  | 	auto [identifier, nameLocation] = expectIdentifierWithLocation(); | ||||||
|  | 
 | ||||||
|  | 	ASTPointer<TypeName> type; | ||||||
|  | 	if (m_scanner->currentToken() == Token::Colon) | ||||||
|  | 	{ | ||||||
|  | 		advance(); | ||||||
|  | 		type = parseTypeName(); | ||||||
|  | 		nodeFactory.setEndPositionFromNode(type); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nodeFactory.createNode<VariableDeclaration>( | ||||||
|  | 		type, | ||||||
|  | 		identifier, | ||||||
|  | 		nameLocation, | ||||||
|  | 		nullptr, | ||||||
|  | 		Visibility::Default, | ||||||
|  | 		documentation | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString) | ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString) | ||||||
| { | { | ||||||
| 	RecursionGuard recursionGuard(*this); | 	RecursionGuard recursionGuard(*this); | ||||||
| 	LookAheadInfo statementType; | 	LookAheadInfo statementType; | ||||||
| 	IndexAccessedPath iap; | 	IndexAccessedPath iap; | ||||||
| 
 | 
 | ||||||
|  | 	if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Let) | ||||||
|  | 		return parsePostfixVariableDeclarationStatement(_docString); | ||||||
|  | 
 | ||||||
| 	if (m_scanner->currentToken() == Token::LParen) | 	if (m_scanner->currentToken() == Token::LParen) | ||||||
| 	{ | 	{ | ||||||
| 		ASTNodeFactory nodeFactory(*this); | 		ASTNodeFactory nodeFactory(*this); | ||||||
| @ -1766,7 +1828,10 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce | |||||||
| 	{ | 	{ | ||||||
| 	case LookAheadInfo::VariableDeclaration: | 	case LookAheadInfo::VariableDeclaration: | ||||||
| 	case LookAheadInfo::Expression: | 	case LookAheadInfo::Expression: | ||||||
| 		return make_pair(statementType, IndexAccessedPath()); | 		return make_pair( | ||||||
|  | 			m_experimentalSolidityEnabledInCurrentSourceUnit ? LookAheadInfo::Expression : statementType, | ||||||
|  | 			IndexAccessedPath() | ||||||
|  | 		); | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| @ -1777,6 +1842,9 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce | |||||||
| 	// VariableDeclarationStatement out of it.
 | 	// VariableDeclarationStatement out of it.
 | ||||||
| 	IndexAccessedPath iap = parseIndexAccessedPath(); | 	IndexAccessedPath iap = parseIndexAccessedPath(); | ||||||
| 
 | 
 | ||||||
|  | 	if (m_experimentalSolidityEnabledInCurrentSourceUnit) | ||||||
|  | 		return make_pair(LookAheadInfo::Expression, std::move(iap)); | ||||||
|  | 
 | ||||||
| 	if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) | 	if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) | ||||||
| 		return make_pair(LookAheadInfo::VariableDeclaration, std::move(iap)); | 		return make_pair(LookAheadInfo::VariableDeclaration, std::move(iap)); | ||||||
| 	else | 	else | ||||||
|  | |||||||
| @ -170,6 +170,14 @@ private: | |||||||
| 	std::pair<ASTPointer<ASTString>, langutil::SourceLocation> expectIdentifierWithLocation(); | 	std::pair<ASTPointer<ASTString>, langutil::SourceLocation> expectIdentifierWithLocation(); | ||||||
| 	///@}
 | 	///@}
 | ||||||
| 
 | 
 | ||||||
|  | 	///@{
 | ||||||
|  | 	///@name Specialized parsing functions for the AST nodes of experimental solidity.
 | ||||||
|  | 	ASTPointer<VariableDeclarationStatement> parsePostfixVariableDeclarationStatement( | ||||||
|  | 		ASTPointer<ASTString> const& _docString | ||||||
|  | 	); | ||||||
|  | 	ASTPointer<VariableDeclaration> parsePostfixVariableDeclaration(); | ||||||
|  | 	///@}
 | ||||||
|  | 
 | ||||||
| 	///@{
 | 	///@{
 | ||||||
| 	///@name Helper functions
 | 	///@name Helper functions
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -116,8 +116,9 @@ unique_ptr<Block> Parser::parseInline(std::shared_ptr<Scanner> const& _scanner) | |||||||
| { | { | ||||||
| 	m_recursionDepth = 0; | 	m_recursionDepth = 0; | ||||||
| 
 | 
 | ||||||
|  | 	auto previousScannerKind = _scanner->scannerKind(); | ||||||
| 	_scanner->setScannerMode(ScannerKind::Yul); | 	_scanner->setScannerMode(ScannerKind::Yul); | ||||||
| 	ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); }); | 	ScopeGuard resetScanner([&]{ _scanner->setScannerMode(previousScannerKind); }); | ||||||
| 
 | 
 | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
| @ -1,10 +1,21 @@ | |||||||
| pragma experimental solidity; | pragma experimental solidity; | ||||||
| 
 | 
 | ||||||
|  | function f(a:word) -> (b:word) { | ||||||
|  |     assembly { | ||||||
|  |         b := a | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| contract C { | contract C { | ||||||
| 	fallback() external { | 	fallback() external { | ||||||
| 	    word x; | 		let x : word; | ||||||
|  | 		let y : word; | ||||||
| 		assembly { | 		assembly { | ||||||
| 			mstore(0, 42) | 			x := 0x42 | ||||||
|  | 		} | ||||||
|  | 		y = x; | ||||||
|  | 		assembly { | ||||||
|  | 			mstore(0, y) | ||||||
| 			return(0, 32) | 			return(0, 32) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -12,4 +23,4 @@ contract C { | |||||||
| // ==== | // ==== | ||||||
| // compileViaYul: true | // compileViaYul: true | ||||||
| // ---- | // ---- | ||||||
| // () -> 42 | // () -> 0x42 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user