From 201259d50b936bdef5c4b7d54c5e6de0940a2aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 20 Sep 2023 14:26:12 +0200 Subject: [PATCH 1/5] [TMP] Remove polymorphicInstance() from class type member types --- libsolidity/experimental/analysis/TypeInference.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 7d663e894..e0265ab6f 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -216,8 +216,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) subNode->accept(*this); auto const* functionDefinition = dynamic_cast(subNode.get()); solAssert(functionDefinition); - // TODO: need polymorphicInstance? - auto functionType = polymorphicInstance(typeAnnotation(*functionDefinition)); + auto functionType = typeAnnotation(*functionDefinition); if (!functionTypes.emplace(functionDefinition->name(), functionType).second) m_errorReporter.fatalTypeError(3195_error, functionDefinition->location(), "Function in type class declared multiple times."); auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); From 4960de7b42b6bbcd7389be358bbe27d789d3b92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 19 Sep 2023 17:13:25 +0200 Subject: [PATCH 2/5] Move builtin type class registration to TypeClassRegistration --- .../analysis/TypeClassRegistration.cpp | 24 +++++++++++ .../analysis/TypeClassRegistration.h | 2 + .../experimental/analysis/TypeInference.cpp | 42 +++++-------------- .../experimental/analysis/TypeInference.h | 2 - .../codegen/IRGeneratorForStatements.cpp | 2 +- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.cpp b/libsolidity/experimental/analysis/TypeClassRegistration.cpp index 10d3d6c3f..3dd41d922 100644 --- a/libsolidity/experimental/analysis/TypeClassRegistration.cpp +++ b/libsolidity/experimental/analysis/TypeClassRegistration.cpp @@ -32,6 +32,30 @@ TypeClassRegistration::TypeClassRegistration(Analysis& _analysis): m_errorReporter(_analysis.errorReporter()), m_typeSystem(_analysis.typeSystem()) { + auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class) -> TypeClass { + Type type = m_typeSystem.freshTypeVariable({}); + auto result = m_typeSystem.declareTypeClass( + type, + _name, + nullptr + ); + if (auto error = std::get_if(&result)) + solAssert(!error, *error); + TypeClass declaredClass = std::get(result); + // TODO: validation? + GlobalAnnotation& annotation = m_analysis.annotation(); + solAssert(annotation.builtinClassesByName.emplace(_name, _class).second); + return annotation.builtinClasses.emplace(_class, declaredClass).first->second; + }; + + declareBuiltinClass("integer", BuiltinClass::Integer); + declareBuiltinClass("*", BuiltinClass::Mul); + declareBuiltinClass("+", BuiltinClass::Add); + declareBuiltinClass("==", BuiltinClass::Equal); + declareBuiltinClass("<", BuiltinClass::Less); + declareBuiltinClass("<=", BuiltinClass::LessOrEqual); + declareBuiltinClass(">", BuiltinClass::Greater); + declareBuiltinClass(">=", BuiltinClass::GreaterOrEqual); } bool TypeClassRegistration::analyze(SourceUnit const& _sourceUnit) diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.h b/libsolidity/experimental/analysis/TypeClassRegistration.h index c0c1c3549..15923b78a 100644 --- a/libsolidity/experimental/analysis/TypeClassRegistration.h +++ b/libsolidity/experimental/analysis/TypeClassRegistration.h @@ -41,6 +41,8 @@ public: }; struct GlobalAnnotation { + std::map builtinClasses; + std::map builtinClassesByName; }; TypeClassRegistration(Analysis& _analysis); diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index e0265ab6f..97b09ea04 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -53,23 +53,8 @@ TypeInference::TypeInference(Analysis& _analysis): { TypeSystemHelpers helper{m_typeSystem}; - auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class) -> TypeClass { - Type type = m_typeSystem.freshTypeVariable({}); - auto result = m_typeSystem.declareTypeClass( - type, - _name, - nullptr - ); - if (auto error = std::get_if(&result)) - solAssert(!error, *error); - TypeClass declaredClass = std::get(result); - // TODO: validation? - solAssert(annotation().builtinClassesByName.emplace(_name, _class).second); - return annotation().builtinClasses.emplace(_class, declaredClass).first->second; - }; - auto registeredTypeClass = [&](BuiltinClass _builtinClass) -> TypeClass { - return annotation().builtinClasses.at(_builtinClass); + return m_analysis.annotation().builtinClasses.at(_builtinClass); }; auto defineConversion = [&](BuiltinClass _builtinClass, PrimitiveType _fromType, std::string _functionName) { @@ -106,15 +91,6 @@ TypeInference::TypeInference(Analysis& _analysis): }}; }; - declareBuiltinClass("integer", BuiltinClass::Integer); - declareBuiltinClass("*", BuiltinClass::Mul); - declareBuiltinClass("+", BuiltinClass::Add); - declareBuiltinClass("==", BuiltinClass::Equal); - declareBuiltinClass("<", BuiltinClass::Less); - declareBuiltinClass("<=", BuiltinClass::LessOrEqual); - declareBuiltinClass(">", BuiltinClass::Greater); - declareBuiltinClass(">=", BuiltinClass::GreaterOrEqual); - defineConversion(BuiltinClass::Integer, PrimitiveType::Integer, "fromInteger"); defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); @@ -640,8 +616,10 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) } }, [&](Token _token) -> std::optional { + auto const& classRegistrationAnnotation = m_analysis.annotation(); + if (auto builtinClass = builtinClassFromToken(_token)) - if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) + if (auto typeClass = util::valueOrNullptr(classRegistrationAnnotation.builtinClasses, *builtinClass)) return *typeClass; m_errorReporter.typeError(2658_error, _typeClassInstantiation.location(), "Invalid type class name."); return std::nullopt; @@ -1044,7 +1022,9 @@ bool TypeInference::visit(Literal const& _literal) m_errorReporter.typeError(2345_error, _literal.location(), "Only integers are supported."); return false; } - literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{annotation().builtinClasses.at(BuiltinClass::Integer)}}); + + TypeClass integerClass = m_analysis.annotation().builtinClasses.at(BuiltinClass::Integer); + literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{integerClass}}); return false; } @@ -1058,10 +1038,10 @@ TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(Analysi if (typeClassDeclaration) return _analysis.annotation(*typeClassDeclaration).instantiations; // TODO: better mechanism than fetching by name. - auto const& annotation = _analysis.annotation(); - auto const& inferenceAnnotation = _analysis.annotation(); - return annotation.builtinClassInstantiations.at( - inferenceAnnotation.builtinClassesByName.at( + auto const& typeRegistrationAnnotation = _analysis.annotation(); + auto const& classRegistrationAnnotation = _analysis.annotation(); + return typeRegistrationAnnotation.builtinClassInstantiations.at( + classRegistrationAnnotation.builtinClassesByName.at( _analysis.typeSystem().typeClassName(_class) ) ); diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index eb1b03f69..7f873e11f 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -45,8 +45,6 @@ public: }; struct GlobalAnnotation { - std::map builtinClasses; - std::map builtinClassesByName; std::map> typeClassFunctions; std::map> operators; std::map> members; diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 589fe6787..a579a8369 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -219,7 +219,7 @@ TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGener return _context.analysis.annotation(*typeClassDeclaration).instantiations; // TODO: better mechanism than fetching by name. auto& instantiations = _context.analysis.annotation().builtinClassInstantiations; - auto& builtinClassesByName = _context.analysis.annotation().builtinClassesByName; + auto& builtinClassesByName = _context.analysis.annotation().builtinClassesByName; return instantiations.at(builtinClassesByName.at(_context.analysis.typeSystem().typeClassName(_class))); } } From ea8daf16138186d877c05a9745ed12fa648249a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 28 Sep 2023 14:57:04 +0200 Subject: [PATCH 3/5] Extract type class member registration into a separate experimental analysis pass --- libsolidity/CMakeLists.txt | 2 + .../experimental/analysis/Analysis.cpp | 29 ++++ .../analysis/TypeClassMemberRegistration.cpp | 139 ++++++++++++++++++ .../analysis/TypeClassMemberRegistration.h | 62 ++++++++ .../experimental/analysis/TypeInference.cpp | 101 ++++--------- .../experimental/analysis/TypeInference.h | 2 - .../codegen/IRGeneratorForStatements.cpp | 5 +- 7 files changed, 262 insertions(+), 78 deletions(-) create mode 100644 libsolidity/experimental/analysis/TypeClassMemberRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeClassMemberRegistration.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 6ad451ed1..db06e9802 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -188,6 +188,8 @@ set(sources experimental/analysis/Analysis.h experimental/analysis/DebugWarner.cpp experimental/analysis/DebugWarner.h + experimental/analysis/TypeClassMemberRegistration.cpp + experimental/analysis/TypeClassMemberRegistration.h experimental/analysis/TypeClassRegistration.cpp experimental/analysis/TypeClassRegistration.h experimental/analysis/TypeInference.cpp diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 8d3f40ca0..b8355423b 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ using namespace solidity::frontend::experimental; // TODO: creating all of them for all nodes up front may be wasteful, we should improve the mechanism. struct Analysis::AnnotationContainer { + TypeClassMemberRegistration::Annotation typeClassMemberRegistrationAnnotation; TypeClassRegistration::Annotation typeClassRegistrationAnnotation; TypeRegistration::Annotation typeRegistrationAnnotation; TypeInference::Annotation typeInferenceAnnotation; @@ -35,11 +37,37 @@ struct Analysis::AnnotationContainer struct Analysis::GlobalAnnotationContainer { + TypeClassMemberRegistration::GlobalAnnotation typeClassMemberRegistrationAnnotation; TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation; TypeRegistration::GlobalAnnotation typeRegistrationAnnotation; TypeInference::GlobalAnnotation typeInferenceAnnotation; }; +template<> +TypeClassMemberRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeClassMemberRegistrationAnnotation; +} + +template<> +TypeClassMemberRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeClassMemberRegistrationAnnotation; +} + + +template<> +TypeClassMemberRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeClassMemberRegistrationAnnotation; +} + +template<> +TypeClassMemberRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeClassMemberRegistrationAnnotation; +} + template<> TypeClassRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) { @@ -153,6 +181,7 @@ bool Analysis::check(std::vector> const& _sour SyntaxRestrictor, TypeClassRegistration, TypeRegistration, + TypeClassMemberRegistration, TypeInference, DebugWarner >; diff --git a/libsolidity/experimental/analysis/TypeClassMemberRegistration.cpp b/libsolidity/experimental/analysis/TypeClassMemberRegistration.cpp new file mode 100644 index 000000000..5fe088cdc --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassMemberRegistration.cpp @@ -0,0 +1,139 @@ +/* + 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 +#include + +#include +#include + +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeClassMemberRegistration::TypeClassMemberRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ + TypeSystemHelpers helper{m_typeSystem}; + + auto registeredTypeClass = [&](BuiltinClass _builtinClass) -> TypeClass { + return m_analysis.annotation().builtinClasses.at(_builtinClass); + }; + + auto defineConversion = [&](BuiltinClass _builtinClass, PrimitiveType _fromType, std::string _functionName) { + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + m_typeSystem.type(_fromType, {}), + m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable + ), + }}; + }; + + auto defineBinaryMonoidalOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + typeVar + ) + }}; + }; + + auto defineBinaryCompareOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + m_typeSystem.type(PrimitiveType::Bool, {}) + ) + }}; + }; + + defineConversion(BuiltinClass::Integer, PrimitiveType::Integer, "fromInteger"); + + defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); + defineBinaryMonoidalOperator(BuiltinClass::Add, Token::Add, "add"); + + defineBinaryCompareOperator(BuiltinClass::Equal, Token::Equal, "eq"); + defineBinaryCompareOperator(BuiltinClass::Less, Token::LessThan, "lt"); + defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq"); + defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt"); + defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq"); +} + +bool TypeClassMemberRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +void TypeClassMemberRegistration::endVisit(TypeClassDefinition const& _typeClassDefinition) +{ + solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); + + _typeClassDefinition.typeVariable().accept(*this); + + std::map functionTypes; + for (auto subNode: _typeClassDefinition.subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + + std::optional functionType = TypeSystemHelpers{m_typeSystem}.functionType( + m_typeSystem.freshTypeVariable({}), + m_typeSystem.freshTypeVariable({}) + ); + + if (!functionTypes.emplace(functionDefinition->name(), functionType.value()).second) + m_errorReporter.fatalTypeError( + 3195_error, + // TODO: Secondary location with previous definition + functionDefinition->location(), + "Function in type class declared multiple times." + ); + } + + annotation().typeClassFunctions[typeClass] = std::move(functionTypes); +} + +TypeClassMemberRegistration::Annotation& TypeClassMemberRegistration::annotation(ASTNode const& _node) +{ + return m_analysis.annotation(_node); +} + +TypeClassMemberRegistration::Annotation const& TypeClassMemberRegistration::annotation(ASTNode const& _node) const +{ + return m_analysis.annotation(_node); +} + +TypeClassMemberRegistration::GlobalAnnotation& TypeClassMemberRegistration::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/TypeClassMemberRegistration.h b/libsolidity/experimental/analysis/TypeClassMemberRegistration.h new file mode 100644 index 000000000..cb58143a4 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassMemberRegistration.h @@ -0,0 +1,62 @@ +/* + 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::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend::experimental +{ + +class Analysis; +class TypeSystem; + +class TypeClassMemberRegistration: public ASTConstVisitor +{ +public: + struct Annotation + { + }; + struct GlobalAnnotation + { + std::map> typeClassFunctions; + std::map> operators; + }; + + TypeClassMemberRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + +private: + void endVisit(TypeClassDefinition const& _typeClassDefinition) override; + + Annotation& annotation(ASTNode const& _node); + Annotation const& annotation(ASTNode const& _node) const; + GlobalAnnotation& annotation(); + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 97b09ea04..fded26b75 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -51,56 +52,6 @@ TypeInference::TypeInference(Analysis& _analysis): m_unitType(m_typeSystem.type(PrimitiveType::Unit, {})), m_boolType(m_typeSystem.type(PrimitiveType::Bool, {})) { - TypeSystemHelpers helper{m_typeSystem}; - - auto registeredTypeClass = [&](BuiltinClass _builtinClass) -> TypeClass { - return m_analysis.annotation().builtinClasses.at(_builtinClass); - }; - - auto defineConversion = [&](BuiltinClass _builtinClass, PrimitiveType _fromType, std::string _functionName) { - annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ - std::move(_functionName), - helper.functionType( - m_typeSystem.type(_fromType, {}), - m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable - ), - }}; - }; - - auto defineBinaryMonoidalOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { - Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; - annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); - annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ - std::move(_functionName), - helper.functionType( - helper.tupleType({typeVar, typeVar}), - typeVar - ) - }}; - }; - - auto defineBinaryCompareOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { - Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; - annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); - annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ - std::move(_functionName), - helper.functionType( - helper.tupleType({typeVar, typeVar}), - m_typeSystem.type(PrimitiveType::Bool, {}) - ) - }}; - }; - - defineConversion(BuiltinClass::Integer, PrimitiveType::Integer, "fromInteger"); - - defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); - defineBinaryMonoidalOperator(BuiltinClass::Add, Token::Add, "add"); - - defineBinaryCompareOperator(BuiltinClass::Equal, Token::Equal, "eq"); - defineBinaryCompareOperator(BuiltinClass::Less, Token::LessThan, "lt"); - defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq"); - defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt"); - defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq"); } bool TypeInference::analyze(SourceUnit const& _sourceUnit) @@ -112,19 +63,17 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit) bool TypeInference::visit(FunctionDefinition const& _functionDefinition) { solAssert(m_expressionContext == ExpressionContext::Term); - auto& functionAnnotation = annotation(_functionDefinition); - if (functionAnnotation.type) - return false; + + // For type class members the type annotation is filled by visit(TypeClassDefinition) + if (!annotation(_functionDefinition).type.has_value()) + annotation(_functionDefinition).type = TypeSystemHelpers{m_typeSystem}.functionType( + m_typeSystem.freshTypeVariable({}), + m_typeSystem.freshTypeVariable({}) + ); ScopedSaveAndRestore signatureRestore(m_currentFunctionType, std::nullopt); - - Type argumentsType = m_typeSystem.freshTypeVariable({}); - Type returnType = m_typeSystem.freshTypeVariable({}); - Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(argumentsType, returnType); - - m_currentFunctionType = functionType; - functionAnnotation.type = functionType; - + m_currentFunctionType = annotation(_functionDefinition).type; + auto [argumentsType, returnType] = TypeSystemHelpers{m_typeSystem}.destFunctionType(m_currentFunctionType.value()); _functionDefinition.parameterList().accept(*this); unify(argumentsType, typeAnnotation(_functionDefinition.parameterList()), _functionDefinition.parameterList().location()); @@ -180,21 +129,27 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) _typeClassDefinition.typeVariable().accept(*this); } - std::map functionTypes; - solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); + Type typeVar = m_typeSystem.typeClassInfo(typeClass).typeVariable; auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; + std::map const& functionTypes = m_analysis.annotation().typeClassFunctions.at(typeClass); + for (auto subNode: _typeClassDefinition.subNodes()) { - subNode->accept(*this); auto const* functionDefinition = dynamic_cast(subNode.get()); solAssert(functionDefinition); - auto functionType = typeAnnotation(*functionDefinition); - if (!functionTypes.emplace(functionDefinition->name(), functionType).second) - m_errorReporter.fatalTypeError(3195_error, functionDefinition->location(), "Function in type class declared multiple times."); + + // For type class members the type is assigned by TypeClassMemberRegistration. + // Fill in the `type` annotation to avoid `visit(FunctionDefinition)` giving it a fresh one. + Type functionType = functionTypes.at(functionDefinition->name()); + solAssert(!annotation(*functionDefinition).type.has_value()); + annotation(*functionDefinition).type = functionType; + + subNode->accept(*this); + auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); if (typeVars.size() != 1) m_errorReporter.fatalTypeError(8379_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); @@ -202,8 +157,6 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; } - annotation().typeClassFunctions[typeClass] = std::move(functionTypes); - for (auto [functionName, functionType]: functionTypes) { TypeEnvironmentHelpers helper{*m_env}; @@ -270,15 +223,16 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation) { auto& operationAnnotation = annotation(_binaryOperation); solAssert(!operationAnnotation.type); + auto const& memberRegistrationAnnotation = m_analysis.annotation(); TypeSystemHelpers helper{m_typeSystem}; switch (m_expressionContext) { case ExpressionContext::Term: - if (auto* operatorInfo = util::valueOrNullptr(annotation().operators, _binaryOperation.getOperator())) + if (auto* operatorInfo = util::valueOrNullptr(memberRegistrationAnnotation.operators, _binaryOperation.getOperator())) { auto [typeClass, functionName] = *operatorInfo; // TODO: error robustness? - Type functionType = m_env->fresh(annotation().typeClassFunctions.at(typeClass).at(functionName)); + Type functionType = m_env->fresh(memberRegistrationAnnotation.typeClassFunctions.at(typeClass).at(functionName)); _binaryOperation.leftExpression().accept(*this); _binaryOperation.rightExpression().accept(*this); @@ -603,8 +557,6 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) [&](ASTPointer _typeClassName) -> std::optional { if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) { - // visiting the type class will re-visit this instantiation - typeClassDefinition->accept(*this); // TODO: more error handling? Should be covered by the visit above. solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); return m_analysis.annotation(*typeClassDefinition).typeClass.value(); @@ -672,7 +624,8 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) if (auto error = m_typeSystem.instantiateClass(type, arity)) m_errorReporter.typeError(5094_error, _typeClassInstantiation.location(), *error); - auto const& classFunctions = annotation().typeClassFunctions.at(*typeClass); + auto const& memberRegistrationAnnotation = m_analysis.annotation(); + auto const& classFunctions = memberRegistrationAnnotation.typeClassFunctions.at(*typeClass); TypeEnvironment newEnv = m_env->clone(); if (!newEnv.unify(m_typeSystem.typeClassVariable(*typeClass), type).empty()) diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index 7f873e11f..929dcfe32 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -45,8 +45,6 @@ public: }; struct GlobalAnnotation { - std::map> typeClassFunctions; - std::map> operators; std::map> members; }; bool visit(Block const&) override { return true; } diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index a579a8369..464c98b82 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -199,7 +200,7 @@ void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation) Type rightType = type(_binaryOperation.rightExpression()); Type resultType = type(_binaryOperation); Type functionType = helper.functionType(helper.tupleType({leftType, rightType}), resultType); - auto [typeClass, memberName] = m_context.analysis.annotation().operators.at(_binaryOperation.getOperator()); + auto [typeClass, memberName] = m_context.analysis.annotation().operators.at(_binaryOperation.getOperator()); auto const& functionDefinition = resolveTypeClassFunction(typeClass, memberName, functionType); // TODO: deduplicate with FunctionCall // TODO: get around resolveRecursive by passing the environment further down? @@ -229,7 +230,7 @@ FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(Typ TypeSystemHelpers helper{m_context.analysis.typeSystem()}; TypeEnvironment env = m_context.env->clone(); - Type genericFunctionType = env.fresh(m_context.analysis.annotation().typeClassFunctions.at(_class).at(_name)); + Type genericFunctionType = env.fresh(m_context.analysis.annotation().typeClassFunctions.at(_class).at(_name)); auto typeVars = TypeEnvironmentHelpers{env}.typeVars(genericFunctionType); solAssert(typeVars.size() == 1); solAssert(env.unify(genericFunctionType, _type).empty()); From 4028125234ae5a39f9df89141f0e439aa8314ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 28 Sep 2023 20:19:53 +0200 Subject: [PATCH 4/5] fixup! Extract type class member registration into a separate experimental analysis pass --- libsolidity/experimental/analysis/TypeInference.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index fded26b75..1d8dd1d03 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -557,6 +557,9 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) [&](ASTPointer _typeClassName) -> std::optional { if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) { + // visiting the type class will re-visit this instantiation + typeClassDefinition->accept(*this); + // TODO: more error handling? Should be covered by the visit above. solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); return m_analysis.annotation(*typeClassDefinition).typeClass.value(); From a47aa09bb458647e6bab0e73bb1e8a1891362ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 28 Sep 2023 20:17:13 +0200 Subject: [PATCH 5/5] [TMP] Extra tests --- .../experimental/algebraicTypes.sol | 49 +++++++++++++++++ .../class_and_instantiation_order.sol | 52 +++++++++++++++++++ .../infinite_recursion_two_functions.sol | 30 +++++++++++ .../syntaxTests/experimental/sorts.sol | 38 ++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 test/libsolidity/syntaxTests/experimental/algebraicTypes.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/class_and_instantiation_order.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/infinite_recursion_two_functions.sol create mode 100644 test/libsolidity/syntaxTests/experimental/sorts.sol diff --git a/test/libsolidity/syntaxTests/experimental/algebraicTypes.sol b/test/libsolidity/syntaxTests/experimental/algebraicTypes.sol new file mode 100644 index 000000000..6c6e8d752 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/algebraicTypes.sol @@ -0,0 +1,49 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type bool = __builtin("bool"); +type unit = __builtin("unit"); + +type wordOrBool = word | bool; +type wordAndBool = (word, bool); + +function g() -> () {} + +function f() -> () { + let ptr1: unit -> unit = g; + let ptr2: () -> () = g; + + // FIXME: Does not unify: + //let ptr3: () -> () | () -> () = g; + //let ptr4: unit -> unit | () -> () = g; + //let ptr5: word | () -> () = g; +} +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: word +// Info 4164: (62-92): Inferred type: bool +// Info 4164: (93-123): Inferred type: () +// Info 4164: (125-155): Inferred type: wordOrBool +// Info 4164: (143-154): Inferred type: sum(word, bool) +// Info 4164: (143-147): Inferred type: word +// Info 4164: (150-154): Inferred type: bool +// Info 4164: (156-188): Inferred type: wordAndBool +// Info 4164: (175-187): Inferred type: (word, bool) +// Info 4164: (176-180): Inferred type: word +// Info 4164: (182-186): Inferred type: bool +// Info 4164: (190-211): Inferred type: () -> () +// Info 4164: (200-202): Inferred type: () +// Info 4164: (206-208): Inferred type: () +// Info 4164: (213-449): Inferred type: () -> () +// Info 4164: (223-225): Inferred type: () +// Info 4164: (229-231): Inferred type: () +// Info 4164: (242-260): Inferred type: () -> () +// Info 4164: (248-260): Inferred type: () -> () +// Info 4164: (248-252): Inferred type: () +// Info 4164: (256-260): Inferred type: () +// Info 4164: (263-264): Inferred type: () -> () +// Info 4164: (274-288): Inferred type: () -> () +// Info 4164: (280-288): Inferred type: () -> () +// Info 4164: (280-282): Inferred type: () +// Info 4164: (286-288): Inferred type: () +// Info 4164: (291-292): Inferred type: () -> () diff --git a/test/libsolidity/syntaxTests/experimental/inference/class_and_instantiation_order.sol b/test/libsolidity/syntaxTests/experimental/inference/class_and_instantiation_order.sol new file mode 100644 index 000000000..5a9c4740a --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/class_and_instantiation_order.sol @@ -0,0 +1,52 @@ +pragma experimental solidity; + +type word = __builtin("word"); + +type T = word; + +class Self: C { + function f(self: Self) -> word; +} + +instantiation T: C { + function f(self: T) -> word {} +} + +instantiation T: D { + function f(self: T) -> word {} +} + +class Self: D { + function f(self: Self) -> word; +} +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: word +// Info 4164: (63-77): Inferred type: T:(type, C, D) +// Info 4164: (72-76): Inferred type: word +// Info 4164: (79-132): Inferred type: C +// Info 4164: (85-89): Inferred type: 'bb:(type, C) +// Info 4164: (99-130): Inferred type: 'bb:(type, C) -> word +// Info 4164: (109-121): Inferred type: 'bb:(type, C) +// Info 4164: (110-120): Inferred type: 'bb:(type, C) +// Info 4164: (116-120): Inferred type: 'bb:(type, C) +// Info 4164: (125-129): Inferred type: word +// Info 4164: (134-191): Inferred type: void +// Info 4164: (159-189): Inferred type: T -> word +// Info 4164: (169-178): Inferred type: T +// Info 4164: (170-177): Inferred type: T +// Info 4164: (176-177): Inferred type: T +// Info 4164: (182-186): Inferred type: word +// Info 4164: (193-250): Inferred type: void +// Info 4164: (218-248): Inferred type: T -> word +// Info 4164: (228-237): Inferred type: T +// Info 4164: (229-236): Inferred type: T +// Info 4164: (235-236): Inferred type: T +// Info 4164: (241-245): Inferred type: word +// Info 4164: (252-305): Inferred type: D +// Info 4164: (258-262): Inferred type: 'bi:(type, D) +// Info 4164: (272-303): Inferred type: 'bi:(type, D) -> word +// Info 4164: (282-294): Inferred type: 'bi:(type, D) +// Info 4164: (283-293): Inferred type: 'bi:(type, D) +// Info 4164: (289-293): Inferred type: 'bi:(type, D) +// Info 4164: (298-302): Inferred type: word diff --git a/test/libsolidity/syntaxTests/experimental/inference/infinite_recursion_two_functions.sol b/test/libsolidity/syntaxTests/experimental/inference/infinite_recursion_two_functions.sol new file mode 100644 index 000000000..efcac2707 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/infinite_recursion_two_functions.sol @@ -0,0 +1,30 @@ +pragma experimental solidity; + +type word = __builtin("word"); + +function f(x: word) -> word { + g(x); +} + +function g(x: word) -> word { + f(x); +} +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: word +// Info 4164: (63-104): Inferred type: word -> word +// Info 4164: (73-82): Inferred type: word +// Info 4164: (74-81): Inferred type: word +// Info 4164: (77-81): Inferred type: word +// Info 4164: (86-90): Inferred type: word +// Info 4164: (97-101): Inferred type: word +// Info 4164: (97-98): Inferred type: word -> word +// Info 4164: (99-100): Inferred type: word +// Info 4164: (106-147): Inferred type: word -> word +// Info 4164: (116-125): Inferred type: word +// Info 4164: (117-124): Inferred type: word +// Info 4164: (120-124): Inferred type: word +// Info 4164: (129-133): Inferred type: word +// Info 4164: (140-144): Inferred type: word +// Info 4164: (140-141): Inferred type: word -> word +// Info 4164: (142-143): Inferred type: word diff --git a/test/libsolidity/syntaxTests/experimental/sorts.sol b/test/libsolidity/syntaxTests/experimental/sorts.sol new file mode 100644 index 000000000..f3eccd525 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/sorts.sol @@ -0,0 +1,38 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type uint = word; + +class Self: Number {} + +instantiation uint: Number {} + +function f(a: T: Number) {} + +function test() { + let x: uint: Number; + f(x); +} +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: word +// Info 4164: (62-79): Inferred type: uint:(type, Number) +// Info 4164: (74-78): Inferred type: word +// Info 4164: (81-102): Inferred type: Number +// Info 4164: (87-91): Inferred type: 'v:(type, Number) +// Info 4164: (104-133): Inferred type: void +// Info 4164: (135-162): Inferred type: 'z:(type, Number) -> () +// Info 4164: (145-159): Inferred type: 'z:(type, Number) +// Info 4164: (146-158): Inferred type: 'z:(type, Number) +// Info 4164: (149-158): Inferred type: 'z:(type, Number) +// Info 4164: (149-150): Inferred type: 'z:(type, Number) +// Info 4164: (152-158): Inferred type: 'z:(type, Number):(type, Number) +// Info 4164: (164-218): Inferred type: () -> () +// Info 4164: (177-179): Inferred type: () +// Info 4164: (190-205): Inferred type: uint +// Info 4164: (193-205): Inferred type: uint +// Info 4164: (193-197): Inferred type: uint +// Info 4164: (199-205): Inferred type: uint:(type, Number) +// Info 4164: (211-215): Inferred type: () +// Info 4164: (211-212): Inferred type: uint -> () +// Info 4164: (213-214): Inferred type: uint