Merge pull request #9892 from ethereum/constantsAtFileLevel

Constants at file-level.
This commit is contained in:
chriseth 2020-10-08 19:53:33 +02:00 committed by GitHub
commit 3970c4e57d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 405 additions and 57 deletions

View File

@ -1,6 +1,7 @@
### 0.7.4 (unreleased)
Important Bugfixes:
Language Features:
* Constants can be defined at file level.
Compiler Features:

View File

@ -1,5 +1,7 @@
.. index:: ! constant
.. _constants:
**************************************
Constant and Immutable State Variables
**************************************
@ -9,6 +11,8 @@ In both cases, the variables cannot be modified after the contract has been cons
For ``constant`` variables, the value has to be fixed at compile-time, while
for ``immutable``, it can still be assigned at construction time.
It is also possible to define ``constant`` variables at the file level.
The compiler does not reserve a storage slot for these variables, and every occurrence is
replaced by the respective value.
@ -26,10 +30,11 @@ Not all types for constants and immutables are implemented at this time. The onl
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.0;
pragma solidity >0.7.2;
uint constant X = 32**22 + 8;
contract C {
uint constant X = 32**22 + 8;
string constant TEXT = "abc";
bytes32 constant MY_HASH = keccak256("abc");
uint immutable decimals;

View File

@ -7,7 +7,7 @@ options { tokenVocab=SolidityLexer; }
/**
* On top level, Solidity allows pragmas, import directives, and
* definitions of contracts, interfaces, libraries, structs and enums.
* definitions of contracts, interfaces, libraries, structs, enums and constants.
*/
sourceUnit: (
pragmaDirective
@ -16,6 +16,7 @@ sourceUnit: (
| interfaceDefinition
| libraryDefinition
| functionDefinition
| constantVariableDeclaration
| structDefinition
| enumDefinition
)* EOF;
@ -240,6 +241,17 @@ locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean
(Assign initialValue=expression)?
Semicolon;
/**
* The declaration of a constant variable.
*/
constantVariableDeclaration
:
type=typeName
Constant
name=identifier
Assign initialValue=expression
Semicolon;
/**
* Parameter of an event.
*/

View File

@ -5,7 +5,8 @@ Layout of a Solidity Source File
Source files can contain an arbitrary number of
:ref:`contract definitions<contract_structure>`, import_ directives,
:ref:`pragma directives<pragma>` and
:ref:`struct<structs>`, :ref:`enum<enums>` and :ref:`function<functions>` definitions.
:ref:`struct<structs>`, :ref:`enum<enums>`, :ref:`function<functions>`
and :ref:`constant variable<constants>` definitions.
.. index:: ! license, spdx

View File

@ -105,7 +105,7 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
if (!_ref.multiline)
{
auto const locationLength = static_cast<size_t>(_ref.endColumn - _ref.startColumn);
size_t const locationLength = static_cast<size_t>(_ref.endColumn - _ref.startColumn);
// line 1:
m_stream << leftpad << ' ';
@ -124,7 +124,11 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
frameColored() << '|';
m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast<size_t>(_ref.startColumn)), ' ');
diagColored() << replaceNonTabs(text.substr(static_cast<size_t>(_ref.startColumn), locationLength), '^');
diagColored() << (
locationLength == 0 ?
"^" :
replaceNonTabs(text.substr(static_cast<size_t>(_ref.startColumn), locationLength), '^')
);
m_stream << '\n';
}
else

View File

@ -276,11 +276,17 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
if (_variable.annotation().type)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
if (_variable.isFileLevelVariable() && !_variable.isConstant())
m_errorReporter.declarationError(
8342_error,
_variable.location(),
"Only constant variables are allowed at file level."
);
if (_variable.isConstant() && (!_variable.isStateVariable() && !_variable.isFileLevelVariable()))
m_errorReporter.declarationError(
1788_error,
_variable.location(),
"The \"constant\" keyword can only be used for state variables."
"The \"constant\" keyword can only be used for state variables or variables at file level."
);
if (_variable.immutable() && !_variable.isStateVariable())
m_errorReporter.declarationError(
@ -344,6 +350,11 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
solAssert(varLoc == Location::Unspecified, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isFileLevelVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");

View File

@ -87,7 +87,7 @@ bool DocStringAnalyser::visit(FunctionDefinition const& _function)
bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
{
if (!_variable.isStateVariable())
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
return false;
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))

View File

@ -61,13 +61,13 @@ bool DocStringTagParser::visit(VariableDeclaration const& _variable)
{
if (_variable.isStateVariable())
{
static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "inheritdoc"};
static set<string> const validNonPublicTags = set<string>{"dev", "inheritdoc"};
if (_variable.isPublic())
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables");
parseDocStrings(_variable, _variable.annotation(), {"dev", "notice", "return", "inheritdoc"}, "public state variables");
else
parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables");
parseDocStrings(_variable, _variable.annotation(), {"dev", "inheritdoc"}, "non-public state variables");
}
else if (_variable.isFileLevelVariable())
parseDocStrings(_variable, _variable.annotation(), {"dev"}, "file-level variables");
return false;
}

View File

@ -130,9 +130,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(SourceUnit& _source)
try
{
for (shared_ptr<ASTNode> const& node: _source.nodes())
{
setScope(&_source);
if (!resolveNamesAndTypesInternal(*node, true))
return false;
}
}
catch (langutil::FatalError const&)
{
if (m_errorReporter.errors().empty())

View File

@ -38,6 +38,13 @@ bool PostTypeChecker::check(ASTNode const& _astRoot)
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool PostTypeChecker::finalize()
{
for (auto& checker: m_checkers)
checker->finalize();
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool PostTypeChecker::visit(ContractDefinition const& _contractDefinition)
{
return callVisit(_contractDefinition);
@ -83,6 +90,11 @@ bool PostTypeChecker::visit(Identifier const& _identifier)
return callVisit(_identifier);
}
bool PostTypeChecker::visit(MemberAccess const& _memberAccess)
{
return callVisit(_memberAccess);
}
bool PostTypeChecker::visit(StructDefinition const& _struct)
{
return callVisit(_struct);
@ -110,14 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
ConstStateVarCircularReferenceChecker(ErrorReporter& _errorReporter):
Checker(_errorReporter) {}
bool visit(ContractDefinition const&) override
{
solAssert(!m_currentConstVariable, "");
solAssert(m_constVariableDependencies.empty(), "");
return true;
}
void endVisit(ContractDefinition const&) override
void finalize() override
{
solAssert(!m_currentConstVariable, "");
for (auto declaration: m_constVariables)
@ -128,9 +133,12 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
"The value of the constant " + declaration->name() +
" has a cyclic dependency via " + identifier->name() + "."
);
}
m_constVariables.clear();
m_constVariableDependencies.clear();
bool visit(ContractDefinition const&) override
{
solAssert(!m_currentConstVariable, "");
return true;
}
bool visit(VariableDeclaration const& _variable) override
@ -162,6 +170,15 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
return true;
}
bool visit(MemberAccess const& _memberAccess) override
{
if (m_currentConstVariable)
if (auto var = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
if (var->isConstant())
m_constVariableDependencies[m_currentConstVariable].insert(var);
return true;
}
VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom)
{
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)

View File

@ -35,7 +35,7 @@ namespace solidity::frontend
/**
* This module performs analyses on the AST that are done after type checking and assignments of types:
* - whether there are circular references in constant state variables
* - whether there are circular references in constant variables
* - whether override specifiers are actually contracts
* - whether a modifier is in a function header
* - whether an event is used outside of an emit statement
@ -54,6 +54,9 @@ public:
{
Checker(langutil::ErrorReporter& _errorReporter):
m_errorReporter(_errorReporter) {}
/// Called after all source units have been visited.
virtual void finalize() {}
protected:
langutil::ErrorReporter& m_errorReporter;
};
@ -63,6 +66,9 @@ public:
bool check(ASTNode const& _astRoot);
/// Called after all source units have been visited.
bool finalize();
private:
bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
@ -77,6 +83,7 @@ private:
bool visit(FunctionCall const& _functionCall) override;
bool visit(Identifier const& _identifier) override;
bool visit(MemberAccess const& _identifier) override;
bool visit(StructDefinition const& _struct) override;
void endVisit(StructDefinition const& _struct) override;

View File

@ -647,6 +647,11 @@ bool VariableDeclaration::isStateVariable() const
return dynamic_cast<ContractDefinition const*>(scope());
}
bool VariableDeclaration::isFileLevelVariable() const
{
return dynamic_cast<SourceUnit const*>(scope());
}
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
{
using Location = VariableDeclaration::Location;

View File

@ -967,6 +967,7 @@ public:
/// Can only be called after reference resolution.
bool hasReferenceOrMappingType() const;
bool isStateVariable() const;
bool isFileLevelVariable() const;
bool isIndexed() const { return m_isIndexed; }
Mutability mutability() const { return m_mutability; }
bool isConstant() const { return m_mutability == Mutability::Constant; }

View File

@ -1753,18 +1753,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
Type::Category category = _memberAccess.annotation().type->category();
solAssert(
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
category == Type::Category::TypeType ||
category == Type::Category::Module ||
category == Type::Category::Function,
category == Type::Category::Module,
""
);
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
{
auto const* funDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(funDef && funDef->isFree(), "");
solAssert(variable->isConstant(), "");
appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
}
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
{
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
solAssert(function && function->isFree(), "");
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
utils().pushCombinedFunctionEntryLabel(*funDef);
utils().pushCombinedFunctionEntryLabel(*function);
}
break;
}

View File

@ -1871,6 +1871,35 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(false, "");
break;
}
case Type::Category::Module:
{
Type::Category category = _memberAccess.annotation().type->category();
solAssert(
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
category == Type::Category::TypeType ||
category == Type::Category::Module,
""
);
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
{
solAssert(variable->isConstant(), "");
handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
}
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
{
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
solAssert(function && function->isFree(), "");
solAssert(function->functionType(true), "");
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, "");
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
define(_memberAccess) << to_string(function->id()) << "\n";
m_context.internalFunctionAccessed(_memberAccess, *function);
}
break;
}
default:
solAssert(false, "Member access to unknown type.");
}
@ -2130,6 +2159,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
{
// no-op
}
else if (dynamic_cast<ImportDirective const*>(declaration))
{
// no-op
}
else
{
solAssert(false, "Identifier type not expected in expression context.");
@ -2162,7 +2195,7 @@ void IRGeneratorForStatements::handleVariableReference(
)
{
setLocation(_referencingExpression);
if (_variable.isStateVariable() && _variable.isConstant())
if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant())
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
else if (_variable.isStateVariable() && _variable.immutable())
setLValue(_referencingExpression, IRLValue{

View File

@ -387,6 +387,8 @@ bool CompilerStack::analyze()
for (Source const* source: m_sourceOrder)
if (source->ast && !postTypeChecker.check(*source->ast))
noErrors = false;
if (!postTypeChecker.finalize())
noErrors = false;
}
// Check that immutable variables are never read in c'tors and assigned

View File

@ -111,7 +111,17 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
nodes.push_back(parseFunctionDefinition(true));
break;
default:
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition.");
// Constant variable.
if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS)
{
VarDeclParserOptions options;
options.kind = VarDeclKind::FileLevel;
options.allowInitialValue = true;
nodes.push_back(parseVariableDeclaration(options));
expectToken(Token::Semicolon);
}
else
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.");
}
}
solAssert(m_recursionDepth == 0, "");
@ -332,15 +342,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
subNodes.push_back(parseStructDefinition());
else if (currentTokenValue == Token::Enum)
subNodes.push_back(parseEnumDefinition());
else if (
currentTokenValue == Token::Identifier ||
currentTokenValue == Token::Mapping ||
TokenTraits::isElementaryTypeName(currentTokenValue) ||
(currentTokenValue == Token::Function && m_scanner->peekNextToken() == Token::LParen)
)
else if (variableDeclarationStart())
{
VarDeclParserOptions options;
options.isStateVariable = true;
options.kind = VarDeclKind::State;
options.allowInitialValue = true;
subNodes.push_back(parseVariableDeclaration(options));
expectToken(Token::Semicolon);
@ -687,10 +692,10 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
ASTPointer<TypeName> type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName();
nodeFactory.setEndPositionFromNode(type);
if (!_options.isStateVariable && documentation != nullptr)
parserError(2837_error, "Only state variables can have a docstring.");
if (_options.kind == VarDeclKind::Other && documentation != nullptr)
parserError(2837_error, "Only state variables or file-level variables can have a docstring.");
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.kind == VarDeclKind::State && m_scanner->currentToken() == Token::LBrace)
fatalParserError(
2915_error,
"Expected a state variable declaration. If you intended this as a fallback function "
@ -708,7 +713,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
while (true)
{
Token token = m_scanner->currentToken();
if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token))
if (_options.kind == VarDeclKind::State && TokenTraits::isVariableVisibilitySpecifier(token))
{
nodeFactory.markEndPosition();
if (visibility != Visibility::Default)
@ -724,7 +729,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else
visibility = parseVisibilitySpecifier();
}
else if (_options.isStateVariable && token == Token::Override)
else if (_options.kind == VarDeclKind::State && token == Token::Override)
{
if (overrides)
parserError(9125_error, "Override already specified.");
@ -1928,6 +1933,16 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
return ret;
}
bool Parser::variableDeclarationStart()
{
Token currentToken = m_scanner->currentToken();
return
currentToken == Token::Identifier ||
currentToken == Token::Mapping ||
TokenTraits::isElementaryTypeName(currentToken) ||
(currentToken == Token::Function && m_scanner->peekNextToken() == Token::LParen);
}
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
{
// We circumvent the scanner here, because it skips non-docstring comments.

View File

@ -52,13 +52,13 @@ public:
private:
class ASTNodeFactory;
enum class VarDeclKind { FileLevel, State, Other };
struct VarDeclParserOptions
{
// This is actually not needed, but due to a defect in the C++ standard, we have to.
// https://stackoverflow.com/questions/17430377
VarDeclParserOptions() {}
bool isStateVariable = false;
VarDeclKind kind = VarDeclKind::Other;
bool allowIndexed = false;
bool allowEmptyName = false;
bool allowInitialValue = false;
@ -155,6 +155,9 @@ private:
///@{
///@name Helper functions
/// @return true if we are at the start of a variable declaration.
bool variableDeclarationStart();
/// Used as return value of @see peekStatementType.
enum class LookAheadInfo
{

View File

@ -1,4 +1,4 @@
Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
Error: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
--> recovery_ast_empty_contract/input.sol:3:1:
|
3 | c

View File

@ -1,4 +1,6 @@
pragma solidity ^99.99.0;
this is surely invalid
// ----
// ParserError 7858: (26-30): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
// ParserError 6635: (31-33): Expected identifier but got 'is'
// ParserError 6635: (34-40): Expected ';' but got identifier
// ParserError 6635: (49-49): Expected ';' but got end of source

View File

@ -0,0 +1,9 @@
address constant e = 0x1212121212121212121212121000002134593163;
contract C {
function f() public returns (address z) {
assembly { z := e }
}
}
// ----
// f() -> 0x1212121212121212121212121000002134593163

View File

@ -0,0 +1,33 @@
bytes constant a = "\x03\x01\x02";
bytes constant b = hex"030102";
string constant c = "hello";
uint256 constant x = 56;
enum ActionChoices {GoLeft, GoRight, GoStraight, Sit}
ActionChoices constant choices = ActionChoices.GoRight;
bytes32 constant st = "abc\x00\xff__";
contract C {
function f() public returns (bytes memory) {
return a;
}
function g() public returns (bytes memory) {
return b;
}
function h() public returns (bytes memory) {
return bytes(c);
}
function i() public returns (uint, ActionChoices, bytes32) {
return (x, choices, st);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x20, 3, "\x03\x01\x02"
// g() -> 0x20, 3, "\x03\x01\x02"
// h() -> 0x20, 5, "hello"
// i() -> 0x38, 1, 0x61626300ff5f5f00000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,41 @@
==== Source: s1.sol ====
bytes constant a = b;
bytes constant b = hex"030102";
function fre() pure returns (bytes memory) {
return a;
}
==== Source: s2.sol ====
import "s1.sol";
uint256 constant c = uint8(a[0]) + 2;
contract C {
function f() public returns (bytes memory) {
return a;
}
function g() public returns (bytes memory) {
return b;
}
function h() public returns (uint) {
return c;
}
function i() public returns (bytes memory) {
return fre();
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x20, 3, "\x03\x01\x02"
// g() -> 0x20, 3, "\x03\x01\x02"
// h() -> 5
// i() -> 0x20, 3, "\x03\x01\x02"

View File

@ -0,0 +1,26 @@
==== Source: s1.sol ====
uint constant a = 89;
function fre() pure returns (uint) {
return a;
}
==== Source: s2.sol ====
import {a as b, fre} from "s1.sol";
import "s1.sol" as M;
uint256 constant a = 13;
contract C {
function f() public returns (uint, uint, uint, uint) {
return (a, fre(), M.a, b);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x0d, 0x59, 0x59, 0x59

View File

@ -0,0 +1,15 @@
==== Source: a.sol ====
function f(uint) pure returns (uint) { return 7; }
function f(bytes memory x) pure returns (uint) { return x.length; }
==== Source: b.sol ====
import "a.sol" as M;
contract C {
function f() public pure returns (uint, uint) {
return (M.f(2), M.f("abc"));
}
}
// ====
// compileViaYul: also
// ----
// f() -> 7, 3

View File

@ -4,6 +4,6 @@ contract C {
}
}
// ----
// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables.
// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables or variables at file level.
// TypeError 5462: (69-72): Invalid array length, expected integer literal or constant expression.
// TypeError 6651: (64-75): Data location must be "storage", "memory" or "calldata" for variable, but none was given.

View File

@ -0,0 +1,3 @@
/// @dev Documentation
uint constant x = 8;
// ----

View File

@ -0,0 +1,4 @@
/// Documentation
uint constant x = 8;
// ----
// DocstringParsingError 6546: (0-18): Documentation tag @notice not valid for file-level variables.

View File

@ -0,0 +1,3 @@
uint constant override x = 2;
// ----
// ParserError 2314: (14-22): Expected identifier but got 'override'

View File

@ -0,0 +1,3 @@
uint constant x;
// ----
// TypeError 4266: (0-15): Uninitialized "constant" variable.

View File

@ -0,0 +1,3 @@
uint constant virtual x;
// ----
// ParserError 2314: (14-21): Expected identifier but got 'virtual'

View File

@ -0,0 +1,3 @@
uint public constant x = 7;
// ----
// ParserError 2314: (5-11): Expected identifier but got 'public'

View File

@ -0,0 +1,3 @@
uint constant public y = 7;
// ----
// ParserError 2314: (14-20): Expected identifier but got 'public'

View File

@ -0,0 +1,15 @@
==== Source: a ====
import "b";
uint constant c = d;
==== Source: b ====
import "a";
uint constant b = c;
uint constant d = b;
contract C {
uint constant a = b;
}
// ----
// TypeError 6161: (b:12-31): The value of the constant b has a cyclic dependency via c.
// TypeError 6161: (b:33-52): The value of the constant d has a cyclic dependency via b.
// TypeError 6161: (b:71-90): The value of the constant a has a cyclic dependency via b.
// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d.

View File

@ -0,0 +1,15 @@
==== Source: a ====
import "b";
uint constant c = d;
==== Source: b ====
import "a" as M;
uint constant b = M.c;
uint constant d = b;
contract C {
uint constant a = b;
}
// ----
// TypeError 6161: (b:17-38): The value of the constant b has a cyclic dependency via c.
// TypeError 6161: (b:40-59): The value of the constant d has a cyclic dependency via b.
// TypeError 6161: (b:78-97): The value of the constant a has a cyclic dependency via b.
// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d.

View File

@ -0,0 +1,3 @@
uint[] memory constant x = 2;
// ----
// ParserError 2314: (7-13): Expected identifier but got 'memory'

View File

@ -0,0 +1,3 @@
uint[] constant memory x = 2;
// ----
// ParserError 2314: (16-22): Expected identifier but got 'memory'

View File

@ -0,0 +1,4 @@
uint immutable x = 7;
// ----
// DeclarationError 8342: (0-20): Only constant variables are allowed at file level.
// DeclarationError 8297: (0-20): The "immutable" keyword can only be used for state variables.

View File

@ -0,0 +1,3 @@
mapping(uint => uint) constant b = b;
// ----
// TypeError 9259: (0-36): Constants of non-value type not yet implemented.

View File

@ -0,0 +1,7 @@
==== Source: a ====
uint constant c = 7;
==== Source: b ====
import {c as d} from "a";
uint constant d = 7;
// ----
// DeclarationError 2333: (b:26-45): Identifier already declared.

View File

@ -0,0 +1,3 @@
uint x = 7;
// ----
// DeclarationError 8342: (0-10): Only constant variables are allowed at file level.

View File

@ -0,0 +1,8 @@
==== Source: a ====
import "b";
uint constant c = 7;
==== Source: b ====
import "a";
uint constant c = 7;
// ----
// DeclarationError 2333: (b:12-31): Identifier already declared.

View File

@ -0,0 +1,4 @@
uint constant c = 7;
uint constant c = 8;
// ----
// DeclarationError 2333: (21-40): Identifier already declared.

View File

@ -0,0 +1,5 @@
struct S { uint x; }
S constant s;
// ----
// TypeError 9259: (21-33): Constants of non-value type not yet implemented.
// TypeError 4266: (21-33): Uninitialized "constant" variable.

View File

@ -1,3 +1,3 @@
constructor() {}
// ----
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.

View File

@ -1,3 +1,3 @@
fallback(){}
// ----
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.

View File

@ -1,3 +1,3 @@
receive() {}
// ----
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.

View File

@ -2,4 +2,4 @@ contract test {
function f(uint[] memory constant a) public { }
}
// ----
// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables.
// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables or variables at file level.

View File

@ -0,0 +1,4 @@
uint constant c = 7;
function c() returns (uint) {}
// ----
// DeclarationError 2333: (21-51): Identifier already declared.

View File

@ -11,4 +11,4 @@ contract C {
}
}
// ----
// ParserError 2837: (290-295): Only state variables can have a docstring.
// ParserError 2837: (290-295): Only state variables or file-level variables can have a docstring.

View File

@ -2,4 +2,4 @@ contract Foo {
function f(uint[] storage constant x, uint[] memory y) internal { }
}
// ----
// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables.
// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables or variables at file level.

View File

@ -1,3 +1,3 @@
unexpected
// ----
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.