This commit is contained in:
Daniel Kirchner 2023-06-20 04:45:30 +02:00
parent bd0e0fcdbe
commit 3249979969
5 changed files with 100 additions and 151 deletions

View File

@ -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<TypeEnvironment>(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<std::vector<Type>>);
};
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<std::vector<Type>>);
};
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<Type> 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))));
}

View File

@ -61,7 +61,6 @@ private:
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
TypeSystem m_typeSystem;
std::unique_ptr<TypeEnvironment> 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<std::unique_ptr<TypeAnnotation>> m_typeAnnotations;
};

View File

@ -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<experimental::Type> 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::UnificationFailure> TypeSystem::unify(Type _a, Type _b)
{
vector<UnificationFailure> 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> TypeEnvironment::fresh() const
{
auto newEnv = make_unique<TypeEnvironment>(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<GenericTypeVariable>(&result))
while(auto const* var = std::get_if<TypeVariable>(&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<vector<Type>>
};
},
[&](TypeVariable const& _var) {
if (_generalize || _var.generic())
return freshTypeVariable(true);
else
return _type;
},
}, resolve(_type));
}
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _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<TypeExpression::Constructor, vector<experimental::Type>> TypeSystemHelpers::destTypeExpression(Type _functionType) const
{
using ResultType = tuple<TypeExpression::Constructor, vector<Type>>;
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<vector<Type>>
};
},
[&](FreeTypeVariable const&) {
return _type;
},
[&](GenericTypeVariable const&) {
return freshTypeVariable();
},
}, resolve(_type));
}
tuple<AtomicType::Constructor, vector<experimental::Type>> TypeSystemHelpers::destAtomicType(Type _functionType) const
{
using ResultType = tuple<AtomicType::Constructor, vector<Type>>;
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<AtomicType::Constructor, vector<experimental::Type>> TypeSystemHelpers::de
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
{
auto [constructor, arguments] = destAtomicType(_functionType);
auto [constructor, arguments] = destTypeExpression(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor);
solAssert(builtinType && *builtinType == BuiltinType::Function);
solAssert(arguments.size() == 2);

View File

@ -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<AtomicType, GenericTypeVariable, FreeTypeVariable>;
using Type = std::variant<TypeExpression, TypeVariable>;
enum class BuiltinType
{
@ -50,7 +49,7 @@ enum class BuiltinType
Word
};
struct AtomicType
struct TypeExpression
{
using Constructor = std::variant<BuiltinType, Declaration const*>;
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<TypeEnvironment> fresh() const;
void assignType(Declaration const* _declaration, Type _typeAssignment);
std::optional<Type> lookup(Declaration const* _declaration);
private:
TypeSystem& m_parent;
std::map<Declaration const*, Type> 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<UnificationFailure> unify(Type _a, Type _b);
private:
void instantiate(TypeVariable _variable, Type _type);
void validate(TypeVariable _variable) const;
std::vector<std::optional<Type>> m_freeTypes;
struct TypeConstructorInfo
{
@ -131,7 +107,7 @@ struct TypeSystemHelpers
TypeSystem& typeSystem;
Type tupleType(std::vector<Type> _elements) const;
Type functionType(Type _argType, Type _resultType) const;
std::tuple<AtomicType::Constructor, std::vector<Type>> destAtomicType(Type _functionType) const;
std::tuple<TypeExpression::Constructor, std::vector<Type>> destTypeExpression(Type _functionType) const;
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
};

View File

@ -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();