From 9f7764c21597ce663b1546ce76fdba1795f8ce41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 5 Sep 2023 19:49:59 +0200 Subject: [PATCH 1/3] Semantic test for type classes --- .../semanticTests/experimental/type_class.sol | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 test/libsolidity/semanticTests/experimental/type_class.sol diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol new file mode 100644 index 000000000..14eb59716 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -0,0 +1,65 @@ +pragma experimental solidity; + +type Cat = word; +type Dog = word; + +class A: Animal { + function new() -> A; + function alive(a: A) -> bool; +} + +instantiation Cat: Animal { + function new() -> Cat { + let c; + return c; + } + + function alive(c: Cat) -> bool { + // TODO: Boolean literals or operators not implemented. + let w; + assembly { + w := 1 + } + return bool.abs(w); + } +} + +instantiation Dog: Animal { + function new() -> Dog { + let d: Dog; + return d; + } + + function alive(d: Dog) -> bool { + let b: bool; + return b; + } +} + +contract C { + fallback() external { + let boolResult1: bool; + let boolResult2: bool; + + let c: Cat = Animal.new(); + boolResult1 = Animal.alive(c); + + let d: Dog = Animal.new(); + boolResult2 = Animal.alive(d); + + let wordResult1 = bool.rep(boolResult1); + let wordResult2 = bool.rep(boolResult2); + assembly { + mstore(0, wordResult1) + mstore(32, wordResult2) + return(0, 64) + } + } +} + +// ==== +// EVMVersion: >=constantinople +// ==== +// compileViaYul: true +// ---- +// () -> 1, 0 From 086c912c65223f020f8acd23fc05cb94177d08a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 11 Sep 2023 14:39:35 +0200 Subject: [PATCH 2/3] TypeSystem: make typeClassInfo() public --- libsolidity/experimental/ast/TypeSystem.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h index 17b427e85..00f710a76 100644 --- a/libsolidity/experimental/ast/TypeSystem.h +++ b/libsolidity/experimental/ast/TypeSystem.h @@ -136,12 +136,14 @@ public: { return m_typeClasses.at(_class.m_index).typeVariable; } -private: - friend class TypeEnvironment; + TypeClassInfo const& typeClassInfo(TypeClass _class) const { return m_typeClasses.at(_class.m_index); } + +private: + friend class TypeEnvironment; size_t m_numTypeVariables = 0; std::map m_primitiveTypeConstructors; std::map m_primitiveTypeClasses; From da83b35a711f90dabc8f0a22242885613f603a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 7 Sep 2023 12:14:28 +0200 Subject: [PATCH 3/3] Separate experimental analysis pass for type class registration --- libsolidity/CMakeLists.txt | 2 + .../experimental/analysis/Analysis.cpp | 30 ++++++++- .../analysis/TypeClassRegistration.cpp | 65 +++++++++++++++++++ .../analysis/TypeClassRegistration.h | 58 +++++++++++++++++ .../experimental/analysis/TypeInference.cpp | 29 ++++----- .../experimental/analysis/TypeInference.h | 2 - .../codegen/IRGeneratorForStatements.cpp | 7 +- .../semanticTests/experimental/type_class.sol | 10 +-- 8 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 46d43291e..6ad451ed1 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/TypeClassRegistration.cpp + experimental/analysis/TypeClassRegistration.h experimental/analysis/TypeInference.cpp experimental/analysis/TypeInference.h experimental/analysis/TypeRegistration.cpp diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 56d4d36f9..5e638f523 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -27,16 +28,43 @@ 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 { + TypeClassRegistration::Annotation typeClassRegistrationAnnotation; TypeRegistration::Annotation typeRegistrationAnnotation; TypeInference::Annotation typeInferenceAnnotation; }; struct Analysis::GlobalAnnotationContainer { + TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation; TypeRegistration::GlobalAnnotation typeRegistrationAnnotation; TypeInference::GlobalAnnotation typeInferenceAnnotation; }; +template<> +TypeClassRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + + +template<> +TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + template<> TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) { @@ -121,7 +149,7 @@ std::tuple...> makeIndexTuple(std::index_sequ bool Analysis::check(std::vector> const& _sourceUnits) { - using AnalysisSteps = std::tuple; + using AnalysisSteps = std::tuple; return std::apply([&](auto... _indexTuple) { return ([&](auto&& _step) { diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.cpp b/libsolidity/experimental/analysis/TypeClassRegistration.cpp new file mode 100644 index 000000000..10d3d6c3f --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.cpp @@ -0,0 +1,65 @@ +/* + 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::experimental; +using namespace solidity::langutil; + +TypeClassRegistration::TypeClassRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ +} + +bool TypeClassRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeClassRegistration::visit(TypeClassDefinition const& _typeClassDefinition) +{ + Type typeVar = m_typeSystem.freshTypeVariable({}); + + std::variant typeClassOrError = m_typeSystem.declareTypeClass( + typeVar, + _typeClassDefinition.name(), + &_typeClassDefinition + ); + + m_analysis.annotation(_typeClassDefinition).typeClass = std::visit( + util::GenericVisitor{ + [](TypeClass _class) -> TypeClass { return _class; }, + [&](std::string _error) -> TypeClass { + m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); + util::unreachable(); + } + }, + typeClassOrError + ); + + return true; +} diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.h b/libsolidity/experimental/analysis/TypeClassRegistration.h new file mode 100644 index 000000000..c0c1c3549 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.h @@ -0,0 +1,58 @@ +/* + 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 TypeClassRegistration: public ASTConstVisitor +{ +public: + struct Annotation + { + // Type classes. + std::optional typeClass; + }; + struct GlobalAnnotation + { + }; + + TypeClassRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + +private: + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + + 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 9d67027b7..d0679b840 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -18,6 +18,8 @@ #include + +#include #include #include #include @@ -205,7 +207,9 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) std::map functionTypes; - Type typeVar = m_typeSystem.freshTypeVariable({}); + 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)]; for (auto subNode: _typeClassDefinition.subNodes()) @@ -224,14 +228,6 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; } - TypeClass typeClass = std::visit(util::GenericVisitor{ - [](TypeClass _class) -> TypeClass { return _class; }, - [&](std::string _error) -> TypeClass { - m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); - util::unreachable(); - } - }, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition)); - typeClassDefinitionAnnotation.typeClass = typeClass; annotation().typeClassFunctions[typeClass] = std::move(functionTypes); for (auto [functionName, functionType]: functionTypes) @@ -574,12 +570,10 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut { ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; typeClassDefinition->accept(*this); - if (!annotation(*typeClassDefinition).typeClass) - { - m_errorReporter.typeError(2736_error, _location, "Unregistered type class."); - return m_typeSystem.freshTypeVariable({}); - } - return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClassDefinition).typeClass}}); + + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(*typeClassDefinition).typeClass.value(); + return m_typeSystem.freshTypeVariable(Sort{{typeClass}}); } else { @@ -680,9 +674,8 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) // visiting the type class will re-visit this instantiation typeClassDefinition->accept(*this); // TODO: more error handling? Should be covered by the visit above. - if (!annotation(*typeClassDefinition).typeClass) - m_errorReporter.typeError(8503_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); - return annotation(*typeClassDefinition).typeClass; + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + return m_analysis.annotation(*typeClassDefinition).typeClass.value(); } else { diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index e8e234862..31de43a7c 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -38,8 +38,6 @@ public: { /// Expressions, variable declarations, function declarations. std::optional type; - // Type classes. - std::optional typeClass; }; struct TypeMember { diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 1da96f147..589fe6787 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -271,11 +272,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(declaration); if (auto const* typeClassDefinition = dynamic_cast(declaration)) { - std::optional typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass; - solAssert(typeClass); + solAssert(m_context.analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass.value(); solAssert(m_expressionDeclaration.emplace( &_memberAccess, - &resolveTypeClassFunction(*typeClass, _memberAccess.memberName(), memberAccessType) + &resolveTypeClassFunction(typeClass, _memberAccess.memberName(), memberAccessType) ).second); } else if (dynamic_cast(declaration)) diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol index 14eb59716..b987b3f06 100644 --- a/test/libsolidity/semanticTests/experimental/type_class.sol +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -3,9 +3,9 @@ pragma experimental solidity; type Cat = word; type Dog = word; -class A: Animal { - function new() -> A; - function alive(a: A) -> bool; +class Self: Animal { + function new() -> Self; + function alive(self: Self) -> bool; } instantiation Cat: Animal { @@ -14,7 +14,7 @@ instantiation Cat: Animal { return c; } - function alive(c: Cat) -> bool { + function alive(self: Cat) -> bool { // TODO: Boolean literals or operators not implemented. let w; assembly { @@ -30,7 +30,7 @@ instantiation Dog: Animal { return d; } - function alive(d: Dog) -> bool { + function alive(self: Dog) -> bool { let b: bool; return b; }