diff --git a/libsolidity/analysis/experimental/TypeInference.cpp b/libsolidity/analysis/experimental/TypeInference.cpp index 39d670f1e..5b2ef1a77 100644 --- a/libsolidity/analysis/experimental/TypeInference.cpp +++ b/libsolidity/analysis/experimental/TypeInference.cpp @@ -46,7 +46,6 @@ m_errorReporter(_analysis.errorReporter()) m_typeSystem.declareBuiltinType(type, name, arity); m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {}); m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {}); - m_env = make_unique(m_typeSystem); m_typeAnnotations.resize(_analysis.maxAstId()); } @@ -63,32 +62,31 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) if (functionAnnotation.type) return false; - Type functionType; - { - _functionDefinition.parameterList().accept(*this); - if (_functionDefinition.returnParameterList()) - _functionDefinition.returnParameterList()->accept(*this); + _functionDefinition.parameterList().accept(*this); + if (_functionDefinition.returnParameterList()) + _functionDefinition.returnParameterList()->accept(*this); - _functionDefinition.body().accept(*this); + _functionDefinition.body().accept(*this); - auto typeFromParameterList = [&](ParameterList const* _list) { - if (!_list) - return m_typeSystem.builtinType(BuiltinType::Unit, {}); - return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::view::transform([&](auto _param) { - auto& argAnnotation = annotation(*_param); - solAssert(argAnnotation.type); - return *argAnnotation.type; - }) | ranges::to>); - }; + auto typeFromParameterList = [&](ParameterList const* _list) { + if (!_list) + return m_typeSystem.builtinType(BuiltinType::Unit, {}); + return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::view::transform([&](auto _param) { + auto& argAnnotation = annotation(*_param); + solAssert(argAnnotation.type); + return *argAnnotation.type; + }) | ranges::to>); + }; - Type argType = typeFromParameterList(&_functionDefinition.parameterList()); - Type resultType = typeFromParameterList(_functionDefinition.returnParameterList().get()); + Type argType = typeFromParameterList(&_functionDefinition.parameterList()); + Type resultType = typeFromParameterList(_functionDefinition.returnParameterList().get()); - functionType = m_typeSystem.builtinType(BuiltinType::Function, {argType, resultType}); - } - functionAnnotation.type = functionType; + functionAnnotation.type = m_typeSystem.fresh( + TypeSystemHelpers{m_typeSystem}.functionType(argType, resultType), + true + ); - m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(m_typeSystem.resolve(functionType))); + m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(*functionAnnotation.type)); return false; } @@ -135,11 +133,15 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) } else m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported."); - // TODO: free type? - return m_typeSystem.freshTypeVariable(); + 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))); +} bool TypeInference::visit(InlineAssembly const& _inlineAssembly) { // External references have already been resolved in a prior stage and stored in the annotation. @@ -166,7 +168,7 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly) auto& declarationAnnotation = annotation(*declaration); solAssert(declarationAnnotation.type); - m_typeSystem.unify(*declarationAnnotation.type, m_wordType); + unify(*declarationAnnotation.type, m_wordType); identifierInfo.valueSize = 1; return true; }; @@ -192,7 +194,7 @@ bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) if (_variableDeclaration.hasTypeName()) return fromTypeName(_variableDeclaration.typeName()); else - return m_typeSystem.freshTypeVariable(); + return m_typeSystem.freshTypeVariable(false); }(); return false; } @@ -211,7 +213,7 @@ void TypeInference::endVisit(Assignment const& _assignment) solAssert(lhsAnnotation.type); auto& rhsAnnotation = annotation(_assignment.rightHandSide()); solAssert(rhsAnnotation.type); - m_typeSystem.unify(*lhsAnnotation.type, *rhsAnnotation.type); + unify(*lhsAnnotation.type, *rhsAnnotation.type); assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type); } @@ -250,7 +252,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall) auto& expressionAnnotation = annotation(_functionCall.expression()); solAssert(expressionAnnotation.type); - Type functionType = m_typeSystem.fresh(*expressionAnnotation.type); + Type functionType = m_typeSystem.fresh(*expressionAnnotation.type, false); std::vector argTypes; for(auto arg: _functionCall.arguments()) @@ -260,8 +262,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()); - m_typeSystem.unify(genericFunctionType, functionType); + 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)))); } diff --git a/libsolidity/analysis/experimental/TypeInference.h b/libsolidity/analysis/experimental/TypeInference.h index 18ab717cf..635d3267c 100644 --- a/libsolidity/analysis/experimental/TypeInference.h +++ b/libsolidity/analysis/experimental/TypeInference.h @@ -61,7 +61,6 @@ private: Analysis& m_analysis; langutil::ErrorReporter& m_errorReporter; TypeSystem m_typeSystem; - std::unique_ptr m_env; Type m_voidType; Type m_wordType; @@ -72,6 +71,8 @@ private: TypeAnnotation& annotation(ASTNode const& _node); + void unify(Type _a, Type _b); + std::vector> m_typeAnnotations; }; diff --git a/libsolidity/ast/experimental/TypeSystem.cpp b/libsolidity/ast/experimental/TypeSystem.cpp index 00beee261..1ebe333ec 100644 --- a/libsolidity/ast/experimental/TypeSystem.cpp +++ b/libsolidity/ast/experimental/TypeSystem.cpp @@ -38,7 +38,7 @@ using namespace solidity::frontend::experimental; std::string TypeSystem::typeToString(Type const& _type) const { return std::visit(util::GenericVisitor{ - [&](AtomicType const& _type) { + [&](TypeExpression const& _type) { std::stringstream stream; if (!_type.arguments.empty()) { @@ -58,89 +58,56 @@ std::string TypeSystem::typeToString(Type const& _type) const }, _type.constructor); return stream.str(); }, - [](FreeTypeVariable const& _type) { - return fmt::format("free[{}]", _type.index()); - }, - [](GenericTypeVariable const& _type) { + [](TypeVariable const& _type) { return fmt::format("var[{}]", _type.index()); }, }, resolve(_type)); } -void TypeEnvironment::assignType(Declaration const* _declaration, Type _typeAssignment) -{ - auto&& [type, newlyInserted] = m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment))); - if (!newlyInserted) - { - m_parent.unify(type->second, _typeAssignment); - } -} -std::optional TypeEnvironment::lookup(Declaration const* _declaration) -{ - if (m_types.count(_declaration)) - return m_types[_declaration]; - return std::nullopt; -} - - -void TypeSystem::unify(Type _a, Type _b) +vector TypeSystem::unify(Type _a, Type _b) { + vector failures; auto unificationFailure = [&]() { - solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b))); + failures.emplace_back(UnificationFailure{_a, _b}); }; _a = resolve(_a); _b = resolve(_b); std::visit(util::GenericVisitor{ - [&](GenericTypeVariable _left, GenericTypeVariable _right) { + [&](TypeVariable _left, TypeVariable _right) { validate(_left); validate(_right); if (_left.index() != _right.index()) instantiate(_left, _right); }, - [&](GenericTypeVariable _var, auto) { + [&](TypeVariable _var, auto) { instantiate(_var, _b); }, - [&](auto, GenericTypeVariable _var) { + [&](auto, TypeVariable _var) { instantiate(_var, _a); }, - [&](AtomicType _atomicLeft, AtomicType _atomicRight) { - if(_atomicLeft.constructor != _atomicRight.constructor) - unificationFailure(); - if (_atomicLeft.arguments.size() != _atomicRight.arguments.size()) - unificationFailure(); - for (auto&& [left, right]: ranges::zip_view(_atomicLeft.arguments, _atomicRight.arguments)) - unify(left, right); + [&](TypeExpression _left, TypeExpression _right) { + if(_left.constructor != _right.constructor) + return unificationFailure(); + if (_left.arguments.size() != _right.arguments.size()) + return unificationFailure(); + for (auto&& [left, right]: ranges::zip_view(_left.arguments, _right.arguments)) + failures += unify(left, right); }, [&](auto, auto) { unificationFailure(); } }, _a, _b); + return failures; } -unique_ptr TypeEnvironment::fresh() const -{ - auto newEnv = make_unique(m_parent); - for(auto [decl,type]: m_types) - newEnv->m_types.emplace(decl, type); - return newEnv; - -} - -experimental::Type TypeSystem::freshTypeVariable() +experimental::Type TypeSystem::freshTypeVariable(bool _generic) { uint64_t index = m_typeVariables.size(); m_typeVariables.emplace_back(std::nullopt); - return GenericTypeVariable(*this, index); + return TypeVariable(*this, index, _generic); } -experimental::Type TypeSystem::freshFreeType() -{ - uint64_t index = m_freeTypes.size(); - m_freeTypes.emplace_back(std::nullopt); - return FreeTypeVariable(*this, index); -} - -void TypeSystem::instantiate(GenericTypeVariable _variable, Type _type) +void TypeSystem::instantiate(TypeVariable _variable, Type _type) { validate(_variable); solAssert(!m_typeVariables.at(_variable.index()).has_value()); @@ -151,7 +118,7 @@ void TypeSystem::instantiate(GenericTypeVariable _variable, Type _type) experimental::Type TypeSystem::resolve(Type _type) const { Type result = _type; - while(auto const* var = std::get_if(&result)) + while(auto const* var = std::get_if(&result)) if (auto value = m_typeVariables.at(var->index())) result = *value; else @@ -172,15 +139,35 @@ experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector { auto const& info = m_builtinTypes.at(_builtinType); solAssert(info.arity == _arguments.size(), "Invalid arity."); - return AtomicType{_builtinType, _arguments}; + return TypeExpression{_builtinType, _arguments}; } -void TypeSystem::validate(GenericTypeVariable _variable) const +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) +{ + return std::visit(util::GenericVisitor{ + [&](TypeExpression const& _type) -> Type { + return TypeExpression{ + _type.constructor, + _type.arguments | ranges::view::transform([&](Type _argType) { + return fresh(_argType, _generalize); + }) | ranges::to> + }; + }, + [&](TypeVariable const& _var) { + if (_generalize || _var.generic()) + return freshTypeVariable(true); + else + return _type; + }, + }, resolve(_type)); +} + experimental::Type TypeSystemHelpers::tupleType(vector _elements) const { if (_elements.empty()) @@ -198,31 +185,11 @@ experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType}); } -experimental::Type TypeSystem::fresh(Type _type) +tuple> TypeSystemHelpers::destTypeExpression(Type _functionType) const { + using ResultType = tuple>; return std::visit(util::GenericVisitor{ - [&](AtomicType const& _type) -> Type { - return AtomicType{ - _type.constructor, - _type.arguments | ranges::view::transform([&](Type _argType) { - return fresh(_argType); - }) | ranges::to> - }; - }, - [&](FreeTypeVariable const&) { - return _type; - }, - [&](GenericTypeVariable const&) { - return freshTypeVariable(); - }, - }, resolve(_type)); -} - -tuple> TypeSystemHelpers::destAtomicType(Type _functionType) const -{ - using ResultType = tuple>; - return std::visit(util::GenericVisitor{ - [&](AtomicType const& _type) -> ResultType { + [&](TypeExpression const& _type) -> ResultType { return std::make_tuple(_type.constructor, _type.arguments); }, [](auto) -> ResultType { @@ -234,7 +201,7 @@ tuple> TypeSystemHelpers::de tuple TypeSystemHelpers::destFunctionType(Type _functionType) const { - auto [constructor, arguments] = destAtomicType(_functionType); + auto [constructor, arguments] = destTypeExpression(_functionType); auto const* builtinType = get_if(&constructor); solAssert(builtinType && *builtinType == BuiltinType::Function); solAssert(arguments.size() == 2); diff --git a/libsolidity/ast/experimental/TypeSystem.h b/libsolidity/ast/experimental/TypeSystem.h index 2f5aeed13..b61ec18ab 100644 --- a/libsolidity/ast/experimental/TypeSystem.h +++ b/libsolidity/ast/experimental/TypeSystem.h @@ -35,11 +35,10 @@ namespace solidity::frontend::experimental class TypeSystem; class TypeEnvironment; -struct AtomicType; -struct GenericTypeVariable; -struct FreeTypeVariable; +struct TypeExpression; +struct TypeVariable; -using Type = std::variant; +using Type = std::variant; enum class BuiltinType { @@ -50,7 +49,7 @@ enum class BuiltinType Word }; -struct AtomicType +struct TypeExpression { using Constructor = std::variant; Constructor constructor; @@ -58,43 +57,19 @@ struct AtomicType }; -struct FreeTypeVariable +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; - FreeTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {} + bool m_generic = false; + TypeVariable(TypeSystem const& _parent, uint64_t _index, bool _generic): m_parent(&_parent), m_index(_index), m_generic(_generic) {} }; -struct GenericTypeVariable -{ - TypeSystem const& parent() const { return *m_parent; } - uint64_t index() const { return m_index; } -private: - friend class TypeSystem; - TypeSystem const* m_parent = nullptr; - uint64_t m_index = 0; - GenericTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {} -}; - -class TypeEnvironment -{ -public: - TypeEnvironment(TypeSystem& _parent): m_parent(_parent) {} - TypeEnvironment(TypeEnvironment const& _env) = delete; - TypeEnvironment& operator=(TypeEnvironment const& _env) = delete; - std::unique_ptr fresh() const; - void assignType(Declaration const* _declaration, Type _typeAssignment); - std::optional lookup(Declaration const* _declaration); -private: - TypeSystem& m_parent; - std::map m_types; -}; - - class TypeSystem { public: @@ -108,14 +83,15 @@ public: return m_builtinTypes.at(_builtinType).name; } Type freshFreeType(); - void instantiate(GenericTypeVariable _variable, Type _type); - void validate(GenericTypeVariable _variable) const; Type resolve(Type _type) const; std::string typeToString(Type const& _type) const; - Type freshTypeVariable(); - Type fresh(Type _type); - void unify(Type _a, Type _b); + Type freshTypeVariable(bool _generic); + Type fresh(Type _type, bool _generalize); + struct UnificationFailure { Type a; Type b; }; + [[nodiscard]] std::vector unify(Type _a, Type _b); private: + void instantiate(TypeVariable _variable, Type _type); + void validate(TypeVariable _variable) const; std::vector> m_freeTypes; struct TypeConstructorInfo { @@ -131,7 +107,7 @@ struct TypeSystemHelpers TypeSystem& typeSystem; Type tupleType(std::vector _elements) const; Type functionType(Type _argType, Type _resultType) const; - std::tuple> destAtomicType(Type _functionType) const; + std::tuple> destTypeExpression(Type _functionType) const; std::tuple destFunctionType(Type _functionType) const; }; diff --git a/libsolidity/codegen/experimental/IRGenerator.cpp b/libsolidity/codegen/experimental/IRGenerator.cpp index 978e1e9ad..482fcda64 100644 --- a/libsolidity/codegen/experimental/IRGenerator.cpp +++ b/libsolidity/codegen/experimental/IRGenerator.cpp @@ -85,8 +85,11 @@ string IRGenerator::generate(ContractDefinition const& _contract) { FunctionDefinition const* function = m_context.functionQueue.front(); m_context.functionQueue.pop_front(); - m_context.generatedFunctions.insert(function); - code << generate(*function); + if (!m_context.generatedFunctions.count(function)) + { + m_context.generatedFunctions.insert(function); + code << generate(*function); + } } return code.str();