diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 84945828d..552aa5d50 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -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; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 2fc12efb2..e4c854b2a 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -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) { diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 641f02280..76b65d35b 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -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(); for (Declaration const* declaration: _globalContext.declarations()) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 6ba591e8a..a6cf5ed76 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -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; }; /** diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 399c15b56..92c818353 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -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 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 suffixes{"slot", "offset", "length", "address", "selector"}; string suffix; for (string const& s: suffixes) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 512a681b4..a708ef810 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -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; }; } diff --git a/libsolidity/analysis/experimental/SyntaxRestrictor.h b/libsolidity/analysis/experimental/SyntaxRestrictor.h index fd5c75f58..a375b3380 100644 --- a/libsolidity/analysis/experimental/SyntaxRestrictor.h +++ b/libsolidity/analysis/experimental/SyntaxRestrictor.h @@ -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; }; diff --git a/libsolidity/analysis/experimental/TypeInference.cpp b/libsolidity/analysis/experimental/TypeInference.cpp index 48d5e2b31..7b91c71b2 100644 --- a/libsolidity/analysis/experimental/TypeInference.cpp +++ b/libsolidity/analysis/experimental/TypeInference.cpp @@ -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>); + 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(&_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(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(referencedDeclaration) && - !dynamic_cast(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(referencedDeclaration) && + !dynamic_cast(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(referencedDeclaration)) - identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false); - else if (dynamic_cast(referencedDeclaration)) - identifierAnnotation.type = declarationAnnotation.type; - else - solAssert(false); + solAssert(declarationAnnotation.type); + + if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true); + else if (dynamic_cast(referencedDeclaration)) + identifierAnnotation.type = declarationAnnotation.type; + else + solAssert(false); + break; + } + case ExpressionContext::Type: + { + if (referencedDeclaration) + { + if ( + !dynamic_cast(referencedDeclaration) && + !dynamic_cast(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(referencedDeclaration)) + identifierAnnotation.type = declarationAnnotation.type; + else if (dynamic_cast(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(referencedDeclaration); + if ( + !dynamic_cast(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>; + 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(referencedDeclaration) && - !dynamic_cast(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(referencedDeclaration)) - identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false); - else if (dynamic_cast(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(&_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 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 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; + } + + } diff --git a/libsolidity/analysis/experimental/TypeInference.h b/libsolidity/analysis/experimental/TypeInference.h index ed1e69289..6d33c8eb7 100644 --- a/libsolidity/analysis/experimental/TypeInference.h +++ b/libsolidity/analysis/experimental/TypeInference.h @@ -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; }; } diff --git a/libsolidity/analysis/experimental/TypeRegistration.cpp b/libsolidity/analysis/experimental/TypeRegistration.cpp index 166c29581..6550597f3 100644 --- a/libsolidity/analysis/experimental/TypeRegistration.cpp +++ b/libsolidity/analysis/experimental/TypeRegistration.cpp @@ -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(_node); diff --git a/libsolidity/analysis/experimental/TypeRegistration.h b/libsolidity/analysis/experimental/TypeRegistration.h index 0f6091bd1..33d8fb1be 100644 --- a/libsolidity/analysis/experimental/TypeRegistration.h +++ b/libsolidity/analysis/experimental/TypeRegistration.h @@ -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; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 3543e722b..1cd7b432a 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -1065,4 +1065,8 @@ TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const { return initAnnotation(); } +TypeDeclarationAnnotation& TypeDefinition::annotation() const +{ + return initAnnotation(); +} /// @} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8a4935ac5..8910e9b69 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1069,7 +1069,7 @@ public: Mutability _mutability = Mutability::Mutable, ASTPointer _overrides = nullptr, Location _referenceLocation = Location::Unspecified, - std::vector> _sort = {} + ASTPointer _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> const& sort() const { return m_sort; } + ASTPointer const& typeExpression() const { return m_typeExpression; } VariableDeclarationAnnotation& annotation() const override; protected: @@ -1160,7 +1160,7 @@ private: Mutability m_mutability = Mutability::Mutable; ASTPointer m_overrides; ///< Contains the override specifier node Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. - std::vector> m_sort; + ASTPointer 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> const& _argumentSorts, ASTPointer _class, std::vector> _subNodes - ): + ): ASTNode(_id, _location), m_typeConstructor(std::move(_typeConstructor)), m_argumentSorts(std::move(_argumentSorts)), @@ -2524,6 +2525,40 @@ private: std::vector> m_subNodes; }; +class TypeDefinition: public Declaration, public ScopeOpener +{ +public: + TypeDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _name, + SourceLocation _nameLocation, + ASTPointer _arguments, + ASTPointer _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 m_arguments; + ASTPointer m_typeExpression; +}; + /// @} } diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 79e6a4ecf..81d874de8 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -99,6 +99,12 @@ class ElementaryTypeNameExpression; class Literal; class StructuredDocumentation; +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition; +class TypeClassInstantiation; +/// @} + class VariableScope; template diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index ba044a378..d952973f3 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -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: diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 1d853cf5d..f0d7785ba 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -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); +} /// @} } diff --git a/libsolidity/ast/experimental/TypeSystem.cpp b/libsolidity/ast/experimental/TypeSystem.cpp index a833285e7..1d565e24d 100644 --- a/libsolidity/ast/experimental/TypeSystem.cpp +++ b/libsolidity/ast/experimental/TypeSystem.cpp @@ -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 _arguments) const +{ + return type(_builtinType, std::move(_arguments)); +} + +experimental::Type TypeSystem::type(TypeExpression::Constructor _constructor, std::vector _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) diff --git a/libsolidity/ast/experimental/TypeSystem.h b/libsolidity/ast/experimental/TypeSystem.h index 56f4dc09a..616276f7a 100644 --- a/libsolidity/ast/experimental/TypeSystem.h +++ b/libsolidity/ast/experimental/TypeSystem.h @@ -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 _arguments) const; + Type type(TypeExpression::Constructor _typeConstructor, std::vector _arguments) const; std::string typeName(TypeExpression::Constructor _typeConstructor) const { // TODO: proper error handling diff --git a/libsolidity/codegen/experimental/IRVariable.cpp b/libsolidity/codegen/experimental/IRVariable.cpp new file mode 100644 index 000000000..ae5a52743 --- /dev/null +++ b/libsolidity/codegen/experimental/IRVariable.cpp @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend::experimental; +using namespace solidity::util; + +template +Type getType(IRGenerationContext const& _context, Node const& _node) +{ + auto& annotation = _context.analysis.annotation(_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 IRVariable::stackSlots() const +{ + vector 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; +} diff --git a/libsolidity/codegen/experimental/IRVariable.h b/libsolidity/codegen/experimental/IRVariable.h new file mode 100644 index 000000000..dec77a581 --- /dev/null +++ b/libsolidity/codegen/experimental/IRVariable.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +#include +#include +#include + +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 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; +}; + + +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 0692e7637..739430490 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -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(); // 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; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 61b238480..ae55f237b 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -126,7 +126,10 @@ ASTPointer 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 Parser::parsePostfixVariableDeclaration() nodeFactory.markEndPosition(); auto [identifier, nameLocation] = expectIdentifierWithLocation(); - ASTPointer type; - vector> sorts; + ASTPointer 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( - type, + nullptr, identifier, nameLocation, nullptr, @@ -1766,7 +1738,7 @@ ASTPointer Parser::parsePostfixVariableDeclaration() VariableDeclaration::Mutability::Mutable, nullptr, VariableDeclaration::Location::Unspecified, - sorts + type ); } @@ -1868,6 +1840,33 @@ ASTPointer Parser::parseTypeClassInstantiation() ); } +ASTPointer Parser::parseTypeDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Type); + auto&& [name, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer arguments; + if (m_scanner->currentToken() == Token::LParen) + arguments = parseParameterList(); + + ASTPointer expression; + if (m_scanner->currentToken() == Token::Assign) + { + expectToken(Token::Assign); + expression = parseExpression(); + } + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode( + std::move(name), + std::move(nameLocation), + std::move(arguments), + std::move(expression) + ); +} + ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); @@ -2078,9 +2077,9 @@ ASTPointer Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer 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 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 Parser::parseUnaryExpression( ASTPointer 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(getLiteralAndAdvance())); + } + else + iap.path.push_back(parseIdentifierOrAddress()); } } else diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index c005d3e7e..da53b8a6b 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -178,6 +178,7 @@ private: ASTPointer parsePostfixVariableDeclaration(); ASTPointer parseTypeClassDefinition(); ASTPointer parseTypeClassInstantiation(); + ASTPointer parseTypeDefinition(); ///@} ///@{ @@ -234,6 +235,8 @@ private: bool isQuotedPath() const; bool isStdlibPath() const; + int tokenPrecedence(Token _token) const; + ASTPointer getStdlibImportPathAndAdvance(); /// Creates an empty ParameterList at the current location (used if parameters can be omitted). diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index f12c12ca9..bdbcccdf0 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -65,7 +65,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _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); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 1d9e03c9b..330f18127 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -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()); diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index 395b32a9c..a0c99e04b 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -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