This commit is contained in:
Daniel Kirchner 2023-06-24 06:24:28 +02:00
parent 62a5291bbd
commit 0d1679cee0
26 changed files with 763 additions and 141 deletions

View File

@ -666,7 +666,7 @@ void Scanner::scanToken()
case '.': case '.':
// . Number // . Number
advance(); advance();
if (isDecimalDigit(m_char)) if (m_kind != ScannerKind::ExperimentalSolidity && isDecimalDigit(m_char))
token = scanNumber('.'); token = scanNumber('.');
else else
token = Token::Period; token = Token::Period;

View File

@ -339,7 +339,7 @@ namespace TokenTraits
{ {
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback || 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::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) constexpr bool isExperimentalSolidityOnlyKeyword(Token tok)
{ {

View File

@ -39,11 +39,13 @@ namespace solidity::frontend
NameAndTypeResolver::NameAndTypeResolver( NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext, GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
ErrorReporter& _errorReporter ErrorReporter& _errorReporter,
bool _experimentalSolidity
): ):
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_globalContext(_globalContext) m_globalContext(_globalContext),
m_experimentalSolidity(_experimentalSolidity)
{ {
m_scopes[nullptr] = make_shared<DeclarationContainer>(); m_scopes[nullptr] = make_shared<DeclarationContainer>();
for (Declaration const* declaration: _globalContext.declarations()) for (Declaration const* declaration: _globalContext.declarations())

View File

@ -59,7 +59,8 @@ public:
NameAndTypeResolver( NameAndTypeResolver(
GlobalContext& _globalContext, GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
langutil::ErrorReporter& _errorReporter langutil::ErrorReporter& _errorReporter,
bool _experimentalSolidity
); );
/// Registers all declarations found in the AST node, usually a source unit. /// Registers all declarations found in the AST node, usually a source unit.
/// @returns false in case of error. /// @returns false in case of error.
@ -107,6 +108,7 @@ public:
/// Sets the current scope. /// Sets the current scope.
void setScope(ASTNode const* _node); void setScope(ASTNode const* _node);
bool experimentalSolidity() const { return m_experimentalSolidity; }
private: private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors. /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true); bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
@ -132,6 +134,7 @@ private:
DeclarationContainer* m_currentScope = nullptr; DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext; GlobalContext& m_globalContext;
bool m_experimentalSolidity = false;
}; };
/** /**

View File

@ -113,6 +113,21 @@ bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
if (_varDecl.documentation()) if (_varDecl.documentation())
resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation()); 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; return true;
} }
@ -121,6 +136,8 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
if (declarations.empty()) if (declarations.empty())
{ {
if (m_resolver.experimentalSolidity() && m_typeContext)
return false;
string suggestions = m_resolver.similarNameSuggestions(_identifier.name()); string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
string errorMessage = "Undeclared identifier."; string errorMessage = "Undeclared identifier.";
if (!suggestions.empty()) if (!suggestions.empty())
@ -233,6 +250,24 @@ bool ReferencesResolver::visit(Return const& _return)
return true; 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) void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
{ {
solAssert(nativeLocationOf(_function) == originLocationOf(_function), ""); solAssert(nativeLocationOf(_function) == originLocationOf(_function), "");
@ -253,6 +288,47 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{ {
solAssert(nativeLocationOf(_identifier) == originLocationOf(_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"}; static set<string> suffixes{"slot", "offset", "length", "address", "selector"};
string suffix; string suffix;
for (string const& s: suffixes) for (string const& s: suffixes)

View File

@ -85,6 +85,7 @@ private:
bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(Return const& _return) override; bool visit(Return const& _return) override;
bool visit(UsingForDirective const& _usingFor) override; bool visit(UsingForDirective const& _usingFor) override;
bool visit(BinaryOperation const& _binaryOperation) override;
void operator()(yul::FunctionDefinition const& _function) override; void operator()(yul::FunctionDefinition const& _function) override;
void operator()(yul::Identifier const& _identifier) override; void operator()(yul::Identifier const& _identifier) override;
@ -104,6 +105,7 @@ private:
InlineAssemblyAnnotation* m_yulAnnotation = nullptr; InlineAssemblyAnnotation* m_yulAnnotation = nullptr;
bool m_yulInsideFunction = false; bool m_yulInsideFunction = false;
bool m_typeContext = false;
}; };
} }

View File

@ -55,6 +55,9 @@ private:
bool visit(ParameterList const&) override { return true; } bool visit(ParameterList const&) override { return true; }
bool visit(Return const&) override { return true; } bool visit(Return const&) override { return true; }
bool visit(MemberAccess 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; langutil::ErrorReporter& m_errorReporter;
}; };

View File

@ -63,11 +63,9 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
auto typeFromParameterList = [&](ParameterList const* _list) { auto typeFromParameterList = [&](ParameterList const* _list) {
if (!_list) if (!_list)
return m_typeSystem.builtinType(BuiltinType::Unit, {}); return m_typeSystem.builtinType(BuiltinType::Unit, {});
return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::views::transform([&](auto _param) { auto& listAnnotation = annotation(*_list);
auto& argAnnotation = annotation(*_param); solAssert(listAnnotation.type);
solAssert(argAnnotation.type); return *listAnnotation.type;
return *argAnnotation.type;
}) | ranges::to<std::vector<Type>>);
}; };
Type functionType = TypeSystemHelpers{m_typeSystem}.functionType( Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(
@ -139,6 +137,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
return false; return false;
} }
/*
experimental::Type TypeInference::fromTypeName(TypeName const& _typeName) experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
{ {
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName 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."); m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
return m_typeSystem.freshTypeVariable(false, {}); return m_typeSystem.freshTypeVariable(false, {});
} }
*/
void TypeInference::unify(Type _a, Type _b) void TypeInference::unify(Type _a, Type _b)
{ {
for (auto failure: m_env->unify(_a, _b)) for (auto failure: m_env->unify(_a, _b))
@ -209,6 +210,8 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
Declaration const* declaration = identifierInfo.declaration; Declaration const* declaration = identifierInfo.declaration;
solAssert(!!declaration, ""); solAssert(!!declaration, "");
solAssert(identifierInfo.suffix == "", "");
auto& declarationAnnotation = annotation(*declaration); auto& declarationAnnotation = annotation(*declaration);
solAssert(declarationAnnotation.type); solAssert(declarationAnnotation.type);
unify(*declarationAnnotation.type, m_wordType); unify(*declarationAnnotation.type, m_wordType);
@ -228,29 +231,89 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
return false; 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) bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
{ {
solAssert(!_variableDeclaration.value()); solAssert(!_variableDeclaration.value());
auto& variableAnnotation = annotation(_variableDeclaration); auto& variableAnnotation = annotation(_variableDeclaration);
solAssert(!variableAnnotation.type); solAssert(!variableAnnotation.type);
Sort sort; if (_variableDeclaration.typeExpression())
for (auto const& typeClass: _variableDeclaration.sort())
{ {
auto const* declaration = dynamic_cast<TypeClassDefinition const*>(typeClass->annotation().referencedDeclaration); ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
if (!declaration) _variableDeclaration.typeExpression()->accept(*this);
m_errorReporter.typeError(0000_error, typeClass->location(), "Expected type class."); auto& typeExpressionAnnotation = annotation(*_variableDeclaration.typeExpression());
sort.classes.emplace(TypeClass{declaration}); solAssert(typeExpressionAnnotation.type);
variableAnnotation.type = m_env->fresh(*typeExpressionAnnotation.type, false);
return false;
} }
variableAnnotation.type = [&] { variableAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
if (_variableDeclaration.hasTypeName())
return fromTypeName(_variableDeclaration.typeName());
else
return m_typeSystem.freshTypeVariable(false, sort);
}();
// TODO: validate sort.
return false; return false;
} }
@ -265,6 +328,13 @@ void TypeInference::endVisit(Assignment const& _assignment)
auto& assignmentAnnotation = annotation(_assignment); auto& assignmentAnnotation = annotation(_assignment);
solAssert(!assignmentAnnotation.type); 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()); auto& lhsAnnotation = annotation(_assignment.leftHandSide());
solAssert(lhsAnnotation.type); solAssert(lhsAnnotation.type);
auto& rhsAnnotation = annotation(_assignment.rightHandSide()); auto& rhsAnnotation = annotation(_assignment.rightHandSide());
@ -284,59 +354,121 @@ bool TypeInference::visit(Identifier const& _identifier)
solAssert(!identifierAnnotation.type); solAssert(!identifierAnnotation.type);
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration; auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
solAssert(referencedDeclaration);
if ( switch(m_expressionContext)
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) && {
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration) case ExpressionContext::Term:
) {
m_errorReporter.fatalTypeError(0000_error, referencedDeclaration->location(), "Attempt to type ídentifier referring to unexpected node."); solAssert(referencedDeclaration);
auto& declarationAnnotation = annotation(*referencedDeclaration); if (
if (!declarationAnnotation.type) !dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
referencedDeclaration->accept(*this); !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)) solAssert(declarationAnnotation.type);
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false);
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration)) if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
identifierAnnotation.type = declarationAnnotation.type; identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true);
else else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
solAssert(false); 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; return true;
} }
bool TypeInference::visit(IdentifierPath const& _identifier) void TypeInference::endVisit(TupleExpression const& _tupleExpression)
{ {
// TODO: deduplicate with Identifier visit auto& expressionAnnotation = annotation(_tupleExpression);
auto& identifierAnnotation = annotation(_identifier); solAssert(!expressionAnnotation.type);
solAssert(!identifierAnnotation.type);
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration; auto componentTypes = _tupleExpression.components() | ranges::views::transform([&](auto _expr) -> Type {
solAssert(referencedDeclaration); 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 ( bool TypeInference::visit(IdentifierPath const&)
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) && {
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration) solAssert(false);
)
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(TypeClassInstantiation const& _typeClassInstantiation) bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
@ -377,14 +509,24 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
solAssert(functionType); solAssert(functionType);
// TODO: require exact match? // TODO: require exact match?
unify(*functionType, m_env->fresh(*expectedFunctionType, true)); 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; return false;
} }
bool TypeInference::visit(MemberAccess const& _memberAccess) 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())) if (auto const* identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()))
{ {
auto const* declaration = identifier->annotation().referencedDeclaration; auto const* declaration = identifier->annotation().referencedDeclaration;
@ -422,6 +564,34 @@ bool TypeInference::visit(MemberAccess const& _memberAccess)
return false; 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; } bool TypeInference::visit(FunctionCall const&) { return true; }
void TypeInference::endVisit(FunctionCall const& _functionCall) void TypeInference::endVisit(FunctionCall const& _functionCall)
{ {
@ -436,13 +606,28 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
std::vector<Type> argTypes; std::vector<Type> argTypes;
for(auto arg: _functionCall.arguments()) for(auto arg: _functionCall.arguments())
{ {
auto& argAnnotation = annotation(*arg); auto& argAnnotation = annotation(*arg);
solAssert(argAnnotation.type); solAssert(argAnnotation.type);
argTypes.emplace_back(*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;
}
} }

View File

@ -62,14 +62,18 @@ public:
void endVisit(Return const& _return) override; void endVisit(Return const& _return) override;
bool visit(MemberAccess const& _memberAccess) override; bool visit(MemberAccess const& _memberAccess) override;
bool visit(ElementaryTypeNameExpression const& _expression) override;
// TODO: properly account for it bool visit(TypeClassDefinition const& _typeClassDefinition) override;
bool visit(TypeClassDefinition const&) override; bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
bool visit(TypeClassInstantiation const&) 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 visitNode(ASTNode const& _node) override;
bool visit(BinaryOperation const& _operation) override;
private: private:
Type fromTypeName(TypeName const& _typeName);
Analysis& m_analysis; Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
TypeSystem& m_typeSystem; TypeSystem& m_typeSystem;
@ -82,6 +86,13 @@ private:
Annotation& annotation(ASTNode const& _node); Annotation& annotation(ASTNode const& _node);
void unify(Type _a, Type _b); void unify(Type _a, Type _b);
enum class ExpressionContext
{
Term,
Type,
Sort
};
ExpressionContext m_expressionContext = ExpressionContext::Term;
}; };
} }

View File

@ -43,7 +43,7 @@ m_typeSystem(_analysis.typeSystem())
{BuiltinType::Word, "word", 0}, {BuiltinType::Word, "word", 0},
{BuiltinType::Integer, "integer", 0} {BuiltinType::Integer, "integer", 0}
}) })
m_typeSystem.declareBuiltinType(type, name, arity); m_typeSystem.declareTypeConstructor(type, name, arity);
} }
bool TypeRegistration::analyze(SourceUnit const& _sourceUnit) bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
@ -140,6 +140,16 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati
return false; 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) TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
{ {
return m_analysis.annotation<TypeRegistration>(_node); return m_analysis.annotation<TypeRegistration>(_node);

View File

@ -41,6 +41,7 @@ public:
private: private:
bool visit(TypeClassDefinition const& _typeClassDefinition) override; bool visit(TypeClassDefinition const& _typeClassDefinition) override;
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
bool visit(TypeDefinition const& _typeDefinition) override;
Annotation& annotation(ASTNode const& _node); Annotation& annotation(ASTNode const& _node);
Analysis& m_analysis; Analysis& m_analysis;

View File

@ -1065,4 +1065,8 @@ TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const
{ {
return initAnnotation<TypeClassDefinitionAnnotation>(); return initAnnotation<TypeClassDefinitionAnnotation>();
} }
TypeDeclarationAnnotation& TypeDefinition::annotation() const
{
return initAnnotation<TypeDeclarationAnnotation>();
}
/// @} /// @}

View File

@ -1069,7 +1069,7 @@ public:
Mutability _mutability = Mutability::Mutable, Mutability _mutability = Mutability::Mutable,
ASTPointer<OverrideSpecifier> _overrides = nullptr, ASTPointer<OverrideSpecifier> _overrides = nullptr,
Location _referenceLocation = Location::Unspecified, Location _referenceLocation = Location::Unspecified,
std::vector<ASTPointer<IdentifierPath>> _sort = {} ASTPointer<Expression> _typeExpression = {}
): ):
Declaration(_id, _location, _name, std::move(_nameLocation), _visibility), Declaration(_id, _location, _name, std::move(_nameLocation), _visibility),
StructurallyDocumented(std::move(_documentation)), StructurallyDocumented(std::move(_documentation)),
@ -1079,10 +1079,10 @@ public:
m_mutability(_mutability), m_mutability(_mutability),
m_overrides(std::move(_overrides)), m_overrides(std::move(_overrides)),
m_location(_referenceLocation), m_location(_referenceLocation),
m_sort(std::move(_sort)) m_typeExpression(std::move(_typeExpression))
{ {
// TODO: consider still asserting unless we are in experimental solidity. // 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. /// @returns null when it is not accessible as a function.
FunctionTypePointer functionType(bool /*_internal*/) const override; 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; VariableDeclarationAnnotation& annotation() const override;
protected: protected:
@ -1160,7 +1160,7 @@ private:
Mutability m_mutability = Mutability::Mutable; Mutability m_mutability = Mutability::Mutable;
ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node
Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. 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)) 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(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -2499,7 +2500,7 @@ public:
std::vector<ASTPointer<IdentifierPath>> const& _argumentSorts, std::vector<ASTPointer<IdentifierPath>> const& _argumentSorts,
ASTPointer<IdentifierPath> _class, ASTPointer<IdentifierPath> _class,
std::vector<ASTPointer<ASTNode>> _subNodes std::vector<ASTPointer<ASTNode>> _subNodes
): ):
ASTNode(_id, _location), ASTNode(_id, _location),
m_typeConstructor(std::move(_typeConstructor)), m_typeConstructor(std::move(_typeConstructor)),
m_argumentSorts(std::move(_argumentSorts)), m_argumentSorts(std::move(_argumentSorts)),
@ -2524,6 +2525,40 @@ private:
std::vector<ASTPointer<ASTNode>> m_subNodes; 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;
};
/// @} /// @}
} }

View File

@ -99,6 +99,12 @@ class ElementaryTypeNameExpression;
class Literal; class Literal;
class StructuredDocumentation; class StructuredDocumentation;
/// Experimental Solidity nodes
/// @{
class TypeClassDefinition;
class TypeClassInstantiation;
/// @}
class VariableScope; class VariableScope;
template <class T> template <class T>

View File

@ -113,6 +113,7 @@ public:
/// @{ /// @{
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); } virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
virtual bool visit(TypeClassInstantiation& _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); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
@ -174,6 +175,7 @@ public:
/// @{ /// @{
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); }
virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); }
/// @} /// @}
protected: protected:
@ -257,6 +259,7 @@ public:
/// @{ /// @{
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); } virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
virtual bool visit(TypeClassInstantiation 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); } virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
@ -318,6 +321,7 @@ public:
/// @{ /// @{
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); }
virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); }
/// @} /// @}
protected: protected:

View File

@ -296,7 +296,8 @@ void VariableDeclaration::accept(ASTVisitor& _visitor)
{ {
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
listAccept(m_sort, _visitor); if (m_typeExpression)
m_typeExpression->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
if (m_value) if (m_value)
@ -311,7 +312,8 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
{ {
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
listAccept(m_sort, _visitor); if (m_typeExpression)
m_typeExpression->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
if (m_value) if (m_value)
@ -1075,6 +1077,30 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const
} }
_visitor.endVisit(*this); _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);
}
/// @} /// @}
} }

View File

@ -109,7 +109,7 @@ std::string experimental::canonicalTypeName(Type _type)
{ {
case BuiltinType::Void: case BuiltinType::Void:
stream << "void"; stream << "void";
break; break;
case BuiltinType::Function: case BuiltinType::Function:
stream << "fun"; stream << "fun";
break; 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) void TypeSystem::declareTypeConstructor(TypeExpression::Constructor _typeConstructor, std::string _name, size_t _arguments)
{ {
bool newlyInserted = m_typeConstructors.emplace(std::make_pair(_typeConstructor, TypeConstructorInfo{ 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 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 // 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."); solAssert(info.arguments == _arguments.size(), "Invalid arity.");
return TypeExpression{_builtinType, _arguments}; return TypeExpression{_constructor, _arguments};
} }
experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize) experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize)

View File

@ -146,8 +146,8 @@ public:
TypeSystem() {} TypeSystem() {}
TypeSystem(TypeSystem const&) = delete; TypeSystem(TypeSystem const&) = delete;
TypeSystem const& operator=(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 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 std::string typeName(TypeExpression::Constructor _typeConstructor) const
{ {
// TODO: proper error handling // TODO: proper error handling

View 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;
}

View 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;
};
}

View File

@ -463,6 +463,8 @@ bool CompilerStack::analyze()
try try
{ {
bool experimentalSolidity = !m_sourceOrder.empty() && m_sourceOrder.front()->ast->experimentalSolidity();
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser); SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
for (Source const* source: m_sourceOrder) for (Source const* source: m_sourceOrder)
if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
@ -470,7 +472,7 @@ bool CompilerStack::analyze()
m_globalContext = make_shared<GlobalContext>(); m_globalContext = make_shared<GlobalContext>();
// We need to keep the same resolver during the whole process. // 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) for (Source const* source: m_sourceOrder)
if (source->ast && !resolver.registerDeclarations(*source->ast)) if (source->ast && !resolver.registerDeclarations(*source->ast))
return false; return false;
@ -496,7 +498,7 @@ bool CompilerStack::analyze()
if (source->ast && !resolver.resolveNamesAndTypes(*source->ast)) if (source->ast && !resolver.resolveNamesAndTypes(*source->ast))
return false; return false;
if (!m_sourceOrder.empty() && m_sourceOrder.front()->ast->experimentalSolidity()) if (experimentalSolidity)
{ {
if (!analyzeExperimental()) if (!analyzeExperimental())
noErrors = false; noErrors = false;

View File

@ -126,7 +126,10 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
nodes.push_back(parseEnumDefinition()); nodes.push_back(parseEnumDefinition());
break; break;
case Token::Type: case Token::Type:
nodes.push_back(parseUserDefinedValueTypeDefinition()); if (m_experimentalSolidityEnabledInCurrentSourceUnit)
nodes.push_back(parseTypeDefinition());
else
nodes.push_back(parseUserDefinedValueTypeDefinition());
break; break;
case Token::Using: case Token::Using:
nodes.push_back(parseUsingDirective()); nodes.push_back(parseUsingDirective());
@ -1716,47 +1719,16 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
auto [identifier, nameLocation] = expectIdentifierWithLocation(); auto [identifier, nameLocation] = expectIdentifierWithLocation();
ASTPointer<TypeName> type; ASTPointer<Expression> type;
vector<ASTPointer<IdentifierPath>> sorts;
if (m_scanner->currentToken() == Token::Colon) if (m_scanner->currentToken() == Token::Colon)
{ {
advance(); advance();
// TODO: handle this in parseTypeName()? type = parseExpression();
if (m_scanner->currentLiteral() == "_") nodeFactory.setEndPositionFromNode(type);
{
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());
}
} }
return nodeFactory.createNode<VariableDeclaration>( return nodeFactory.createNode<VariableDeclaration>(
type, nullptr,
identifier, identifier,
nameLocation, nameLocation,
nullptr, nullptr,
@ -1766,7 +1738,7 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
VariableDeclaration::Mutability::Mutable, VariableDeclaration::Mutability::Mutable,
nullptr, nullptr,
VariableDeclaration::Location::Unspecified, 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) ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
@ -2078,9 +2077,9 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression); ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression);
ASTNodeFactory nodeFactory(*this, expression); ASTNodeFactory nodeFactory(*this, expression);
int precedence = TokenTraits::precedence(m_scanner->currentToken()); int precedence = tokenPrecedence(m_scanner->currentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) while (tokenPrecedence(m_scanner->currentToken()) == precedence)
{ {
Token op = m_scanner->currentToken(); Token op = m_scanner->currentToken();
advance(); advance();
@ -2097,6 +2096,15 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
return expression; 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> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _partiallyParsedExpression ASTPointer<Expression> const& _partiallyParsedExpression
) )
@ -2555,7 +2563,14 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
while (m_scanner->currentToken() == Token::Period) while (m_scanner->currentToken() == Token::Period)
{ {
advance(); 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 else

View File

@ -178,6 +178,7 @@ private:
ASTPointer<VariableDeclaration> parsePostfixVariableDeclaration(); ASTPointer<VariableDeclaration> parsePostfixVariableDeclaration();
ASTPointer<TypeClassDefinition> parseTypeClassDefinition(); ASTPointer<TypeClassDefinition> parseTypeClassDefinition();
ASTPointer<TypeClassInstantiation> parseTypeClassInstantiation(); ASTPointer<TypeClassInstantiation> parseTypeClassInstantiation();
ASTPointer<TypeDefinition> parseTypeDefinition();
///@} ///@}
///@{ ///@{
@ -234,6 +235,8 @@ private:
bool isQuotedPath() const; bool isQuotedPath() const;
bool isStdlibPath() const; bool isStdlibPath() const;
int tokenPrecedence(Token _token) const;
ASTPointer<ASTString> getStdlibImportPathAndAdvance(); ASTPointer<ASTString> getStdlibImportPathAndAdvance();
/// Creates an empty ParameterList at the current location (used if parameters can be omitted). /// Creates an empty ParameterList at the current location (used if parameters can be omitted).

View File

@ -65,7 +65,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
Scoper::assignScopes(*sourceUnit); Scoper::assignScopes(*sourceUnit);
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
GlobalContext globalContext; 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()); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
solAssert(!Error::containsErrors(errorReporter.errors()), ""); solAssert(!Error::containsErrors(errorReporter.errors()), "");
resolver.registerDeclarations(*sourceUnit); resolver.registerDeclarations(*sourceUnit);

View File

@ -128,7 +128,7 @@ bytes compileFirstExpression(
GlobalContext globalContext; GlobalContext globalContext;
Scoper::assignScopes(*sourceUnit); Scoper::assignScopes(*sourceUnit);
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*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); resolver.registerDeclarations(*sourceUnit);
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());

View File

@ -30,15 +30,29 @@ function f(a:_:A) -> b:_:B {
return a; return a;
} }
type uint256 = word;
instantiation uint256 : A {
function testValue(x:uint256) -> y:word {
assembly {
y := 21
}
}
}
contract C { contract C {
fallback() external { fallback() external {
let x : word : A; let x : word : A;
let y; let y;
let z: (word, word);
let w: uint256;
assembly { assembly {
x := 0x42 x := 0x42
} }
z = f(z);
y = f(x); y = f(x);
y = B.testValue(x); y = B.testValue(x);
y = A.testValue(w);
assembly { assembly {
mstore(0, y) mstore(0, y)
return(0, 32) return(0, 32)
@ -48,4 +62,4 @@ contract C {
// ==== // ====
// compileViaYul: true // compileViaYul: true
// ---- // ----
// () -> 21 // () -> 0