mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
tmp
This commit is contained in:
parent
62a5291bbd
commit
0d1679cee0
@ -666,7 +666,7 @@ void Scanner::scanToken()
|
||||
case '.':
|
||||
// . Number
|
||||
advance();
|
||||
if (isDecimalDigit(m_char))
|
||||
if (m_kind != ScannerKind::ExperimentalSolidity && isDecimalDigit(m_char))
|
||||
token = scanNumber('.');
|
||||
else
|
||||
token = Token::Period;
|
||||
|
@ -339,7 +339,7 @@ namespace TokenTraits
|
||||
{
|
||||
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||
|
||||
tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let ||
|
||||
tok == Token::Return || (tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
|
||||
tok == Token::Return || tok == Token::Type || (tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
|
||||
}
|
||||
constexpr bool isExperimentalSolidityOnlyKeyword(Token tok)
|
||||
{
|
||||
|
@ -39,11 +39,13 @@ namespace solidity::frontend
|
||||
NameAndTypeResolver::NameAndTypeResolver(
|
||||
GlobalContext& _globalContext,
|
||||
langutil::EVMVersion _evmVersion,
|
||||
ErrorReporter& _errorReporter
|
||||
ErrorReporter& _errorReporter,
|
||||
bool _experimentalSolidity
|
||||
):
|
||||
m_evmVersion(_evmVersion),
|
||||
m_errorReporter(_errorReporter),
|
||||
m_globalContext(_globalContext)
|
||||
m_globalContext(_globalContext),
|
||||
m_experimentalSolidity(_experimentalSolidity)
|
||||
{
|
||||
m_scopes[nullptr] = make_shared<DeclarationContainer>();
|
||||
for (Declaration const* declaration: _globalContext.declarations())
|
||||
|
@ -59,7 +59,8 @@ public:
|
||||
NameAndTypeResolver(
|
||||
GlobalContext& _globalContext,
|
||||
langutil::EVMVersion _evmVersion,
|
||||
langutil::ErrorReporter& _errorReporter
|
||||
langutil::ErrorReporter& _errorReporter,
|
||||
bool _experimentalSolidity
|
||||
);
|
||||
/// Registers all declarations found in the AST node, usually a source unit.
|
||||
/// @returns false in case of error.
|
||||
@ -107,6 +108,7 @@ public:
|
||||
/// Sets the current scope.
|
||||
void setScope(ASTNode const* _node);
|
||||
|
||||
bool experimentalSolidity() const { return m_experimentalSolidity; }
|
||||
private:
|
||||
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||
@ -132,6 +134,7 @@ private:
|
||||
DeclarationContainer* m_currentScope = nullptr;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
GlobalContext& m_globalContext;
|
||||
bool m_experimentalSolidity = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -113,6 +113,21 @@ bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
|
||||
if (_varDecl.documentation())
|
||||
resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());
|
||||
|
||||
if (m_resolver.experimentalSolidity())
|
||||
{
|
||||
solAssert(!_varDecl.hasTypeName());
|
||||
if (_varDecl.typeExpression())
|
||||
{
|
||||
ScopedSaveAndRestore typeContext{m_typeContext, true};
|
||||
_varDecl.typeExpression()->accept(*this);
|
||||
}
|
||||
if (_varDecl.overrides())
|
||||
_varDecl.overrides()->accept(*this);
|
||||
if (_varDecl.value())
|
||||
_varDecl.value()->accept(*this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -121,6 +136,8 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||
if (declarations.empty())
|
||||
{
|
||||
if (m_resolver.experimentalSolidity() && m_typeContext)
|
||||
return false;
|
||||
string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
|
||||
string errorMessage = "Undeclared identifier.";
|
||||
if (!suggestions.empty())
|
||||
@ -233,6 +250,24 @@ bool ReferencesResolver::visit(Return const& _return)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(BinaryOperation const& _binaryOperation)
|
||||
{
|
||||
if (m_resolver.experimentalSolidity())
|
||||
{
|
||||
_binaryOperation.leftExpression().accept(*this);
|
||||
if (_binaryOperation.getOperator() == Token::Colon)
|
||||
{
|
||||
ScopedSaveAndRestore typeContext(m_typeContext, !m_typeContext);
|
||||
_binaryOperation.rightExpression().accept(*this);
|
||||
}
|
||||
else
|
||||
_binaryOperation.rightExpression().accept(*this);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return ASTConstVisitor::visit(_binaryOperation);
|
||||
}
|
||||
|
||||
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
|
||||
@ -253,6 +288,47 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
{
|
||||
solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");
|
||||
|
||||
if (m_resolver.experimentalSolidity())
|
||||
{
|
||||
vector<string> splitName;
|
||||
boost::split(splitName, _identifier.name.str(), boost::is_any_of("."));
|
||||
solAssert(!splitName.empty());
|
||||
if (splitName.size() > 2)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
0000_error,
|
||||
nativeLocationOf(_identifier),
|
||||
"Unsupported identifier in inline assembly."
|
||||
);
|
||||
return;
|
||||
}
|
||||
string name = splitName.front();
|
||||
auto declarations = m_resolver.nameFromCurrentScope(name);
|
||||
switch(declarations.size())
|
||||
{
|
||||
case 0:
|
||||
if (splitName.size() > 1)
|
||||
m_errorReporter.declarationError(
|
||||
0000_error,
|
||||
nativeLocationOf(_identifier),
|
||||
"Unsupported identifier in inline assembly."
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
|
||||
m_yulAnnotation->externalReferences[&_identifier].suffix = splitName.size() > 1 ? splitName.back() : "";
|
||||
break;
|
||||
default:
|
||||
m_errorReporter.declarationError(
|
||||
0000_error,
|
||||
nativeLocationOf(_identifier),
|
||||
"Multiple matching identifiers. Resolving overloaded identifiers is not supported."
|
||||
);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static set<string> suffixes{"slot", "offset", "length", "address", "selector"};
|
||||
string suffix;
|
||||
for (string const& s: suffixes)
|
||||
|
@ -85,6 +85,7 @@ private:
|
||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||
bool visit(Return const& _return) override;
|
||||
bool visit(UsingForDirective const& _usingFor) override;
|
||||
bool visit(BinaryOperation const& _binaryOperation) override;
|
||||
|
||||
void operator()(yul::FunctionDefinition const& _function) override;
|
||||
void operator()(yul::Identifier const& _identifier) override;
|
||||
@ -104,6 +105,7 @@ private:
|
||||
|
||||
InlineAssemblyAnnotation* m_yulAnnotation = nullptr;
|
||||
bool m_yulInsideFunction = false;
|
||||
bool m_typeContext = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ private:
|
||||
bool visit(ParameterList const&) override { return true; }
|
||||
bool visit(Return const&) override { return true; }
|
||||
bool visit(MemberAccess const&) override { return true; }
|
||||
bool visit(BinaryOperation const&) override { return true; }
|
||||
bool visit(ElementaryTypeNameExpression const&) override { return true; }
|
||||
bool visit(TupleExpression const&) override { return true; }
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
@ -63,11 +63,9 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
|
||||
auto typeFromParameterList = [&](ParameterList const* _list) {
|
||||
if (!_list)
|
||||
return m_typeSystem.builtinType(BuiltinType::Unit, {});
|
||||
return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::views::transform([&](auto _param) {
|
||||
auto& argAnnotation = annotation(*_param);
|
||||
solAssert(argAnnotation.type);
|
||||
return *argAnnotation.type;
|
||||
}) | ranges::to<std::vector<Type>>);
|
||||
auto& listAnnotation = annotation(*_list);
|
||||
solAssert(listAnnotation.type);
|
||||
return *listAnnotation.type;
|
||||
};
|
||||
|
||||
Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(
|
||||
@ -139,6 +137,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
{
|
||||
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
|
||||
@ -179,6 +178,8 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
|
||||
return m_typeSystem.freshTypeVariable(false, {});
|
||||
}
|
||||
*/
|
||||
|
||||
void TypeInference::unify(Type _a, Type _b)
|
||||
{
|
||||
for (auto failure: m_env->unify(_a, _b))
|
||||
@ -209,6 +210,8 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
Declaration const* declaration = identifierInfo.declaration;
|
||||
solAssert(!!declaration, "");
|
||||
|
||||
solAssert(identifierInfo.suffix == "", "");
|
||||
|
||||
auto& declarationAnnotation = annotation(*declaration);
|
||||
solAssert(declarationAnnotation.type);
|
||||
unify(*declarationAnnotation.type, m_wordType);
|
||||
@ -228,29 +231,89 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
|
||||
{
|
||||
auto& expressionAnnotation = annotation(_expression);
|
||||
if (m_expressionContext != ExpressionContext::Type)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _expression.location(), "Elementary type name expression only supported in type context.");
|
||||
expressionAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return false;
|
||||
}
|
||||
switch(_expression.type().typeName().token())
|
||||
{
|
||||
case Token::Word:
|
||||
expressionAnnotation.type = m_wordType;
|
||||
break;
|
||||
case Token::Void:
|
||||
expressionAnnotation.type = m_voidType;
|
||||
break;
|
||||
case Token::Integer:
|
||||
expressionAnnotation.type = m_integerType;
|
||||
break;
|
||||
default:
|
||||
m_errorReporter.typeError(0000_error, _expression.location(), "Only elementary types are supported.");
|
||||
expressionAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(BinaryOperation const& _binaryOperation)
|
||||
{
|
||||
auto& operationAnnotation = annotation(_binaryOperation);
|
||||
auto& leftAnnotation = annotation(_binaryOperation.leftExpression());
|
||||
auto& rightAnnotation = annotation(_binaryOperation.rightExpression());
|
||||
switch (m_expressionContext)
|
||||
{
|
||||
case ExpressionContext::Term:
|
||||
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operations in term context not yet supported.");
|
||||
operationAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return false;
|
||||
case ExpressionContext::Type:
|
||||
if (_binaryOperation.getOperator() == Token::Colon)
|
||||
{
|
||||
_binaryOperation.leftExpression().accept(*this);
|
||||
{
|
||||
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort};
|
||||
_binaryOperation.rightExpression().accept(*this);
|
||||
}
|
||||
solAssert(leftAnnotation.type);
|
||||
solAssert(rightAnnotation.type);
|
||||
unify(*leftAnnotation.type, *rightAnnotation.type);
|
||||
operationAnnotation.type = leftAnnotation.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operations other than colon in type context not yet supported.");
|
||||
operationAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
}
|
||||
return false;
|
||||
case ExpressionContext::Sort:
|
||||
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Invalid binary operation in sort context.");
|
||||
operationAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
|
||||
{
|
||||
solAssert(!_variableDeclaration.value());
|
||||
auto& variableAnnotation = annotation(_variableDeclaration);
|
||||
solAssert(!variableAnnotation.type);
|
||||
|
||||
Sort sort;
|
||||
for (auto const& typeClass: _variableDeclaration.sort())
|
||||
if (_variableDeclaration.typeExpression())
|
||||
{
|
||||
auto const* declaration = dynamic_cast<TypeClassDefinition const*>(typeClass->annotation().referencedDeclaration);
|
||||
if (!declaration)
|
||||
m_errorReporter.typeError(0000_error, typeClass->location(), "Expected type class.");
|
||||
sort.classes.emplace(TypeClass{declaration});
|
||||
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
|
||||
_variableDeclaration.typeExpression()->accept(*this);
|
||||
auto& typeExpressionAnnotation = annotation(*_variableDeclaration.typeExpression());
|
||||
solAssert(typeExpressionAnnotation.type);
|
||||
variableAnnotation.type = m_env->fresh(*typeExpressionAnnotation.type, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
variableAnnotation.type = [&] {
|
||||
if (_variableDeclaration.hasTypeName())
|
||||
return fromTypeName(_variableDeclaration.typeName());
|
||||
else
|
||||
return m_typeSystem.freshTypeVariable(false, sort);
|
||||
}();
|
||||
|
||||
// TODO: validate sort.
|
||||
variableAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -265,6 +328,13 @@ void TypeInference::endVisit(Assignment const& _assignment)
|
||||
auto& assignmentAnnotation = annotation(_assignment);
|
||||
solAssert(!assignmentAnnotation.type);
|
||||
|
||||
if (m_expressionContext != ExpressionContext::Term)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _assignment.location(), "Assignment outside term context.");
|
||||
assignmentAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return;
|
||||
}
|
||||
|
||||
auto& lhsAnnotation = annotation(_assignment.leftHandSide());
|
||||
solAssert(lhsAnnotation.type);
|
||||
auto& rhsAnnotation = annotation(_assignment.rightHandSide());
|
||||
@ -284,59 +354,121 @@ bool TypeInference::visit(Identifier const& _identifier)
|
||||
solAssert(!identifierAnnotation.type);
|
||||
|
||||
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
|
||||
solAssert(referencedDeclaration);
|
||||
|
||||
if (
|
||||
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
|
||||
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration)
|
||||
)
|
||||
m_errorReporter.fatalTypeError(0000_error, referencedDeclaration->location(), "Attempt to type ídentifier referring to unexpected node.");
|
||||
switch(m_expressionContext)
|
||||
{
|
||||
case ExpressionContext::Term:
|
||||
{
|
||||
solAssert(referencedDeclaration);
|
||||
|
||||
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||
if (!declarationAnnotation.type)
|
||||
referencedDeclaration->accept(*this);
|
||||
if (
|
||||
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
|
||||
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration)
|
||||
)
|
||||
{
|
||||
SecondarySourceLocation ssl;
|
||||
ssl.append("Referenced node.", referencedDeclaration->location());
|
||||
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), ssl, "Attempt to type identifier referring to unexpected node.");
|
||||
}
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||
if (!declarationAnnotation.type)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false);
|
||||
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else
|
||||
solAssert(false);
|
||||
solAssert(declarationAnnotation.type);
|
||||
|
||||
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true);
|
||||
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else
|
||||
solAssert(false);
|
||||
break;
|
||||
}
|
||||
case ExpressionContext::Type:
|
||||
{
|
||||
if (referencedDeclaration)
|
||||
{
|
||||
if (
|
||||
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration) &&
|
||||
!dynamic_cast<TypeDefinition const*>(referencedDeclaration)
|
||||
)
|
||||
{
|
||||
SecondarySourceLocation ssl;
|
||||
ssl.append("Referenced node.", referencedDeclaration->location());
|
||||
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), ssl, "Attempt to type identifier referring to unexpected node.");
|
||||
}
|
||||
|
||||
// TODO: Assert that this is a type class variable declaration.
|
||||
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||
if (!declarationAnnotation.type)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
|
||||
if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else if (dynamic_cast<TypeDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true);
|
||||
else
|
||||
solAssert(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: register free type variable name!
|
||||
identifierAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionContext::Sort:
|
||||
{
|
||||
solAssert(referencedDeclaration);
|
||||
|
||||
auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(referencedDeclaration);
|
||||
if (
|
||||
!dynamic_cast<TypeClassDefinition const*>(referencedDeclaration)
|
||||
)
|
||||
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), "Expected type class.");
|
||||
|
||||
identifierAnnotation.type = m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{typeClass}}});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(IdentifierPath const& _identifier)
|
||||
void TypeInference::endVisit(TupleExpression const& _tupleExpression)
|
||||
{
|
||||
// TODO: deduplicate with Identifier visit
|
||||
auto& identifierAnnotation = annotation(_identifier);
|
||||
solAssert(!identifierAnnotation.type);
|
||||
auto& expressionAnnotation = annotation(_tupleExpression);
|
||||
solAssert(!expressionAnnotation.type);
|
||||
|
||||
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
|
||||
solAssert(referencedDeclaration);
|
||||
auto componentTypes = _tupleExpression.components() | ranges::views::transform([&](auto _expr) -> Type {
|
||||
auto& componentAnnotation = annotation(*_expr);
|
||||
solAssert(componentAnnotation.type);
|
||||
return *componentAnnotation.type;
|
||||
}) | ranges::to<vector<Type>>;
|
||||
switch (m_expressionContext)
|
||||
{
|
||||
case ExpressionContext::Type:
|
||||
case ExpressionContext::Term:
|
||||
expressionAnnotation.type = TypeSystemHelpers{m_typeSystem}.tupleType(componentTypes);
|
||||
break;
|
||||
case ExpressionContext::Sort:
|
||||
{
|
||||
Type type = m_typeSystem.freshTypeVariable(false, {});
|
||||
for (auto componentType: componentTypes)
|
||||
unify(type, componentType);
|
||||
expressionAnnotation.type = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
|
||||
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration)
|
||||
)
|
||||
m_errorReporter.fatalTypeError(0000_error, referencedDeclaration->location(), "Attempt to type ídentifier referring to unexpected node.");
|
||||
|
||||
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||
if (!declarationAnnotation.type)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
|
||||
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false);
|
||||
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else
|
||||
solAssert(false);
|
||||
|
||||
return true;
|
||||
bool TypeInference::visit(IdentifierPath const&)
|
||||
{
|
||||
solAssert(false);
|
||||
}
|
||||
|
||||
bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
|
||||
@ -377,14 +509,24 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
|
||||
solAssert(functionType);
|
||||
// TODO: require exact match?
|
||||
unify(*functionType, m_env->fresh(*expectedFunctionType, true));
|
||||
functionTypes.erase(functionDefinition->name());
|
||||
}
|
||||
}
|
||||
if (!functionTypes.empty())
|
||||
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Type class instantiation does not implement all required functions.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (m_expressionContext != ExpressionContext::Term)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _memberAccess.location(), "Member access outside term context.");
|
||||
annotation(_memberAccess).type = m_typeSystem.freshTypeVariable(false, {});
|
||||
return false;
|
||||
|
||||
}
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()))
|
||||
{
|
||||
auto const* declaration = identifier->annotation().referencedDeclaration;
|
||||
@ -422,6 +564,34 @@ bool TypeInference::visit(MemberAccess const& _memberAccess)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(TypeDefinition const& _typeDefinition)
|
||||
{
|
||||
auto& typeDefinitionAnnotation = annotation(_typeDefinition);
|
||||
if (typeDefinitionAnnotation.type)
|
||||
return false;
|
||||
|
||||
if (_typeDefinition.typeExpression())
|
||||
{
|
||||
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
|
||||
_typeDefinition.typeExpression()->accept(*this);
|
||||
}
|
||||
|
||||
vector<Type> arguments;
|
||||
if (_typeDefinition.arguments())
|
||||
for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i)
|
||||
arguments.emplace_back(m_typeSystem.freshTypeVariable(true, {}));
|
||||
|
||||
if (arguments.empty())
|
||||
typeDefinitionAnnotation.type = m_typeSystem.type(TypeExpression::Constructor{&_typeDefinition}, arguments);
|
||||
else
|
||||
typeDefinitionAnnotation.type =
|
||||
TypeSystemHelpers{m_typeSystem}.functionType(
|
||||
TypeSystemHelpers{m_typeSystem}.tupleType(arguments),
|
||||
m_typeSystem.type(TypeExpression::Constructor{&_typeDefinition}, arguments)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(FunctionCall const&) { return true; }
|
||||
void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
@ -436,13 +606,28 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||
std::vector<Type> argTypes;
|
||||
for(auto arg: _functionCall.arguments())
|
||||
{
|
||||
auto& argAnnotation = annotation(*arg);
|
||||
solAssert(argAnnotation.type);
|
||||
argTypes.emplace_back(*argAnnotation.type);
|
||||
auto& argAnnotation = annotation(*arg);
|
||||
solAssert(argAnnotation.type);
|
||||
argTypes.emplace_back(*argAnnotation.type);
|
||||
}
|
||||
Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {}));
|
||||
unify(genericFunctionType, functionType);
|
||||
|
||||
functionCallAnnotation.type = m_env->resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_env->resolve(genericFunctionType))));
|
||||
switch(m_expressionContext)
|
||||
{
|
||||
case ExpressionContext::Term:
|
||||
case ExpressionContext::Type:
|
||||
{
|
||||
Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {}));
|
||||
unify(genericFunctionType, functionType);
|
||||
|
||||
functionCallAnnotation.type = m_env->resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_env->resolve(genericFunctionType))));
|
||||
break;
|
||||
}
|
||||
case ExpressionContext::Sort:
|
||||
m_errorReporter.typeError(0000_error, _functionCall.location(), "Function call in sort context.");
|
||||
functionCallAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -62,14 +62,18 @@ public:
|
||||
void endVisit(Return const& _return) override;
|
||||
|
||||
bool visit(MemberAccess const& _memberAccess) override;
|
||||
bool visit(ElementaryTypeNameExpression const& _expression) override;
|
||||
|
||||
// TODO: properly account for it
|
||||
bool visit(TypeClassDefinition const&) override;
|
||||
bool visit(TypeClassInstantiation const&) override;
|
||||
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
|
||||
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
|
||||
bool visit(TupleExpression const&) override { return true; }
|
||||
void endVisit(TupleExpression const& _tupleExpression) override;
|
||||
bool visit(TypeDefinition const& _typeDefinition) override;
|
||||
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
|
||||
bool visit(BinaryOperation const& _operation) override;
|
||||
private:
|
||||
Type fromTypeName(TypeName const& _typeName);
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
TypeSystem& m_typeSystem;
|
||||
@ -82,6 +86,13 @@ private:
|
||||
Annotation& annotation(ASTNode const& _node);
|
||||
|
||||
void unify(Type _a, Type _b);
|
||||
enum class ExpressionContext
|
||||
{
|
||||
Term,
|
||||
Type,
|
||||
Sort
|
||||
};
|
||||
ExpressionContext m_expressionContext = ExpressionContext::Term;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ m_typeSystem(_analysis.typeSystem())
|
||||
{BuiltinType::Word, "word", 0},
|
||||
{BuiltinType::Integer, "integer", 0}
|
||||
})
|
||||
m_typeSystem.declareBuiltinType(type, name, arity);
|
||||
m_typeSystem.declareTypeConstructor(type, name, arity);
|
||||
}
|
||||
|
||||
bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
|
||||
@ -140,6 +140,16 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeRegistration::visit(TypeDefinition const& _typeDefinition)
|
||||
{
|
||||
m_typeSystem.declareTypeConstructor(
|
||||
TypeExpression::Constructor{&_typeDefinition},
|
||||
_typeDefinition.name(),
|
||||
_typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
|
||||
{
|
||||
return m_analysis.annotation<TypeRegistration>(_node);
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
private:
|
||||
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
|
||||
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
|
||||
bool visit(TypeDefinition const& _typeDefinition) override;
|
||||
Annotation& annotation(ASTNode const& _node);
|
||||
|
||||
Analysis& m_analysis;
|
||||
|
@ -1065,4 +1065,8 @@ TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const
|
||||
{
|
||||
return initAnnotation<TypeClassDefinitionAnnotation>();
|
||||
}
|
||||
TypeDeclarationAnnotation& TypeDefinition::annotation() const
|
||||
{
|
||||
return initAnnotation<TypeDeclarationAnnotation>();
|
||||
}
|
||||
/// @}
|
||||
|
@ -1069,7 +1069,7 @@ public:
|
||||
Mutability _mutability = Mutability::Mutable,
|
||||
ASTPointer<OverrideSpecifier> _overrides = nullptr,
|
||||
Location _referenceLocation = Location::Unspecified,
|
||||
std::vector<ASTPointer<IdentifierPath>> _sort = {}
|
||||
ASTPointer<Expression> _typeExpression = {}
|
||||
):
|
||||
Declaration(_id, _location, _name, std::move(_nameLocation), _visibility),
|
||||
StructurallyDocumented(std::move(_documentation)),
|
||||
@ -1079,10 +1079,10 @@ public:
|
||||
m_mutability(_mutability),
|
||||
m_overrides(std::move(_overrides)),
|
||||
m_location(_referenceLocation),
|
||||
m_sort(std::move(_sort))
|
||||
m_typeExpression(std::move(_typeExpression))
|
||||
{
|
||||
// TODO: consider still asserting unless we are in experimental solidity.
|
||||
// solAssert(m_typeName, ""); solAssert(m_sorts.empy(), "");
|
||||
// solAssert(m_typeName, ""); solAssert(!m_typeExpression, "");
|
||||
}
|
||||
|
||||
|
||||
@ -1144,7 +1144,7 @@ public:
|
||||
/// @returns null when it is not accessible as a function.
|
||||
FunctionTypePointer functionType(bool /*_internal*/) const override;
|
||||
|
||||
std::vector<ASTPointer<IdentifierPath>> const& sort() const { return m_sort; }
|
||||
ASTPointer<Expression> const& typeExpression() const { return m_typeExpression; }
|
||||
VariableDeclarationAnnotation& annotation() const override;
|
||||
|
||||
protected:
|
||||
@ -1160,7 +1160,7 @@ private:
|
||||
Mutability m_mutability = Mutability::Mutable;
|
||||
ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node
|
||||
Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type.
|
||||
std::vector<ASTPointer<IdentifierPath>> m_sort;
|
||||
ASTPointer<Expression> m_typeExpression;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2142,7 +2142,8 @@ public:
|
||||
):
|
||||
Expression(_id, _location), m_left(std::move(_left)), m_operator(_operator), m_right(std::move(_right))
|
||||
{
|
||||
solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
|
||||
// TODO: assert against colon for non-experimental solidity
|
||||
solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator) || _operator == Token::Colon, "");
|
||||
}
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
@ -2499,7 +2500,7 @@ public:
|
||||
std::vector<ASTPointer<IdentifierPath>> const& _argumentSorts,
|
||||
ASTPointer<IdentifierPath> _class,
|
||||
std::vector<ASTPointer<ASTNode>> _subNodes
|
||||
):
|
||||
):
|
||||
ASTNode(_id, _location),
|
||||
m_typeConstructor(std::move(_typeConstructor)),
|
||||
m_argumentSorts(std::move(_argumentSorts)),
|
||||
@ -2524,6 +2525,40 @@ private:
|
||||
std::vector<ASTPointer<ASTNode>> m_subNodes;
|
||||
};
|
||||
|
||||
class TypeDefinition: public Declaration, public ScopeOpener
|
||||
{
|
||||
public:
|
||||
TypeDefinition(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> _name,
|
||||
SourceLocation _nameLocation,
|
||||
ASTPointer<ParameterList> _arguments,
|
||||
ASTPointer<Expression> _typeExpression
|
||||
):
|
||||
Declaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default),
|
||||
m_arguments(std::move(_arguments)),
|
||||
m_typeExpression(std::move(_typeExpression))
|
||||
{
|
||||
}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
Type const* type() const override { return nullptr; }
|
||||
|
||||
TypeDeclarationAnnotation& annotation() const override;
|
||||
|
||||
ParameterList const* arguments() const { return m_arguments.get(); }
|
||||
Expression const* typeExpression() const { return m_typeExpression.get(); }
|
||||
|
||||
bool experimentalSolidityOnly() const override { return true; }
|
||||
|
||||
private:
|
||||
ASTPointer<ParameterList> m_arguments;
|
||||
ASTPointer<Expression> m_typeExpression;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
}
|
||||
|
@ -99,6 +99,12 @@ class ElementaryTypeNameExpression;
|
||||
class Literal;
|
||||
class StructuredDocumentation;
|
||||
|
||||
/// Experimental Solidity nodes
|
||||
/// @{
|
||||
class TypeClassDefinition;
|
||||
class TypeClassInstantiation;
|
||||
/// @}
|
||||
|
||||
class VariableScope;
|
||||
|
||||
template <class T>
|
||||
|
@ -113,6 +113,7 @@ public:
|
||||
/// @{
|
||||
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
|
||||
virtual bool visit(TypeClassInstantiation& _node) { return visitNode(_node); }
|
||||
virtual bool visit(TypeDefinition& _node) { return visitNode(_node); }
|
||||
/// @}
|
||||
|
||||
virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
|
||||
@ -174,6 +175,7 @@ public:
|
||||
/// @{
|
||||
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); }
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
@ -257,6 +259,7 @@ public:
|
||||
/// @{
|
||||
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(TypeClassInstantiation const& _node) { return visitNode(_node); }
|
||||
virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); }
|
||||
/// @}
|
||||
|
||||
virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
|
||||
@ -318,6 +321,7 @@ public:
|
||||
/// @{
|
||||
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); }
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
|
@ -296,7 +296,8 @@ void VariableDeclaration::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
listAccept(m_sort, _visitor);
|
||||
if (m_typeExpression)
|
||||
m_typeExpression->accept(_visitor);
|
||||
if (m_overrides)
|
||||
m_overrides->accept(_visitor);
|
||||
if (m_value)
|
||||
@ -311,7 +312,8 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
listAccept(m_sort, _visitor);
|
||||
if (m_typeExpression)
|
||||
m_typeExpression->accept(_visitor);
|
||||
if (m_overrides)
|
||||
m_overrides->accept(_visitor);
|
||||
if (m_value)
|
||||
@ -1075,6 +1077,30 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void TypeDefinition::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
if (m_arguments)
|
||||
m_arguments->accept(_visitor);
|
||||
if (m_typeExpression)
|
||||
m_typeExpression->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void TypeDefinition::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
if (m_arguments)
|
||||
m_arguments->accept(_visitor);
|
||||
if (m_typeExpression)
|
||||
m_typeExpression->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
/// @}
|
||||
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ std::string experimental::canonicalTypeName(Type _type)
|
||||
{
|
||||
case BuiltinType::Void:
|
||||
stream << "void";
|
||||
break;
|
||||
break;
|
||||
case BuiltinType::Function:
|
||||
stream << "fun";
|
||||
break;
|
||||
@ -305,11 +305,6 @@ experimental::Type TypeEnvironment::resolveRecursive(Type _type) const
|
||||
}
|
||||
|
||||
|
||||
void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments)
|
||||
{
|
||||
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{
|
||||
@ -322,11 +317,16 @@ void TypeSystem::declareTypeConstructor(TypeExpression::Constructor _typeConstru
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const
|
||||
{
|
||||
return type(_builtinType, std::move(_arguments));
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::type(TypeExpression::Constructor _constructor, std::vector<Type> _arguments) const
|
||||
{
|
||||
// TODO: proper error handling
|
||||
auto const& info = m_typeConstructors.at(_builtinType);
|
||||
auto const& info = m_typeConstructors.at(_constructor);
|
||||
solAssert(info.arguments == _arguments.size(), "Invalid arity.");
|
||||
return TypeExpression{_builtinType, _arguments};
|
||||
return TypeExpression{_constructor, _arguments};
|
||||
}
|
||||
|
||||
experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize)
|
||||
|
@ -146,8 +146,8 @@ public:
|
||||
TypeSystem() {}
|
||||
TypeSystem(TypeSystem const&) = delete;
|
||||
TypeSystem const& operator=(TypeSystem const&) = delete;
|
||||
void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments);
|
||||
Type builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const;
|
||||
Type type(TypeExpression::Constructor _typeConstructor, std::vector<Type> _arguments) const;
|
||||
std::string typeName(TypeExpression::Constructor _typeConstructor) const
|
||||
{
|
||||
// TODO: proper error handling
|
||||
|
135
libsolidity/codegen/experimental/IRVariable.cpp
Normal file
135
libsolidity/codegen/experimental/IRVariable.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolidity/codegen/experimental/Common.h>
|
||||
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/experimental/IRVariable.h>
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::util;
|
||||
|
||||
template<typename Node>
|
||||
Type getType(IRGenerationContext const& _context, Node const& _node)
|
||||
{
|
||||
auto& annotation = _context.analysis.annotation<TypeInference>(_node);
|
||||
solAssert(annotation.type);
|
||||
return _context.env->resolve(*annotation.type);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
size_t getTypeStackSlots(IRGenerationContext const& _context, Type _type)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
IRVariable::IRVariable(IRGenerationContext const& _context, std::string _baseName, Type _type):
|
||||
m_baseName(std::move(_baseName)), m_type(_type)
|
||||
{
|
||||
}
|
||||
|
||||
IRVariable::IRVariable(IRGenerationContext const& _context, VariableDeclaration const& _declaration):
|
||||
IRVariable(_context, IRNames::localVariable(_declaration), getType(_context, _declaration))
|
||||
{
|
||||
}
|
||||
|
||||
IRVariable::IRVariable(IRGenerationContext const& _context, Expression const& _expression):
|
||||
IRVariable(_context, IRNames::localVariable(_expression), getType(_context, _expression))
|
||||
{
|
||||
}
|
||||
|
||||
IRVariable IRVariable::part(string const& _name) const
|
||||
{
|
||||
for (auto const& [itemName, itemType]: m_type.stackItems())
|
||||
if (itemName == _name)
|
||||
{
|
||||
solAssert(itemName.empty() || itemType, "");
|
||||
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
||||
}
|
||||
solAssert(false, "Invalid stack item name: " + _name);
|
||||
}
|
||||
|
||||
bool IRVariable::hasPart(std::string const& _name) const
|
||||
{
|
||||
for (auto const& [itemName, itemType]: m_type.stackItems())
|
||||
if (itemName == _name)
|
||||
{
|
||||
solAssert(itemName.empty() || itemType, "");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<string> IRVariable::stackSlots() const
|
||||
{
|
||||
vector<string> result;
|
||||
for (auto const& [itemName, itemType]: m_type.stackItems())
|
||||
if (itemType)
|
||||
{
|
||||
solAssert(!itemName.empty(), "");
|
||||
solAssert(m_type != *itemType, "");
|
||||
result += IRVariable{suffixedName(itemName), *itemType}.stackSlots();
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(itemName.empty(), "");
|
||||
result.emplace_back(m_baseName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string IRVariable::commaSeparatedList() const
|
||||
{
|
||||
return joinHumanReadable(stackSlots());
|
||||
}
|
||||
|
||||
string IRVariable::commaSeparatedListPrefixed() const
|
||||
{
|
||||
return joinHumanReadablePrefixed(stackSlots());
|
||||
}
|
||||
|
||||
string IRVariable::name() const
|
||||
{
|
||||
solAssert(m_type.sizeOnStack() == 1, "");
|
||||
auto const& [itemName, type] = m_type.stackItems().front();
|
||||
solAssert(!type, "Expected null type for name " + itemName);
|
||||
return suffixedName(itemName);
|
||||
}
|
||||
|
||||
IRVariable IRVariable::tupleComponent(size_t _i) const
|
||||
{
|
||||
solAssert(
|
||||
m_type.category() == Type::Category::Tuple,
|
||||
"Requested tuple component of non-tuple IR variable."
|
||||
);
|
||||
return part(IRNames::tupleComponent(_i));
|
||||
}
|
||||
|
||||
string IRVariable::suffixedName(string const& _suffix) const
|
||||
{
|
||||
if (_suffix.empty())
|
||||
return m_baseName;
|
||||
else
|
||||
return m_baseName + '_' + _suffix;
|
||||
}
|
85
libsolidity/codegen/experimental/IRVariable.h
Normal file
85
libsolidity/codegen/experimental/IRVariable.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
class VariableDeclaration;
|
||||
class Expression;
|
||||
}
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class IRGenerationContext;
|
||||
|
||||
class IRVariable
|
||||
{
|
||||
public:
|
||||
/// IR variable with explicit base name @a _baseName and type @a _type.
|
||||
IRVariable(IRGenerationContext const& _analysis, std::string _baseName, Type _type);
|
||||
/// IR variable referring to the declaration @a _decl.
|
||||
IRVariable(IRGenerationContext const& _analysis, VariableDeclaration const& _decl);
|
||||
/// IR variable referring to the expression @a _expr.
|
||||
IRVariable(IRGenerationContext const& _analysis, Expression const& _expression);
|
||||
|
||||
/// @returns the name of the variable, if it occupies a single stack slot (otherwise throws).
|
||||
std::string name() const;
|
||||
|
||||
/// @returns a comma-separated list of the stack slots of the variable.
|
||||
std::string commaSeparatedList() const;
|
||||
|
||||
/// @returns a comma-separated list of the stack slots of the variable that is
|
||||
/// prefixed with a comma, unless it is empty.
|
||||
std::string commaSeparatedListPrefixed() const;
|
||||
|
||||
/// @returns an IRVariable referring to the tuple component @a _i of a tuple variable.
|
||||
IRVariable tupleComponent(std::size_t _i) const;
|
||||
|
||||
/// @returns the type of the variable.
|
||||
Type const& type() const { return m_type; }
|
||||
|
||||
/// @returns an IRVariable referring to the stack component @a _slot of the variable.
|
||||
/// @a _slot must be among the stack slots in ``m_type.stackItems()``.
|
||||
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
||||
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
||||
IRVariable part(std::string const& _slot) const;
|
||||
|
||||
/// @returns true if variable contains @a _name component
|
||||
/// @a _name name of the component that is being checked
|
||||
bool hasPart(std::string const& _name) const;
|
||||
|
||||
/// @returns a vector containing the names of the stack slots of the variable.
|
||||
std::vector<std::string> stackSlots() const;
|
||||
|
||||
private:
|
||||
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
||||
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
||||
std::string suffixedName(std::string const& _suffix) const;
|
||||
std::string m_baseName;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -463,6 +463,8 @@ bool CompilerStack::analyze()
|
||||
|
||||
try
|
||||
{
|
||||
bool experimentalSolidity = !m_sourceOrder.empty() && m_sourceOrder.front()->ast->experimentalSolidity();
|
||||
|
||||
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
|
||||
@ -470,7 +472,7 @@ bool CompilerStack::analyze()
|
||||
|
||||
m_globalContext = make_shared<GlobalContext>();
|
||||
// We need to keep the same resolver during the whole process.
|
||||
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter);
|
||||
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter, experimentalSolidity);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast && !resolver.registerDeclarations(*source->ast))
|
||||
return false;
|
||||
@ -496,7 +498,7 @@ bool CompilerStack::analyze()
|
||||
if (source->ast && !resolver.resolveNamesAndTypes(*source->ast))
|
||||
return false;
|
||||
|
||||
if (!m_sourceOrder.empty() && m_sourceOrder.front()->ast->experimentalSolidity())
|
||||
if (experimentalSolidity)
|
||||
{
|
||||
if (!analyzeExperimental())
|
||||
noErrors = false;
|
||||
|
@ -126,7 +126,10 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
|
||||
nodes.push_back(parseEnumDefinition());
|
||||
break;
|
||||
case Token::Type:
|
||||
nodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||
if (m_experimentalSolidityEnabledInCurrentSourceUnit)
|
||||
nodes.push_back(parseTypeDefinition());
|
||||
else
|
||||
nodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||
break;
|
||||
case Token::Using:
|
||||
nodes.push_back(parseUsingDirective());
|
||||
@ -1716,47 +1719,16 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
|
||||
nodeFactory.markEndPosition();
|
||||
auto [identifier, nameLocation] = expectIdentifierWithLocation();
|
||||
|
||||
ASTPointer<TypeName> type;
|
||||
vector<ASTPointer<IdentifierPath>> sorts;
|
||||
ASTPointer<Expression> type;
|
||||
if (m_scanner->currentToken() == Token::Colon)
|
||||
{
|
||||
advance();
|
||||
// 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());
|
||||
}
|
||||
type = parseExpression();
|
||||
nodeFactory.setEndPositionFromNode(type);
|
||||
}
|
||||
|
||||
return nodeFactory.createNode<VariableDeclaration>(
|
||||
type,
|
||||
nullptr,
|
||||
identifier,
|
||||
nameLocation,
|
||||
nullptr,
|
||||
@ -1766,7 +1738,7 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
|
||||
VariableDeclaration::Mutability::Mutable,
|
||||
nullptr,
|
||||
VariableDeclaration::Location::Unspecified,
|
||||
sorts
|
||||
type
|
||||
);
|
||||
}
|
||||
|
||||
@ -1868,6 +1840,33 @@ ASTPointer<TypeClassInstantiation> Parser::parseTypeClassInstantiation()
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<TypeDefinition> Parser::parseTypeDefinition()
|
||||
{
|
||||
solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
expectToken(Token::Type);
|
||||
auto&& [name, nameLocation] = expectIdentifierWithLocation();
|
||||
|
||||
ASTPointer<ParameterList> arguments;
|
||||
if (m_scanner->currentToken() == Token::LParen)
|
||||
arguments = parseParameterList();
|
||||
|
||||
ASTPointer<Expression> expression;
|
||||
if (m_scanner->currentToken() == Token::Assign)
|
||||
{
|
||||
expectToken(Token::Assign);
|
||||
expression = parseExpression();
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<TypeDefinition>(
|
||||
std::move(name),
|
||||
std::move(nameLocation),
|
||||
std::move(arguments),
|
||||
std::move(expression)
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
@ -2078,9 +2077,9 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression);
|
||||
ASTNodeFactory nodeFactory(*this, expression);
|
||||
int precedence = TokenTraits::precedence(m_scanner->currentToken());
|
||||
int precedence = tokenPrecedence(m_scanner->currentToken());
|
||||
for (; precedence >= _minPrecedence; --precedence)
|
||||
while (TokenTraits::precedence(m_scanner->currentToken()) == precedence)
|
||||
while (tokenPrecedence(m_scanner->currentToken()) == precedence)
|
||||
{
|
||||
Token op = m_scanner->currentToken();
|
||||
advance();
|
||||
@ -2097,6 +2096,15 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
|
||||
return expression;
|
||||
}
|
||||
|
||||
int Parser::tokenPrecedence(Token _token) const
|
||||
{
|
||||
if (m_experimentalSolidityEnabledInCurrentSourceUnit && _token == Token::Colon)
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
return TokenTraits::precedence(m_scanner->currentToken());
|
||||
}
|
||||
|
||||
ASTPointer<Expression> Parser::parseUnaryExpression(
|
||||
ASTPointer<Expression> const& _partiallyParsedExpression
|
||||
)
|
||||
@ -2555,7 +2563,14 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
|
||||
while (m_scanner->currentToken() == Token::Period)
|
||||
{
|
||||
advance();
|
||||
iap.path.push_back(parseIdentifierOrAddress());
|
||||
if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Number)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
nodeFactory.markEndPosition();
|
||||
iap.path.push_back(nodeFactory.createNode<Identifier>(getLiteralAndAdvance()));
|
||||
}
|
||||
else
|
||||
iap.path.push_back(parseIdentifierOrAddress());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -178,6 +178,7 @@ private:
|
||||
ASTPointer<VariableDeclaration> parsePostfixVariableDeclaration();
|
||||
ASTPointer<TypeClassDefinition> parseTypeClassDefinition();
|
||||
ASTPointer<TypeClassInstantiation> parseTypeClassInstantiation();
|
||||
ASTPointer<TypeDefinition> parseTypeDefinition();
|
||||
///@}
|
||||
|
||||
///@{
|
||||
@ -234,6 +235,8 @@ private:
|
||||
bool isQuotedPath() const;
|
||||
bool isStdlibPath() const;
|
||||
|
||||
int tokenPrecedence(Token _token) const;
|
||||
|
||||
ASTPointer<ASTString> getStdlibImportPathAndAdvance();
|
||||
|
||||
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
|
||||
|
@ -65,7 +65,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
||||
Scoper::assignScopes(*sourceUnit);
|
||||
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||
GlobalContext globalContext;
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false);
|
||||
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
|
||||
solAssert(!Error::containsErrors(errorReporter.errors()), "");
|
||||
resolver.registerDeclarations(*sourceUnit);
|
||||
|
@ -128,7 +128,7 @@ bytes compileFirstExpression(
|
||||
GlobalContext globalContext;
|
||||
Scoper::assignScopes(*sourceUnit);
|
||||
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false);
|
||||
resolver.registerDeclarations(*sourceUnit);
|
||||
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
|
||||
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
|
||||
|
@ -30,15 +30,29 @@ function f(a:_:A) -> b:_:B {
|
||||
return a;
|
||||
}
|
||||
|
||||
type uint256 = word;
|
||||
|
||||
instantiation uint256 : A {
|
||||
function testValue(x:uint256) -> y:word {
|
||||
assembly {
|
||||
y := 21
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
fallback() external {
|
||||
let x : word : A;
|
||||
let y;
|
||||
let z: (word, word);
|
||||
let w: uint256;
|
||||
assembly {
|
||||
x := 0x42
|
||||
}
|
||||
z = f(z);
|
||||
y = f(x);
|
||||
y = B.testValue(x);
|
||||
y = A.testValue(w);
|
||||
assembly {
|
||||
mstore(0, y)
|
||||
return(0, 32)
|
||||
@ -48,4 +62,4 @@ contract C {
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// () -> 21
|
||||
// () -> 0
|
||||
|
Loading…
Reference in New Issue
Block a user