diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 22e3c720f..2bdadef83 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -48,6 +48,8 @@ set(sources analysis/ViewPureChecker.h analysis/experimental/Analysis.cpp analysis/experimental/Analysis.h + analysis/experimental/DebugWarner.cpp + analysis/experimental/DebugWarner.h analysis/experimental/TypeInference.cpp analysis/experimental/TypeInference.h analysis/experimental/TypeRegistration.cpp @@ -100,6 +102,8 @@ set(sources codegen/ReturnInfo.cpp codegen/YulUtilFunctions.h codegen/YulUtilFunctions.cpp + codegen/experimental/Common.h + codegen/experimental/Common.cpp codegen/experimental/IRGenerationContext.h codegen/experimental/IRGenerator.cpp codegen/experimental/IRGenerator.h diff --git a/libsolidity/analysis/experimental/Analysis.cpp b/libsolidity/analysis/experimental/Analysis.cpp index 7e255a28c..7f83ac287 100644 --- a/libsolidity/analysis/experimental/Analysis.cpp +++ b/libsolidity/analysis/experimental/Analysis.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include #include #include #include @@ -38,24 +39,44 @@ TypeRegistration::Annotation& solidity::frontend::experimental::detail::Annotati return analysis.annotationContainer(_node).typeRegistrationAnnotation; } +template<> +TypeRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeRegistrationAnnotation; +} + template<> TypeInference::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) { return analysis.annotationContainer(_node).typeInferenceAnnotation; } +template<> +TypeInference::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeInferenceAnnotation; +} + Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node) { solAssert(_node.id() > 0); size_t id = static_cast(_node.id()); - solAssert(id < m_maxAstId); + solAssert(id <= m_maxAstId); + return m_annotations[id]; +} + +Analysis::AnnotationContainer const& Analysis::annotationContainer(ASTNode const& _node) const +{ + solAssert(_node.id() > 0); + size_t id = static_cast(_node.id()); + solAssert(id <= m_maxAstId); return m_annotations[id]; } Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): m_errorReporter(_errorReporter), m_maxAstId(_maxAstId), - m_annotations(std::make_unique(static_cast(_maxAstId))) + m_annotations(std::make_unique(static_cast(_maxAstId + 1))) { } @@ -69,7 +90,7 @@ std::tuple...> makeIndexTuple(std::index_sequ bool Analysis::check(vector> const& _sourceUnits) { - using AnalysisSteps = std::tuple; + using AnalysisSteps = std::tuple; return std::apply([&](auto... _indexTuple) { return ([&](auto&& _step) { diff --git a/libsolidity/analysis/experimental/Analysis.h b/libsolidity/analysis/experimental/Analysis.h index 4075095cc..57c4e579e 100644 --- a/libsolidity/analysis/experimental/Analysis.h +++ b/libsolidity/analysis/experimental/Analysis.h @@ -48,6 +48,12 @@ struct AnnotationFetcher Analysis& analysis; typename Step::Annotation& get(ASTNode const& _node); }; +template +struct ConstAnnotationFetcher +{ + Analysis const& analysis; + typename Step::Annotation const& get(ASTNode const& _node) const; +}; } class Analysis @@ -62,12 +68,19 @@ public: langutil::ErrorReporter& errorReporter() { return m_errorReporter; } uint64_t maxAstId() const { return m_maxAstId; } TypeSystem& typeSystem() { return m_typeSystem; } + TypeSystem const& typeSystem() const { return m_typeSystem; } template typename Step::Annotation& annotation(ASTNode const& _node) { return detail::AnnotationFetcher{*this}.get(_node); } + template + typename Step::Annotation const& annotation(ASTNode const& _node) const + { + return detail::ConstAnnotationFetcher{*this}.get(_node); + } AnnotationContainer& annotationContainer(ASTNode const& _node); + AnnotationContainer const& annotationContainer(ASTNode const& _node) const; private: langutil::ErrorReporter& m_errorReporter; TypeSystem m_typeSystem; diff --git a/libsolidity/analysis/experimental/DebugWarner.cpp b/libsolidity/analysis/experimental/DebugWarner.cpp new file mode 100644 index 000000000..f13a6b127 --- /dev/null +++ b/libsolidity/analysis/experimental/DebugWarner.cpp @@ -0,0 +1,47 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +#include + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +DebugWarner::DebugWarner(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter()) +{} + +bool DebugWarner::analyze(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return !Error::containsErrors(m_errorReporter.errors()); +} + +bool DebugWarner::visitNode(ASTNode const& _node) +{ + auto const& typeInferenceAnnotation = m_analysis.annotation(_node); + if (typeInferenceAnnotation.type) + { + m_errorReporter.info(0000_error, _node.location(), "Inferred type: " + m_analysis.typeSystem().env().typeToString(*typeInferenceAnnotation.type)); + } + return true; +} diff --git a/libsolidity/analysis/experimental/DebugWarner.h b/libsolidity/analysis/experimental/DebugWarner.h new file mode 100644 index 000000000..a7b5ac6d0 --- /dev/null +++ b/libsolidity/analysis/experimental/DebugWarner.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +#include + +namespace solidity::frontend::experimental +{ +class Analysis; + +class DebugWarner: public ASTConstVisitor +{ +public: + DebugWarner(Analysis& _analysis); + + bool analyze(ASTNode const& _astRoot); + +private: + bool visitNode(ASTNode const& _node) override; + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/analysis/experimental/SyntaxRestrictor.h b/libsolidity/analysis/experimental/SyntaxRestrictor.h index df12a4c72..fd5c75f58 100644 --- a/libsolidity/analysis/experimental/SyntaxRestrictor.h +++ b/libsolidity/analysis/experimental/SyntaxRestrictor.h @@ -48,11 +48,13 @@ private: bool visit(Block const&) override { return true; } bool visit(InlineAssembly const&) override { return true; } bool visit(Identifier const&) override { return true; } + bool visit(IdentifierPath const&) override { return true; } bool visit(VariableDeclarationStatement const&) override; bool visit(VariableDeclaration const&) override; bool visit(ElementaryTypeName const&) override { return true; } bool visit(ParameterList const&) override { return true; } bool visit(Return const&) override { return true; } + bool visit(MemberAccess const&) override { return true; } langutil::ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/experimental/TypeInference.cpp b/libsolidity/analysis/experimental/TypeInference.cpp index c567cf501..48d5e2b31 100644 --- a/libsolidity/analysis/experimental/TypeInference.cpp +++ b/libsolidity/analysis/experimental/TypeInference.cpp @@ -40,6 +40,7 @@ m_typeSystem(_analysis.typeSystem()) m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {}); m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {}); m_integerType = m_typeSystem.builtinType(BuiltinType::Integer, {}); + m_env = &m_typeSystem.env(); } bool TypeInference::analyze(SourceUnit const& _sourceUnit) @@ -79,12 +80,9 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) if (_functionDefinition.isImplemented()) _functionDefinition.body().accept(*this); - functionAnnotation.type = m_typeSystem.fresh( - functionType, - true - ); + functionAnnotation.type = functionType; - m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(*functionAnnotation.type)); + m_errorReporter.info(0000_error, _functionDefinition.location(), m_env->typeToString(*functionAnnotation.type)); return false; } @@ -122,7 +120,22 @@ void TypeInference::endVisit(ParameterList const& _parameterList) bool TypeInference::visitNode(ASTNode const& _node) { - m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference."); + m_errorReporter.fatalTypeError(0000_error, _node.location(), "Unsupported AST node during type inference."); + return false; +} + +bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) +{ + auto& typeVariableAnnotation = annotation(_typeClassDefinition.typeVariable()); + if (typeVariableAnnotation.type) + return false; + _typeClassDefinition.typeVariable().accept(*this); + + for (auto const& subNode: _typeClassDefinition.subNodes()) + subNode->accept(*this); + + solAssert(typeVariableAnnotation.type); + unify(*typeVariableAnnotation.type, m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{&_typeClassDefinition}}})); return false; } @@ -151,8 +164,10 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) { if (auto const* typeClass = dynamic_cast(variableDeclaration->scope())) { - (void)typeClass; - m_errorReporter.typeError(0000_error, _typeName.location(), "Using type class type variables not yet implemented."); + typeClass->accept(*this); + auto varType = annotation(typeClass->typeVariable()).type; + solAssert(varType); + return *varType; } else m_errorReporter.typeError(0000_error, _typeName.location(), "Type name referencing a variable declaration."); @@ -162,12 +177,12 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) } else m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name."); - return m_typeSystem.freshTypeVariable(false); + return m_typeSystem.freshTypeVariable(false, {}); } void TypeInference::unify(Type _a, Type _b) { - for (auto failure: m_typeSystem.unify(_a, _b)) - m_errorReporter.typeError(0000_error, {}, fmt::format("Cannot unify {} and {}.", m_typeSystem.typeToString(_a), m_typeSystem.typeToString(_b))); + for (auto failure: m_env->unify(_a, _b)) + m_errorReporter.typeError(0000_error, {}, fmt::format("Cannot unify {} and {}.", m_env->typeToString(_a), m_env->typeToString(_b))); } bool TypeInference::visit(InlineAssembly const& _inlineAssembly) @@ -218,12 +233,25 @@ bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) solAssert(!_variableDeclaration.value()); auto& variableAnnotation = annotation(_variableDeclaration); solAssert(!variableAnnotation.type); + + Sort sort; + for (auto const& typeClass: _variableDeclaration.sort()) + { + auto const* declaration = dynamic_cast(typeClass->annotation().referencedDeclaration); + if (!declaration) + m_errorReporter.typeError(0000_error, typeClass->location(), "Expected type class."); + sort.classes.emplace(TypeClass{declaration}); + } + variableAnnotation.type = [&] { if (_variableDeclaration.hasTypeName()) return fromTypeName(_variableDeclaration.typeName()); else - return m_typeSystem.freshTypeVariable(false); + return m_typeSystem.freshTypeVariable(false, sort); }(); + + // TODO: validate sort. + return false; } @@ -242,7 +270,7 @@ void TypeInference::endVisit(Assignment const& _assignment) auto& rhsAnnotation = annotation(_assignment.rightHandSide()); solAssert(rhsAnnotation.type); unify(*lhsAnnotation.type, *rhsAnnotation.type); - assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type); + assignmentAnnotation.type = m_env->resolve(*lhsAnnotation.type); } TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node) @@ -269,7 +297,13 @@ bool TypeInference::visit(Identifier const& _identifier) referencedDeclaration->accept(*this); solAssert(declarationAnnotation.type); - identifierAnnotation.type = declarationAnnotation.type; + + if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false); + else if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = declarationAnnotation.type; + else + solAssert(false); return true; } @@ -282,6 +316,7 @@ bool TypeInference::visit(IdentifierPath const& _identifier) auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration; solAssert(referencedDeclaration); + if ( !dynamic_cast(referencedDeclaration) && !dynamic_cast(referencedDeclaration) @@ -293,15 +328,97 @@ bool TypeInference::visit(IdentifierPath const& _identifier) referencedDeclaration->accept(*this); solAssert(declarationAnnotation.type); - identifierAnnotation.type = declarationAnnotation.type; + + if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false); + else if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = declarationAnnotation.type; + else + solAssert(false); return true; } bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) { + auto const* typeClass = dynamic_cast(_typeClassInstantiation.typeClass().annotation().referencedDeclaration); + if (!typeClass) + m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + typeClass->accept(*this); + + map functionTypes; + + for (auto subNode: typeClass->subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + auto functionType = annotation(*functionDefinition).type; + solAssert(functionType); + functionTypes[functionDefinition->name()] = *functionType; + } for (auto subNode: _typeClassInstantiation.subNodes()) - subNode->accept(*this); + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + Type* expectedFunctionType = util::valueOrNullptr(functionTypes, functionDefinition->name()); + if (!expectedFunctionType) + { + m_errorReporter.typeError(0000_error, functionDefinition->location(), "Function definition during instantiation that does not belong to the class."); + continue; + } + subNode->accept(*this); + } + for (auto subNode: _typeClassInstantiation.subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + if (Type* expectedFunctionType = util::valueOrNullptr(functionTypes, functionDefinition->name())) + { + auto functionType = annotation(*functionDefinition).type; + solAssert(functionType); + // TODO: require exact match? + unify(*functionType, m_env->fresh(*expectedFunctionType, true)); + } + } + + return false; +} + +bool TypeInference::visit(MemberAccess const& _memberAccess) +{ + if (auto const* identifier = dynamic_cast(&_memberAccess.expression())) + { + auto const* declaration = identifier->annotation().referencedDeclaration; + if (auto const* typeClass = dynamic_cast(declaration)) + { + for (auto subNode: typeClass->subNodes()) + { + if (auto const* functionDefinition = dynamic_cast(subNode.get())) + { + if (functionDefinition->name() == _memberAccess.memberName()) + { + auto& declarationAnnotation = annotation(*functionDefinition); + if (!declarationAnnotation.type) + functionDefinition->accept(*this); + solAssert(declarationAnnotation.type); + Type type = m_env->fresh(*declarationAnnotation.type, true); + annotation(_memberAccess).type = type; + auto typeVars = TypeSystemHelpers{m_typeSystem}.typeVars(type); + if (typeVars.size() != 1) + m_errorReporter.typeError(0000_error, _memberAccess.location(), "Type class reference does not uniquely depend on class type."); + annotation(_memberAccess.expression()).type = typeVars.front(); + m_errorReporter.info(0000_error, _memberAccess.location(), m_env->typeToString(*declarationAnnotation.type)); + return false; + } + } + } + m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Unknown member of type-class."); + } + else + m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Member access to non-type-class."); + } + else + m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Member access to non-identifier."); + return false; } @@ -314,7 +431,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) auto& expressionAnnotation = annotation(_functionCall.expression()); solAssert(expressionAnnotation.type); - Type functionType = m_typeSystem.fresh(*expressionAnnotation.type, false); + Type functionType = *expressionAnnotation.type; std::vector argTypes; for(auto arg: _functionCall.arguments()) @@ -324,8 +441,8 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) argTypes.emplace_back(*argAnnotation.type); } Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes); - Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false)); + Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {})); unify(genericFunctionType, functionType); - functionCallAnnotation.type = m_typeSystem.resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_typeSystem.resolve(genericFunctionType)))); + functionCallAnnotation.type = m_env->resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_env->resolve(genericFunctionType)))); } diff --git a/libsolidity/analysis/experimental/TypeInference.h b/libsolidity/analysis/experimental/TypeInference.h index d5b9cc270..ed1e69289 100644 --- a/libsolidity/analysis/experimental/TypeInference.h +++ b/libsolidity/analysis/experimental/TypeInference.h @@ -34,12 +34,11 @@ public: bool analyze(SourceUnit const& _sourceUnit); - struct Annotation { + /// Expressions, variable declarations, function declarations. std::optional type; }; -private: bool visit(Block const&) override { return true; } bool visit(VariableDeclarationStatement const&) override { return true; } bool visit(VariableDeclaration const& _variableDeclaration) override; @@ -62,16 +61,19 @@ private: bool visit(Return const&) override { return true; } void endVisit(Return const& _return) override; + bool visit(MemberAccess const& _memberAccess) override; + // TODO: properly account for it - bool visit(TypeClassDefinition const&) override { return true; } + bool visit(TypeClassDefinition const&) override; bool visit(TypeClassInstantiation const&) override; bool visitNode(ASTNode const& _node) override; - +private: Type fromTypeName(TypeName const& _typeName); Analysis& m_analysis; langutil::ErrorReporter& m_errorReporter; TypeSystem& m_typeSystem; + TypeEnvironment* m_env = nullptr; Type m_voidType; Type m_wordType; Type m_integerType; diff --git a/libsolidity/analysis/experimental/TypeRegistration.cpp b/libsolidity/analysis/experimental/TypeRegistration.cpp index 92eebb89e..166c29581 100644 --- a/libsolidity/analysis/experimental/TypeRegistration.cpp +++ b/libsolidity/analysis/experimental/TypeRegistration.cpp @@ -61,37 +61,81 @@ bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition) } bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation) { - auto const* classDefintion = dynamic_cast(_typeClassInstantiation.sort().annotation().referencedDeclaration); + auto const* classDefintion = dynamic_cast(_typeClassInstantiation.typeClass().annotation().referencedDeclaration); if (!classDefintion) - m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.sort().location(), "Expected a type class."); + m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); classDefintion->accept(*this); -// TypeClass typeClass{classDefintion}; + TypeClass typeClass{classDefintion}; - auto fromTypeName = [&](TypeName const& _typeName) -> Type { - if (auto const* elementaryTypeName = dynamic_cast(&_typeName)) + TypeName const& typeName = _typeClassInstantiation.typeConstructor(); + + TypeExpression::Constructor typeConstructor = [&]() -> TypeExpression::Constructor { + if (auto const* elementaryTypeName = dynamic_cast(&typeName)) { switch(elementaryTypeName->typeName().token()) { case Token::Word: - return m_typeSystem.builtinType(BuiltinType::Word, {}); + return BuiltinType::Word; case Token::Void: - return m_typeSystem.builtinType(BuiltinType::Void, {}); + return BuiltinType::Void; case Token::Integer: - return m_typeSystem.builtinType(BuiltinType::Integer, {}); + return BuiltinType::Integer; default: - m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); - break; + m_errorReporter.typeError(0000_error, typeName.location(), "Only elementary types are supported."); + return BuiltinType::Void; + } + } + else if (auto const* userDefinedType = dynamic_cast(&typeName)) + { + if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration) + return referencedDeclaration; + else + { + m_errorReporter.typeError(0000_error, userDefinedType->pathNode().location(), "No declaration found for user-defined type name."); + return BuiltinType::Void; } } else - m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name."); - return m_typeSystem.freshTypeVariable(false); - }; - auto type = fromTypeName(_typeClassInstantiation.typeConstructor()); - _typeClassInstantiation.argumentSorts(); + { + m_errorReporter.typeError(0000_error, typeName.location(), "Only elementary types are supported."); + return BuiltinType::Void; + } + }(); -// m_typeSystem.instantiateClass(); + Arity arity{ + {}, + typeClass + }; + if (_typeClassInstantiation.argumentSorts().size() != m_typeSystem.constructorArguments(typeConstructor)) + m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.location(), "Invalid number of arguments."); + + for (auto argumentSort : _typeClassInstantiation.argumentSorts()) + { + if (auto const* referencedDeclaration = argumentSort->annotation().referencedDeclaration) + { + if (!dynamic_cast(referencedDeclaration)) + m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Argument sort has to be a type class."); + // TODO: multi arities + arity._argumentSorts.emplace_back(Sort{{TypeClass{referencedDeclaration}}}); + } + else + { + // TODO: error Handling + m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Invalid sort."); + } + } + m_typeSystem.instantiateClass(typeConstructor, arity); + + if ( + auto [instantiation, newlyInserted] = annotation(*classDefintion).instantiations.emplace(typeConstructor, &_typeClassInstantiation); + !newlyInserted + ) + { + SecondarySourceLocation ssl; + ssl.append("Previous instantiation.", instantiation->second->location()); + m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); + } return false; } diff --git a/libsolidity/analysis/experimental/TypeRegistration.h b/libsolidity/analysis/experimental/TypeRegistration.h index 4aca6904f..0f6091bd1 100644 --- a/libsolidity/analysis/experimental/TypeRegistration.h +++ b/libsolidity/analysis/experimental/TypeRegistration.h @@ -33,6 +33,7 @@ public: struct Annotation { Type type; + std::map instantiations; }; TypeRegistration(Analysis& _analysis); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 24dd2b82a..8a4935ac5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1068,7 +1068,8 @@ public: bool _isIndexed = false, Mutability _mutability = Mutability::Mutable, ASTPointer _overrides = nullptr, - Location _referenceLocation = Location::Unspecified + Location _referenceLocation = Location::Unspecified, + std::vector> _sort = {} ): Declaration(_id, _location, _name, std::move(_nameLocation), _visibility), StructurallyDocumented(std::move(_documentation)), @@ -1077,10 +1078,11 @@ public: m_isIndexed(_isIndexed), m_mutability(_mutability), m_overrides(std::move(_overrides)), - m_location(_referenceLocation) + m_location(_referenceLocation), + m_sort(std::move(_sort)) { // TODO: consider still asserting unless we are in experimental solidity. - // solAssert(m_typeName, ""); + // solAssert(m_typeName, ""); solAssert(m_sorts.empy(), ""); } @@ -1142,6 +1144,7 @@ public: /// @returns null when it is not accessible as a function. FunctionTypePointer functionType(bool /*_internal*/) const override; + std::vector> const& sort() const { return m_sort; } VariableDeclarationAnnotation& annotation() const override; protected: @@ -1157,6 +1160,7 @@ private: Mutability m_mutability = Mutability::Mutable; ASTPointer m_overrides; ///< Contains the override specifier node Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. + std::vector> m_sort; }; /** @@ -2493,13 +2497,13 @@ public: SourceLocation const& _location, ASTPointer _typeConstructor, std::vector> const& _argumentSorts, - ASTPointer _sort, + ASTPointer _class, std::vector> _subNodes ): ASTNode(_id, _location), m_typeConstructor(std::move(_typeConstructor)), m_argumentSorts(std::move(_argumentSorts)), - m_sort(std::move(_sort)), + m_class(std::move(_class)), m_subNodes(std::move(_subNodes)) {} @@ -2508,7 +2512,7 @@ public: TypeName const& typeConstructor() const { return *m_typeConstructor; } std::vector> const& argumentSorts() const { return m_argumentSorts; } - IdentifierPath const& sort() const { return *m_sort; } + IdentifierPath const& typeClass() const { return *m_class; } std::vector> const& subNodes() const { return m_subNodes; } bool experimentalSolidityOnly() const override { return true; } @@ -2516,7 +2520,7 @@ public: private: ASTPointer m_typeConstructor; std::vector> m_argumentSorts; - ASTPointer m_sort; + ASTPointer m_class; std::vector> m_subNodes; }; diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 7e5c34794..1d853cf5d 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -296,6 +296,7 @@ void VariableDeclaration::accept(ASTVisitor& _visitor) { if (m_typeName) m_typeName->accept(_visitor); + listAccept(m_sort, _visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -310,6 +311,7 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const { if (m_typeName) m_typeName->accept(_visitor); + listAccept(m_sort, _visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -1056,7 +1058,7 @@ void TypeClassInstantiation::accept(ASTVisitor& _visitor) { m_typeConstructor->accept(_visitor); listAccept(m_argumentSorts, _visitor); - m_sort->accept(_visitor); + m_class->accept(_visitor); listAccept(m_subNodes, _visitor); } _visitor.endVisit(*this); @@ -1068,7 +1070,7 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const { m_typeConstructor->accept(_visitor); listAccept(m_argumentSorts, _visitor); - m_sort->accept(_visitor); + m_class->accept(_visitor); listAccept(m_subNodes, _visitor); } _visitor.endVisit(*this); diff --git a/libsolidity/ast/experimental/TypeSystem.cpp b/libsolidity/ast/experimental/TypeSystem.cpp index 4246b8963..7af05398d 100644 --- a/libsolidity/ast/experimental/TypeSystem.cpp +++ b/libsolidity/ast/experimental/TypeSystem.cpp @@ -35,7 +35,108 @@ using namespace std; using namespace solidity::frontend; using namespace solidity::frontend::experimental; -std::string TypeSystem::typeToString(Type const& _type) const +bool TypeClass::operator<(TypeClass const& _rhs) const +{ + return declaration->id() < _rhs.declaration->id(); +} + +bool Sort::operator==(Sort const& _rhs) const +{ + if (classes.size() != _rhs.classes.size()) + return false; + for (auto [lhs, rhs]: ranges::zip_view(classes, _rhs.classes)) + if (lhs != rhs) + return false; + return true; +} + +bool Sort::operator<(Sort const& _rhs) const +{ + for (auto c: classes) + if (!_rhs.classes.count(c)) + return false; + return true; +} + +Sort Sort::operator+(Sort const& _rhs) const +{ + Sort result { classes }; + result.classes += _rhs.classes; + return result; +} + +bool TypeExpression::operator<(TypeExpression const& _rhs) const +{ + if (constructor < _rhs.constructor) + return true; + if (_rhs.constructor < constructor) + return false; + solAssert(arguments.size() == _rhs.arguments.size()); + for(auto [lhs, rhs]: ranges::zip_view(arguments, _rhs.arguments)) + { + if (lhs < rhs) + return true; + if (rhs < lhs) + return false; + } + return false; +} + +std::string experimental::canonicalTypeName(Type _type) +{ + return std::visit(util::GenericVisitor{ + [&](TypeExpression const& _type) { + std::stringstream stream; + auto printTypeArguments = [&]() { + if (!_type.arguments.empty()) + { + stream << "$"; + for (auto type: _type.arguments | ranges::views::drop_last(1)) + stream << canonicalTypeName(type) << "$"; + stream << canonicalTypeName(_type.arguments.back()); + stream << "$"; + } + }; + std::visit(util::GenericVisitor{ + [&](Declaration const* _declaration) { + printTypeArguments(); + // TODO: canonical name + stream << _declaration->name(); + }, + [&](BuiltinType _builtinType) { + printTypeArguments(); + switch(_builtinType) + { + case BuiltinType::Void: + stream << "void"; + break; + case BuiltinType::Function: + stream << "fun"; + break; + case BuiltinType::Unit: + stream << "unit"; + break; + case BuiltinType::Pair: + stream << "pair"; + break; + case BuiltinType::Word: + stream << "word"; + break; + case BuiltinType::Integer: + stream << "integer"; + break; + } + } + }, _type.constructor); + return stream.str(); + }, + [](TypeVariable const&)-> string { + solAssert(false); + }, + }, _type); +} + +std::string TypeEnvironment::typeToString(Type const& _type) const { return std::visit(util::GenericVisitor{ [&](TypeExpression const& _type) { @@ -53,7 +154,7 @@ std::string TypeSystem::typeToString(Type const& _type) const std::visit(util::GenericVisitor{ [&](Declaration const* _declaration) { printTypeArguments(); - stream << _declaration->name(); + stream << m_typeSystem.typeName(_declaration); }, [&](BuiltinType _builtinType) { switch (_builtinType) @@ -68,7 +169,7 @@ std::string TypeSystem::typeToString(Type const& _type) const break; case BuiltinType::Pair: { - auto tupleTypes = TypeSystemHelpers{*this}.destTupleType(_type); + auto tupleTypes = TypeSystemHelpers{m_typeSystem}.destTupleType(_type); stream << "("; for (auto type: tupleTypes | ranges::views::drop_last(1)) stream << typeToString(type) << ", "; @@ -77,7 +178,7 @@ std::string TypeSystem::typeToString(Type const& _type) const } default: printTypeArguments(); - stream << builtinTypeName(_builtinType); + stream << m_typeSystem.typeName(_builtinType); break; } } @@ -85,12 +186,29 @@ std::string TypeSystem::typeToString(Type const& _type) const return stream.str(); }, [](TypeVariable const& _type) { - return fmt::format("{}var{}", _type.generic() ? '?' : '\'', _type.index()); + std::stringstream stream; + stream << (_type.generic() ? '?' : '\'') << "var" << _type.index(); + switch (_type.sort().classes.size()) + { + case 0: + break; + case 1: + stream << ":" << _type.sort().classes.begin()->declaration->name(); + break; + default: + stream << ":{"; + for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1)) + stream << typeClass.declaration->name() << ", "; + stream << _type.sort().classes.rbegin()->declaration->name(); + stream << "}"; + break; + } + return stream.str(); }, }, resolve(_type)); } -vector TypeSystem::unify(Type _a, Type _b) +vector TypeEnvironment::unify(Type _a, Type _b) { vector failures; auto unificationFailure = [&]() { @@ -100,10 +218,24 @@ vector TypeSystem::unify(Type _a, Type _b) _b = resolve(_b); std::visit(util::GenericVisitor{ [&](TypeVariable _left, TypeVariable _right) { - validate(_left); - validate(_right); - if (_left.index() != _right.index()) - instantiate(_left, _right); + if (_left.index() == _right.index()) + { + if (_left.sort() != _right.sort()) + unificationFailure(); + } + else + { + if (_left.sort() < _right.sort()) + instantiate(_left, _right); + else if (_right.sort() < _left.sort()) + instantiate(_right, _left); + else + { + Type newVar = m_typeSystem.freshTypeVariable(_left.generic() && _right.generic(), _left.sort() + _right.sort()); + instantiate(_left, newVar); + instantiate(_right, newVar); + } + } }, [&](TypeVariable _var, auto) { instantiate(_var, _b); @@ -126,55 +258,60 @@ vector TypeSystem::unify(Type _a, Type _b) return failures; } -experimental::Type TypeSystem::freshTypeVariable(bool _generic) +TypeEnvironment TypeEnvironment::clone() const { - uint64_t index = m_typeVariables.size(); - m_typeVariables.emplace_back(std::nullopt); - return TypeVariable(*this, index, _generic); + TypeEnvironment result{m_typeSystem}; + result.m_typeVariables = m_typeVariables; + return result; } -void TypeSystem::instantiate(TypeVariable _variable, Type _type) +experimental::Type TypeSystem::freshTypeVariable(bool _generic, Sort const& _sort) { - validate(_variable); - solAssert(!m_typeVariables.at(static_cast(_variable.index())).has_value()); - solAssert(_variable.m_parent == this); - m_typeVariables[static_cast(_variable.index())] = _type; + uint64_t index = m_numTypeVariables++; + return TypeVariable(index, _sort, _generic); } -experimental::Type TypeSystem::resolve(Type _type) const +void TypeEnvironment::instantiate(TypeVariable _variable, Type _type) +{ + solAssert(m_typeVariables.emplace(_variable.index(), _type).second); +} + +experimental::Type TypeEnvironment::resolve(Type _type) const { Type result = _type; while(auto const* var = std::get_if(&result)) - if (auto value = m_typeVariables.at(static_cast(var->index()))) - result = *value; + if (Type const* resolvedType = util::valueOrNullptr(m_typeVariables, var->index())) + result = *resolvedType; else break; return result; } -void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity) +void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments) { - solAssert(m_builtinTypes.count(_builtinType) == 0, "Builtin type already declared."); - m_builtinTypes[_builtinType] = TypeConstructorInfo{ + declareTypeConstructor(_builtinType, _name, _arguments); +} + +void TypeSystem::declareTypeConstructor(TypeExpression::Constructor _typeConstructor, std::string _name, size_t _arguments) +{ + bool newlyInserted = m_typeConstructors.emplace(std::make_pair(_typeConstructor, TypeConstructorInfo{ _name, - _arity - }; + _arguments, + {} + })).second; + // TODO: proper error handling. + solAssert(newlyInserted, "Type constructor already declared."); } experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector _arguments) const { - auto const& info = m_builtinTypes.at(_builtinType); - solAssert(info.arity == _arguments.size(), "Invalid arity."); + // TODO: proper error handling + auto const& info = m_typeConstructors.at(_builtinType); + solAssert(info.arguments == _arguments.size(), "Invalid arity."); return TypeExpression{_builtinType, _arguments}; } -void TypeSystem::validate(TypeVariable _variable) const -{ - solAssert(_variable.m_parent == this); - solAssert(_variable.index() < m_typeVariables.size()); -} - -experimental::Type TypeSystem::fresh(Type _type, bool _generalize) +experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize) { std::unordered_map mapping; auto freshImpl = [&](Type _type, bool _generalize, auto _recurse) -> Type { @@ -188,12 +325,17 @@ experimental::Type TypeSystem::fresh(Type _type, bool _generalize) }; }, [&](TypeVariable const& _var) -> Type { - validate(_var); if (_generalize || _var.generic()) { - if (mapping.count(_var.index())) - return mapping[_var.index()]; - return mapping[_var.index()] = freshTypeVariable(true); + if (auto* mapped = util::valueOrNullptr(mapping, _var.index())) + { + auto* typeVariable = get_if(mapped); + solAssert(typeVariable); + // TODO: can there be a mismatch? + solAssert(typeVariable->sort() == _var.sort()); + return *mapped; + } + return mapping[_var.index()] = m_typeSystem.freshTypeVariable(true, _var.sort()); } else return _type; @@ -203,11 +345,12 @@ experimental::Type TypeSystem::fresh(Type _type, bool _generalize) return freshImpl(_type, _generalize, freshImpl); } -void TypeSystem::instantiateClass(TypeExpression::Constructor _typeConstructor, vector _argumentSorts, TypeClass _class) +void TypeSystem::instantiateClass(TypeExpression::Constructor _typeConstructor, Arity _arity) { - (void)_typeConstructor; - (void)_argumentSorts; - (void)_class; + // TODO: proper error handling + auto& typeConstructorInfo = m_typeConstructors.at(_typeConstructor); + solAssert(_arity._argumentSorts.size() == typeConstructorInfo.arguments, "Invalid arity."); + typeConstructorInfo.arities.emplace_back(_arity); } experimental::Type TypeSystemHelpers::tupleType(vector _elements) const @@ -262,7 +405,7 @@ experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType}); } -tuple> TypeSystemHelpers::destTypeExpression(Type _functionType) const +tuple> TypeSystemHelpers::destTypeExpression(Type _type) const { using ResultType = tuple>; return std::visit(util::GenericVisitor{ @@ -272,7 +415,7 @@ tuple> TypeSystemHelpers [](auto) -> ResultType { solAssert(false); } - }, _functionType); + }, _type); } tuple TypeSystemHelpers::destFunctionType(Type _functionType) const @@ -283,3 +426,23 @@ tuple TypeSystemHelpers::destFunctionTyp solAssert(arguments.size() == 2); return make_tuple(arguments.front(), arguments.back()); } + +vector TypeSystemHelpers::typeVars(Type _type) const +{ + vector typeVars; + auto typeVarsImpl = [&](Type _type, auto _recurse) -> void { + std::visit(util::GenericVisitor{ + [&](TypeExpression const& _type) { + for (auto arg: _type.arguments) + _recurse(arg, _recurse); + }, + [&](TypeVariable const& _var) { + typeVars.emplace_back(_var); + }, +// TODO: move to env helpers? + }, typeSystem.env().resolve(_type)); + }; + typeVarsImpl(_type, typeVarsImpl); + return typeVars; + +} \ No newline at end of file diff --git a/libsolidity/ast/experimental/TypeSystem.h b/libsolidity/ast/experimental/TypeSystem.h index 28fdf16de..3406f5a9e 100644 --- a/libsolidity/ast/experimental/TypeSystem.h +++ b/libsolidity/ast/experimental/TypeSystem.h @@ -40,6 +40,8 @@ struct TypeVariable; using Type = std::variant; +std::string canonicalTypeName(Type _type); + enum class BuiltinType { Void, @@ -55,68 +57,123 @@ struct TypeExpression using Constructor = std::variant; Constructor constructor; std::vector arguments; -}; - -struct TypeVariable -{ - TypeSystem const& parent() const { return *m_parent; } - uint64_t index() const { return m_index; } - bool generic() const { return m_generic; } -private: - friend class TypeSystem; - TypeSystem const* m_parent = nullptr; - uint64_t m_index = 0; - bool m_generic = false; - TypeVariable(TypeSystem const& _parent, uint64_t _index, bool _generic): m_parent(&_parent), m_index(_index), m_generic(_generic) {} + bool operator<(TypeExpression const& _rhs) const; + bool operator==(TypeExpression const& _rhs) const + { + // TODO + return !(*this < _rhs) && !(_rhs < *this); + } + bool operator!=(TypeExpression const& _rhs) const + { + return !operator==(_rhs); + } }; struct TypeClass { Declaration const* declaration = nullptr; + bool operator<(TypeClass const& _rhs) const; + bool operator==(TypeClass const& _rhs) const { return declaration == _rhs.declaration; } + bool operator!=(TypeClass const& _rhs) const { return declaration != _rhs.declaration; } }; struct Sort { - + std::set classes; + bool operator==(Sort const& _rhs) const; + bool operator!=(Sort const& _rhs) const { return !operator==(_rhs); } + bool operator<(Sort const& _rhs) const; + Sort operator+(Sort const& _rhs) const; }; struct Arity { - std::vector _argumentSorts; + std::vector _argumentSorts; TypeClass _class; }; + +struct TypeVariable +{ + size_t index() const { return m_index; } + bool generic() const { return m_generic; } + Sort const& sort() const { return m_sort; } + bool operator<(TypeVariable const& _rhs) const + { + // TODO: more robust comparison? + return m_index < _rhs.m_index; + } + bool operator==(TypeVariable const& _rhs) const + { + // TODO + return !(*this < _rhs) && !(_rhs < *this); + } + bool operator!=(TypeVariable const& _rhs) const + { + return !operator==(_rhs); + } +private: + friend class TypeSystem; + size_t m_index = 0; + Sort m_sort; + bool m_generic = false; + TypeVariable(size_t _index, Sort _sort, bool _generic): + m_index(_index), m_sort(std::move(_sort)), m_generic(_generic) {} +}; + +class TypeEnvironment +{ +public: + TypeEnvironment(TypeSystem& _typeSystem): m_typeSystem(_typeSystem) {} + TypeEnvironment(TypeEnvironment const&) = delete; + TypeEnvironment& operator=(TypeEnvironment const&) = delete; + TypeEnvironment clone() const; + Type resolve(Type _type) const; + Type fresh(Type _type, bool _generalize); + struct UnificationFailure { Type a; Type b; }; + [[nodiscard]] std::vector unify(Type _a, Type _b); + std::string typeToString(Type const& _type) const; +private: + TypeEnvironment(TypeEnvironment&& _env): m_typeSystem(_env.m_typeSystem), m_typeVariables(std::move(_env.m_typeVariables)) {} + void instantiate(TypeVariable _variable, Type _type); + TypeSystem& m_typeSystem; + std::map m_typeVariables; +}; + class TypeSystem { public: TypeSystem() {} TypeSystem(TypeSystem const&) = delete; TypeSystem const& operator=(TypeSystem const&) = delete; - void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity); + void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments); Type builtinType(BuiltinType _builtinType, std::vector _arguments) const; - std::string builtinTypeName(BuiltinType _builtinType) const + std::string typeName(TypeExpression::Constructor _typeConstructor) const { - return m_builtinTypes.at(_builtinType).name; + // TODO: proper error handling + return m_typeConstructors.at(_typeConstructor).name; } - Type freshFreeType(); - Type resolve(Type _type) const; - std::string typeToString(Type const& _type) const; - Type freshTypeVariable(bool _generic); - Type fresh(Type _type, bool _generalize); - struct UnificationFailure { Type a; Type b; }; - [[nodiscard]] std::vector unify(Type _a, Type _b); - void instantiateClass(TypeExpression::Constructor _typeConstructor, std::vector _argumentSorts, TypeClass _class); + void declareTypeConstructor(TypeExpression::Constructor _typeConstructor, std::string _name, size_t _arguments); + size_t constructorArguments(TypeExpression::Constructor _typeConstructor) const + { + // TODO: error handling + return m_typeConstructors.at(_typeConstructor).arguments; + } + void instantiateClass(TypeExpression::Constructor _typeConstructor, Arity _arity); + + Type freshTypeVariable(bool _generic, Sort const& _sort); + + TypeEnvironment const& env() const { return m_globalTypeEnvironment; } + TypeEnvironment& env() { return m_globalTypeEnvironment; } private: - void instantiate(TypeVariable _variable, Type _type); - void validate(TypeVariable _variable) const; - std::vector> m_freeTypes; + size_t m_numTypeVariables = 0; struct TypeConstructorInfo { std::string name; - uint64_t arity = 0; + size_t arguments; + std::vector arities; }; - std::map m_builtinTypes; - std::vector> m_typeVariables; - std::map m_sorts; + std::map m_typeConstructors; + TypeEnvironment m_globalTypeEnvironment{*this}; }; struct TypeSystemHelpers @@ -127,6 +184,7 @@ struct TypeSystemHelpers std::vector destTupleType(Type _tupleType) const; Type functionType(Type _argType, Type _resultType) const; std::tuple destFunctionType(Type _functionType) const; + std::vector typeVars(Type _type) const; }; } diff --git a/libsolidity/codegen/experimental/Common.cpp b/libsolidity/codegen/experimental/Common.cpp new file mode 100644 index 000000000..a82efd022 --- /dev/null +++ b/libsolidity/codegen/experimental/Common.cpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::util; +using namespace solidity::yul; + +namespace solidity::frontend::experimental +{ + +string IRNames::function(FunctionDefinition const& _function, Type _type) +{ + if (_function.isConstructor()) + return constructor(*_function.annotation().contract); + + return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + canonicalTypeName(_type) + "$"; +} + +string IRNames::function(VariableDeclaration const& _varDecl) +{ + return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); +} + +string IRNames::creationObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()); +} + +string IRNames::deployedObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; +} + +string IRNames::constructor(ContractDefinition const& _contract) +{ + return "constructor_" + _contract.name() + "_" + to_string(_contract.id()); +} + +string IRNames::localVariable(VariableDeclaration const& _declaration) +{ + return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id()); +} + +string IRNames::localVariable(Expression const& _expression) +{ + return "expr_" + to_string(_expression.id()); +} + +} diff --git a/libsolidity/codegen/experimental/Common.h b/libsolidity/codegen/experimental/Common.h new file mode 100644 index 000000000..7ac88843f --- /dev/null +++ b/libsolidity/codegen/experimental/Common.h @@ -0,0 +1,41 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include + +namespace solidity::frontend::experimental +{ + +struct IRNames +{ + static std::string function(FunctionDefinition const& _function, Type _type); + static std::string function(VariableDeclaration const& _varDecl); + static std::string creationObject(ContractDefinition const& _contract); + static std::string deployedObject(ContractDefinition const& _contract); + static std::string constructor(ContractDefinition const& _contract); + static std::string localVariable(VariableDeclaration const& _declaration); + static std::string localVariable(Expression const& _expression); +}; + +} diff --git a/libsolidity/codegen/experimental/IRGenerationContext.h b/libsolidity/codegen/experimental/IRGenerationContext.h index c52215e70..e52f8f7ae 100644 --- a/libsolidity/codegen/experimental/IRGenerationContext.h +++ b/libsolidity/codegen/experimental/IRGenerationContext.h @@ -20,6 +20,9 @@ #include +#include +#include + #include #include @@ -31,13 +34,23 @@ class Analysis; struct IRGenerationContext { Analysis const& analysis; - std::list functionQueue; - std::set generatedFunctions; - void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition) + TypeEnvironment const* env = nullptr; + void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition, Type _type) { - if (!generatedFunctions.count(_functionDefinition)) - functionQueue.emplace_back(_functionDefinition); + QueuedFunction queue{_functionDefinition, env->resolve(_type)}; + if (!generatedFunctions.count(queue)) + functionQueue.emplace_back(queue); } + struct QueuedFunction + { + FunctionDefinition const* function; + Type type; + bool operator<(QueuedFunction const& _rhs) const { + return std::make_tuple(function, type) < std::make_tuple(_rhs.function, _rhs.type); + } + }; + std::list functionQueue; + std::set generatedFunctions; }; } diff --git a/libsolidity/codegen/experimental/IRGenerator.cpp b/libsolidity/codegen/experimental/IRGenerator.cpp index 10cd65b31..cd2bec0e6 100644 --- a/libsolidity/codegen/experimental/IRGenerator.cpp +++ b/libsolidity/codegen/experimental/IRGenerator.cpp @@ -17,11 +17,14 @@ // SPDX-License-Identifier: GPL-3.0 #include - #include #include -#include +#include + +#include +#include + #include #include @@ -42,6 +45,24 @@ using namespace solidity::frontend::experimental; using namespace solidity::langutil; using namespace solidity::util; +IRGenerator::IRGenerator( + EVMVersion _evmVersion, + std::optional _eofVersion, + frontend::RevertStrings, std::map, + DebugInfoSelection const&, + CharStreamProvider const*, + Analysis const& _analysis +) +: +m_evmVersion(_evmVersion), +m_eofVersion(_eofVersion), +// m_debugInfoSelection(_debugInfoSelection), +// m_soliditySourceProvider(_soliditySourceProvider), +m_env(_analysis.typeSystem().env().clone()), +m_context{_analysis, &m_env, {}, {}} +{ +} + string IRGenerator::run( ContractDefinition const& _contract, bytes const& /*_cborMetadata*/, @@ -75,30 +96,33 @@ string IRGenerator::generate(ContractDefinition const& _contract) code << "{\n"; if (_contract.fallbackFunction()) { - code << IRNames::function(*_contract.fallbackFunction()) << "()\n"; - m_context.functionQueue.emplace_front(_contract.fallbackFunction()); + auto type = m_context.analysis.annotation(*_contract.fallbackFunction()).type; + solAssert(type); + type = m_context.analysis.typeSystem().env().resolve(*type); + code << IRNames::function(*_contract.fallbackFunction(), *type) << "()\n"; + m_context.enqueueFunctionDefinition(_contract.fallbackFunction(), *type); } code << "revert(0,0)\n"; code << "}\n"; while (!m_context.functionQueue.empty()) { - FunctionDefinition const* function = m_context.functionQueue.front(); + auto function = m_context.functionQueue.front(); m_context.functionQueue.pop_front(); if (!m_context.generatedFunctions.count(function)) { m_context.generatedFunctions.insert(function); - code << generate(*function); + code << generate(*function.function, function.type); } } return code.str(); } -string IRGenerator::generate(FunctionDefinition const& _function) +string IRGenerator::generate(FunctionDefinition const& _function, Type _type) { std::stringstream code; - code << "function " << IRNames::function(_function) << "("; + code << "function " << IRNames::function(_function, _type) << "("; if (_function.parameters().size() > 1) for (auto const& arg: _function.parameters() | ranges::views::drop_last(1)) code << IRNames::localVariable(*arg) << ", "; diff --git a/libsolidity/codegen/experimental/IRGenerator.h b/libsolidity/codegen/experimental/IRGenerator.h index c0251fa14..125d06bf0 100644 --- a/libsolidity/codegen/experimental/IRGenerator.h +++ b/libsolidity/codegen/experimental/IRGenerator.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,6 @@ namespace solidity::frontend::experimental { -class SourceUnit; class Analysis; class IRGenerator @@ -49,13 +49,7 @@ public: langutil::DebugInfoSelection const& /*_debugInfoSelection*/, langutil::CharStreamProvider const* /*_soliditySourceProvider*/, Analysis const& _analysis - ): - m_evmVersion(_evmVersion), - m_eofVersion(_eofVersion), -// m_debugInfoSelection(_debugInfoSelection), -// m_soliditySourceProvider(_soliditySourceProvider), - m_context{_analysis, {}, {}} - {} + ); std::string run( ContractDefinition const& _contract, @@ -64,13 +58,14 @@ public: ); std::string generate(ContractDefinition const& _contract); - std::string generate(FunctionDefinition const& _function); + std::string generate(FunctionDefinition const& _function, Type _type); private: langutil::EVMVersion const m_evmVersion; std::optional const m_eofVersion; OptimiserSettings const m_optimiserSettings; // langutil::DebugInfoSelection m_debugInfoSelection = {}; // langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; + TypeEnvironment m_env; IRGenerationContext m_context; }; diff --git a/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp b/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp index 499c66ccb..494f137c0 100644 --- a/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/experimental/IRGeneratorForStatements.cpp @@ -18,12 +18,16 @@ #include +#include +#include +#include + #include #include #include #include -#include +#include #include @@ -46,9 +50,10 @@ namespace { struct CopyTranslate: public yul::ASTCopier { CopyTranslate( + IRGenerationContext const& _context, yul::Dialect const& _dialect, map _references - ): m_dialect(_dialect), m_references(std::move(_references)) {} + ): m_context(_context), m_dialect(_dialect), m_references(std::move(_references)) {} using ASTCopier::operator(); @@ -90,11 +95,15 @@ private: auto const& reference = m_references.at(&_identifier); auto const varDecl = dynamic_cast(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. + auto type = m_context.analysis.annotation(*varDecl).type; + solAssert(type); + type = m_context.env->resolve(*type); + solAssert(*type == m_context.analysis.typeSystem().builtinType(BuiltinType::Word, {})); string value = IRNames::localVariable(*varDecl); return yul::Identifier{_identifier.debugData, yul::YulString{value}}; } + IRGenerationContext const& m_context; yul::Dialect const& m_dialect; map m_references; }; @@ -103,7 +112,7 @@ private: bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly) { - CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences}; + CopyTranslate bodyCopier{m_context, _assembly.dialect(), _assembly.annotation().externalReferences}; yul::Statement modified = bodyCopier(_assembly.operations()); solAssert(holds_alternative(modified)); m_code << yul::AsmPrinter()(std::get(modified)) << "\n"; @@ -153,13 +162,45 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) for(auto arg: _functionCall.arguments()) arg->accept(*this); - auto const* identifier = dynamic_cast(&_functionCall.expression()); - solAssert(identifier, "Complex function call expressions not supported."); - auto const* functionDefinition = dynamic_cast(identifier->annotation().referencedDeclaration); - solAssert(functionDefinition, "Function call expression must refer to a function definition."); - m_context.enqueueFunctionDefinition(functionDefinition); + FunctionDefinition const* functionDefinition = nullptr; + if (auto const* identifier = dynamic_cast(&_functionCall.expression())) + { + functionDefinition = dynamic_cast(identifier->annotation().referencedDeclaration); + } + else if (auto const* memberAccess = dynamic_cast(&_functionCall.expression())) + { + auto const& expressionAnnotation = m_context.analysis.annotation(memberAccess->expression()); + solAssert(expressionAnnotation.type); - m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition) << "("; + auto typeConstructor = std::get<0>(TypeSystemHelpers{m_context.analysis.typeSystem()}.destTypeExpression( + m_context.analysis.typeSystem().env().resolve(*expressionAnnotation.type) + )); + auto const* typeClass = dynamic_cast(&memberAccess->expression()); + solAssert(typeClass, "Function call to member access only supported for type classes."); + auto const* typeClassDefinition = dynamic_cast(typeClass->annotation().referencedDeclaration); + solAssert(typeClassDefinition, "Function call to member access only supported for type classes."); + auto const& classAnnotation = m_context.analysis.annotation(*typeClassDefinition); + TypeClassInstantiation const* instantiation = classAnnotation.instantiations.at(typeConstructor); + for (auto const& node: instantiation->subNodes()) + { + auto const* def = dynamic_cast(node.get()); + solAssert(def); + if (def->name() == memberAccess->memberName()) + { + functionDefinition = def; + break; + } + } + } + else + solAssert(false, "Complex function call expressions not supported."); + + solAssert(functionDefinition); + auto functionType = m_context.analysis.annotation(_functionCall).type; + solAssert(functionType); + functionType = m_context.analysis.typeSystem().env().resolve(*functionType); + m_context.enqueueFunctionDefinition(functionDefinition, *functionType); + m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition, *functionType) << "("; auto const& arguments = _functionCall.arguments(); if (arguments.size() > 1) for (auto arg: arguments | ranges::views::drop_last(1)) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 3aba46305..61b238480 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1717,11 +1717,42 @@ ASTPointer Parser::parsePostfixVariableDeclaration() auto [identifier, nameLocation] = expectIdentifierWithLocation(); ASTPointer type; + vector> sorts; if (m_scanner->currentToken() == Token::Colon) { advance(); - type = parseTypeName(); - nodeFactory.setEndPositionFromNode(type); + // TODO: handle this in parseTypeName()? + if (m_scanner->currentLiteral() == "_") + { + nodeFactory.markEndPosition(); + advance(); + } + else + { + type = parseTypeName(); + nodeFactory.setEndPositionFromNode(type); + } + if (m_scanner->currentToken() == Token::Colon) + { + advance(); + if (m_scanner->currentToken() == Token::LBrace) + { + // TODO + sorts.emplace_back(parseIdentifierPath()); + while (m_scanner->currentToken() == Token::Comma) + { + advance(); + sorts.emplace_back(parseIdentifierPath()); + } + expectToken(Token::RBrace); + } + else + { + sorts.emplace_back(parseIdentifierPath()); + } + if (!sorts.empty()) + nodeFactory.setEndPositionFromNode(sorts.back()); + } } return nodeFactory.createNode( @@ -1730,7 +1761,12 @@ ASTPointer Parser::parsePostfixVariableDeclaration() nameLocation, nullptr, Visibility::Default, - documentation + documentation, + false, + VariableDeclaration::Mutability::Mutable, + nullptr, + VariableDeclaration::Location::Unspecified, + sorts ); } diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index 561df62e0..395b32a9c 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -1,28 +1,44 @@ pragma experimental solidity; -class a:StackType { - function stackSize() -> x:integer; +class a:A { + function testValue(x:a) -> y:word; } -instantiation word : StackType { - function stackSize() -> x:integer { - return x; - } + +class a:B { + function testValue(x:a) -> y:word; } -function f(a) -> b { +instantiation word : A { + function testValue(x:word) -> y:word { + assembly { + y := 7 + } + } +} + +instantiation word : B { + function testValue(x:word) -> y:word { + assembly { + y := 14 + } + } +} + +function f(a:_:A) -> b:_:B { return a; } contract C { fallback() external { - let x : word; - let y : word; + let x : word : A; + let y; assembly { x := 0x42 } y = f(x); + y = B.testValue(x); assembly { mstore(0, y) return(0, 32)