Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-03-24 13:35:41 +01:00
commit 7d68f9f6c3
856 changed files with 12967 additions and 8148 deletions

View File

@ -122,6 +122,10 @@ defaults:
name: command line tests
command: ./test/cmdlineTests.sh
- run_docs_version_pragma_check: &run_docs_version_pragma_check
name: docs version pragma check
command: ./scripts/docs_version_pragma_check.sh
- test_ubuntu1604_clang: &test_ubuntu1604_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1604-clang-ossfuzz-<< pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image-rev >>
@ -303,6 +307,18 @@ jobs:
name: Linting Python Scripts
command: ./scripts/pylint_all.py
chk_antlr_grammar:
docker:
- image: buildpack-deps:eoan
steps:
- checkout
- run:
name: Install Java
command: apt -q update && apt install -y openjdk-14-jdk
- run:
name: Run tests
command: ./scripts/test_antlr_grammar.sh
chk_buglist:
docker:
- image: circleci/node
@ -605,6 +621,7 @@ jobs:
- attach_workspace:
at: build
- run: *run_cmdline_tests
- run: *run_docs_version_pragma_check
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
@ -768,6 +785,7 @@ workflows:
- chk_buglist: *workflow_trigger_on_tags
- chk_proofs: *workflow_trigger_on_tags
- chk_pylint: *workflow_trigger_on_tags
- chk_antlr_grammar: *workflow_trigger_on_tags
# build-only
- b_docs: *workflow_trigger_on_tags

View File

@ -6,26 +6,35 @@
# Note that clang-format cannot express the style that closing parentheses
# behave similar to closing curly braces in a multi-line setting in that
# they have to be on a line of their own at the same indentation level
# as the opening part.
# as the opening part (aka "dangling parenthesis", see https://reviews.llvm.org/D33029).
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignEscapedNewlinesLeft: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Allman
ColumnLimit: 120
ContinuationIndentWidth: 4
FixNamespaceComments: false
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
PenaltyBreakBeforeFirstCallParameter: 2000
PointerAlignment: Left
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
TabWidth: 4
UseTab: ForIndentation
UseTab: Always
# Local Variables:
# mode: yaml

View File

@ -141,7 +141,7 @@ struct MeanSigma
double const d = 0;
int i = 0;
int j = 0;
char* s;
char* s = nullptr;
MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
Derived* x = dynamic_cast<Derived*>(base);
for (auto i = x->begin(); i != x->end(); ++i) {}

View File

@ -18,7 +18,9 @@ Compiler Features:
Bugfixes:
* Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks.
* Inline Assembly: Fix internal error when accessing incorrect constant variables.
* Inheritance: Allow public state variables to override functions with dynamic memory types in their return values.
* JSON AST: Always add pointer suffix for memory reference types.
### 0.6.4 (2020-03-10)
@ -143,6 +145,12 @@ Compiler Features:
* ABIEncoderV2: Do not warn about enabled ABIEncoderV2 anymore (the pragma is still needed, though).
### 0.5.17 (2020-03-17)
Bugfixes:
* Type Checker: Disallow overriding of private functions.
### 0.5.16 (2020-01-02)
Backported Bugfixes:

View File

@ -292,8 +292,9 @@ Consider you have the following pre-0.5.0 contract already deployed:
::
// This will not compile with the current version of the compiler
pragma solidity ^0.4.25;
// This will report a warning until version 0.4.25 of the compiler
// This will not compile after 0.5.0
contract OldContract {
function someOldFunction(uint8 a) {
//...
@ -369,8 +370,8 @@ Old version:
::
// This will not compile
pragma solidity ^0.4.25;
// This will not compile after 0.5.0
contract OtherContract {
uint x;
@ -396,7 +397,7 @@ Old version:
// Throw is fine in this version.
if (x > 100)
throw;
bytes b = new bytes(x);
bytes memory b = new bytes(x);
y = -3 >> 1;
// y == -1 (wrong, should be -2)
do {
@ -431,14 +432,15 @@ New version:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.5.0 <0.5.99;
// This will not compile after 0.6.0
contract OtherContract {
uint x;
function f(uint y) external {
x = y;
}
receive() payable external {}
function() payable external {}
}
contract New {

482
docs/Solidity.g4 Normal file
View File

@ -0,0 +1,482 @@
// Copyright 2020 Gonçalo Sá <goncalo.sa@consensys.net>
// Copyright 2016-2019 Federico Bond <federicobond@gmail.com>
// Licensed under the MIT license. See LICENSE file in the project root for details.
// This grammar is much less strict than what Solidity currently parses
// to allow this to pass with older versions of Solidity.
grammar Solidity;
sourceUnit
: (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ;
pragmaDirective
: 'pragma' pragmaName pragmaValue ';' ;
pragmaName
: identifier ;
pragmaValue
: version | expression ;
version
: versionConstraint versionConstraint? ;
versionConstraint
: versionOperator? VersionLiteral ;
versionOperator
: '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ;
importDirective
: 'import' StringLiteralFragment ('as' identifier)? ';'
| 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';'
| 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ;
importDeclaration
: identifier ('as' identifier)? ;
contractDefinition
: 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier
( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )?
'{' contractPart* '}' ;
inheritanceSpecifier
: userDefinedTypeName ( '(' expressionList? ')' )? ;
contractPart
: stateVariableDeclaration
| usingForDeclaration
| structDefinition
| modifierDefinition
| functionDefinition
| eventDefinition
| enumDefinition ;
stateVariableDeclaration
: typeName
( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )*
identifier ('=' expression)? ';' ;
overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ;
usingForDeclaration
: 'using' identifier 'for' ('*' | typeName) ';' ;
structDefinition
: 'struct' identifier
'{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ;
modifierDefinition
: 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* block ;
functionDefinition
: functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ;
functionDescriptor
: 'function' ( identifier | ReceiveKeyword | FallbackKeyword )?
| ConstructorKeyword
| FallbackKeyword
| ReceiveKeyword ;
returnParameters
: 'returns' parameterList ;
modifierList
: ( modifierInvocation | stateMutability | ExternalKeyword
| PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ;
modifierInvocation
: identifier ( '(' expressionList? ')' )? ;
eventDefinition
: 'event' identifier eventParameterList AnonymousKeyword? ';' ;
enumDefinition
: 'enum' identifier '{' enumValue? (',' enumValue)* '}' ;
enumValue
: identifier ;
parameterList
: '(' ( parameter (',' parameter)* )? ')' ;
parameter
: typeName storageLocation? identifier? ;
eventParameterList
: '(' ( eventParameter (',' eventParameter)* )? ')' ;
eventParameter
: typeName IndexedKeyword? identifier? ;
variableDeclaration
: typeName storageLocation? identifier ;
typeName
: elementaryTypeName
| userDefinedTypeName
| mapping
| typeName '[' expression? ']'
| functionTypeName ;
userDefinedTypeName
: identifier ( '.' identifier )* ;
mapping
: 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ;
functionTypeName
: 'function' parameterList modifierList returnParameters? ;
storageLocation
: 'memory' | 'storage' | 'calldata';
stateMutability
: PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ;
block
: '{' statement* '}' ;
statement
: ifStatement
| tryStatement
| whileStatement
| forStatement
| block
| inlineAssemblyStatement
| doWhileStatement
| continueStatement
| breakStatement
| returnStatement
| throwStatement
| emitStatement
| simpleStatement ;
expressionStatement
: expression ';' ;
ifStatement
: 'if' '(' expression ')' statement ( 'else' statement )? ;
tryStatement : 'try' expression returnParameters? block catchClause+ ;
// In reality catch clauses still are not processed as below
// the identifier can only be a set string: "Error". But plans
// of the Solidity team include possible expansion so we'll
// leave this as is, befitting with the Solidity docs.
catchClause : 'catch' ( identifier? parameterList )? block ;
whileStatement
: 'while' '(' expression ')' statement ;
forStatement
: 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ;
simpleStatement
: ( variableDeclarationStatement | expressionStatement ) ;
inlineAssemblyStatement
: 'assembly' StringLiteralFragment? assemblyBlock ;
doWhileStatement
: 'do' statement 'while' '(' expression ')' ';' ;
continueStatement
: 'continue' ';' ;
breakStatement
: 'break' ';' ;
returnStatement
: 'return' expression? ';' ;
// throw is no longer supported by latest Solidity.
throwStatement
: 'throw' ';' ;
emitStatement
: 'emit' functionCall ';' ;
// 'var' is no longer supported by latest Solidity.
variableDeclarationStatement
: ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';';
variableDeclarationList
: variableDeclaration? (',' variableDeclaration? )* ;
identifierList
: '(' ( identifier? ',' )* identifier? ')' ;
elementaryTypeName
: 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ;
Int
: 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ;
Uint
: 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ;
Byte
: 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ;
Fixed
: 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ;
Ufixed
: 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ;
expression
: expression ('++' | '--')
| 'new' typeName
| expression '[' expression? ']'
| expression '[' expression? ':' expression? ']'
| expression '.' identifier
| expression '{' nameValueList '}'
| expression '(' functionCallArguments ')'
| PayableKeyword '(' expression ')'
| '(' expression ')'
| ('++' | '--') expression
| ('+' | '-') expression
| ('after' | 'delete') expression
| '!' expression
| '~' expression
| expression '**' expression
| expression ('*' | '/' | '%') expression
| expression ('+' | '-') expression
| expression ('<<' | '>>') expression
| expression '&' expression
| expression '^' expression
| expression '|' expression
| expression ('<' | '>' | '<=' | '>=') expression
| expression ('==' | '!=') expression
| expression '&&' expression
| expression '||' expression
| expression '?' expression ':' expression
| expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression
| primaryExpression ;
primaryExpression
: BooleanLiteral
| numberLiteral
| hexLiteral
| stringLiteral
| identifier ('[' ']')?
| TypeKeyword
| tupleExpression
| typeNameExpression ('[' ']')? ;
expressionList
: expression (',' expression)* ;
nameValueList
: nameValue (',' nameValue)* ','? ;
nameValue
: identifier ':' expression ;
functionCallArguments
: '{' nameValueList? '}'
| expressionList? ;
functionCall
: expression '(' functionCallArguments ')' ;
tupleExpression
: '(' ( expression? ( ',' expression? )* ) ')'
| '[' ( expression ( ',' expression )* )? ']' ;
typeNameExpression
: elementaryTypeName
| userDefinedTypeName ;
assemblyItem
: identifier
| assemblyBlock
| assemblyExpression
| assemblyLocalDefinition
| assemblyAssignment
| assemblyStackAssignment
| labelDefinition
| assemblySwitch
| assemblyFunctionDefinition
| assemblyFor
| assemblyIf
| BreakKeyword
| ContinueKeyword
| LeaveKeyword
| subAssembly
| numberLiteral
| stringLiteral
| hexLiteral ;
assemblyBlock
: '{' assemblyItem* '}' ;
assemblyExpression
: assemblyCall | assemblyLiteral ;
assemblyCall
: ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ;
assemblyLocalDefinition
: 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ;
assemblyAssignment
: assemblyIdentifierList ':=' assemblyExpression ;
assemblyIdentifierList
: identifier ( ',' identifier )* ;
assemblyStackAssignment
: '=:' identifier ;
labelDefinition
: identifier ':' ;
assemblySwitch
: 'switch' assemblyExpression assemblyCase* ;
assemblyCase
: 'case' assemblyLiteral assemblyType? assemblyBlock
| 'default' assemblyBlock ;
assemblyFunctionDefinition
: 'function' identifier '(' assemblyTypedVariableList? ')'
assemblyFunctionReturns? assemblyBlock ;
assemblyFunctionReturns
: ( '-' '>' assemblyTypedVariableList ) ;
assemblyFor
: 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ;
assemblyIf
: 'if' assemblyExpression assemblyBlock ;
assemblyLiteral
: ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ;
assemblyTypedVariableList
: identifier assemblyType? ( ',' assemblyTypedVariableList )? ;
assemblyType
: ':' identifier ;
subAssembly
: 'assembly' identifier assemblyBlock ;
numberLiteral
: (DecimalNumber | HexNumber) NumberUnit? ;
identifier
: ('from' | 'calldata' | 'address' | Identifier) ;
BooleanLiteral
: 'true' | 'false' ;
DecimalNumber
: ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ;
fragment
DecimalDigits
: [0-9] ( '_'? [0-9] )* ;
HexNumber
: '0' [xX] HexDigits ;
fragment
HexDigits
: HexCharacter ( '_'? HexCharacter )* ;
NumberUnit
: 'wei' | 'szabo' | 'finney' | 'ether'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ;
HexLiteralFragment
: 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ;
hexLiteral : HexLiteralFragment+ ;
fragment
HexPair
: HexCharacter HexCharacter ;
fragment
HexCharacter
: [0-9A-Fa-f] ;
ReservedKeyword
: 'after'
| 'case'
| 'default'
| 'final'
| 'in'
| 'inline'
| 'let'
| 'match'
| 'null'
| 'of'
| 'relocatable'
| 'static'
| 'switch'
| 'typeof' ;
AnonymousKeyword : 'anonymous' ;
BreakKeyword : 'break' ;
ConstantKeyword : 'constant' ;
ImmutableKeyword : 'immutable' ;
ContinueKeyword : 'continue' ;
LeaveKeyword : 'leave' ;
ExternalKeyword : 'external' ;
IndexedKeyword : 'indexed' ;
InternalKeyword : 'internal' ;
PayableKeyword : 'payable' ;
PrivateKeyword : 'private' ;
PublicKeyword : 'public' ;
VirtualKeyword : 'virtual' ;
PureKeyword : 'pure' ;
TypeKeyword : 'type' ;
ViewKeyword : 'view' ;
ConstructorKeyword : 'constructor' ;
FallbackKeyword : 'fallback' ;
ReceiveKeyword : 'receive' ;
Identifier
: IdentifierStart IdentifierPart* ;
fragment
IdentifierStart
: [a-zA-Z$_] ;
fragment
IdentifierPart
: [a-zA-Z0-9$_] ;
stringLiteral
: StringLiteralFragment+ ;
StringLiteralFragment
: '"' DoubleQuotedStringCharacter* '"'
| '\'' SingleQuotedStringCharacter* '\'' ;
fragment
DoubleQuotedStringCharacter
: ~["\r\n\\] | ('\\' .) ;
fragment
SingleQuotedStringCharacter
: ~['\r\n\\] | ('\\' .) ;
VersionLiteral
: [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ;
WS
: [ \t\r\n\u000C]+ -> skip ;
COMMENT
: '/*' .*? '*/' -> channel(HIDDEN) ;
LINE_COMMENT
: '//' ~[\r\n]* -> channel(HIDDEN) ;

View File

@ -234,7 +234,6 @@ Given the contract:
pragma solidity >=0.4.16 <0.8.0;
contract Foo {
function bar(bytes3[2] memory) public pure {}
function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
@ -583,12 +582,11 @@ As an example, the code
pragma solidity >=0.4.19 <0.8.0;
pragma experimental ABIEncoderV2;
contract Test {
struct S { uint a; uint[] b; T[] c; }
struct T { uint x; uint y; }
function f(S memory s, T memory t, uint a) public {}
function g() public returns (S memory s, T memory t, uint a) {}
function f(S memory, T memory, uint) public pure {}
function g() public pure returns (S memory, T memory, uint) {}
}
would result in the JSON:

View File

@ -41,7 +41,7 @@ without a compiler change.
.. code::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
library GetCode {
function at(address _addr) public view returns (bytes memory o_code) {
@ -136,7 +136,7 @@ Local Solidity variables are available for assignments, for example:
.. code::
pragma solidity >=0.4.11 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
contract C {
uint b;

View File

@ -10,6 +10,14 @@
"yulOptimizer": true
}
},
{
"name": "privateCanBeOverridden",
"summary": "Private methods can be overridden by inheriting contracts.",
"description": "While private methods of base contracts are not visible and cannot be called directly from the derived contract, it is still possible to declare a function of the same name and type and thus change the behaviour of the base contract's function.",
"introduced": "0.3.0",
"fixed": "0.5.17",
"severity": "low"
},
{
"name": "YulOptimizerRedundantAssignmentBreakContinue0.5",
"summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.",

View File

@ -211,6 +211,7 @@
},
"0.3.0": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -232,6 +233,7 @@
},
"0.3.1": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -252,6 +254,7 @@
},
"0.3.2": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -272,6 +275,7 @@
},
"0.3.3": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -291,6 +295,7 @@
},
"0.3.4": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -310,6 +315,7 @@
},
"0.3.5": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -329,6 +335,7 @@
},
"0.3.6": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -346,6 +353,7 @@
},
"0.4.0": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -363,6 +371,7 @@
},
"0.4.1": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -380,6 +389,7 @@
},
"0.4.10": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -395,6 +405,7 @@
},
"0.4.11": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -409,6 +420,7 @@
},
"0.4.12": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -422,6 +434,7 @@
},
"0.4.13": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -435,6 +448,7 @@
},
"0.4.14": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -447,6 +461,7 @@
},
"0.4.15": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -458,6 +473,7 @@
},
"0.4.16": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -471,6 +487,7 @@
},
"0.4.17": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -485,6 +502,7 @@
},
"0.4.18": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -498,6 +516,7 @@
},
"0.4.19": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -512,6 +531,7 @@
},
"0.4.2": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -528,6 +548,7 @@
},
"0.4.20": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -542,6 +563,7 @@
},
"0.4.21": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -556,6 +578,7 @@
},
"0.4.22": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -570,6 +593,7 @@
},
"0.4.23": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -583,6 +607,7 @@
},
"0.4.24": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -596,6 +621,7 @@
},
"0.4.25": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -607,6 +633,7 @@
},
"0.4.26": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2"
@ -615,6 +642,7 @@
},
"0.4.3": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -630,6 +658,7 @@
},
"0.4.4": {
"bugs": [
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -644,6 +673,7 @@
},
"0.4.5": {
"bugs": [
"privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
@ -660,6 +690,7 @@
},
"0.4.6": {
"bugs": [
"privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
"ExpExponentCleanup",
@ -675,6 +706,7 @@
},
"0.4.7": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -690,6 +722,7 @@
},
"0.4.8": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -705,6 +738,7 @@
},
"0.4.9": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"UninitializedFunctionPointerInConstructor_0.4.x",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -720,6 +754,7 @@
},
"0.5.0": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -731,6 +766,7 @@
},
"0.5.1": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -742,6 +778,7 @@
},
"0.5.10": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers"
],
@ -749,24 +786,28 @@
},
"0.5.11": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
],
"released": "2019-08-12"
},
"0.5.12": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
],
"released": "2019-10-01"
},
"0.5.13": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
],
"released": "2019-11-14"
},
"0.5.14": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
"ABIEncoderV2LoopYulOptimizer"
],
@ -774,16 +815,24 @@
},
"0.5.15": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
],
"released": "2019-12-17"
},
"0.5.16": {
"bugs": [],
"bugs": [
"privateCanBeOverridden"
],
"released": "2020-01-02"
},
"0.5.17": {
"bugs": [],
"released": "2020-03-17"
},
"0.5.2": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -795,6 +844,7 @@
},
"0.5.3": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -806,6 +856,7 @@
},
"0.5.4": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -817,6 +868,7 @@
},
"0.5.5": {
"bugs": [
"privateCanBeOverridden",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
"DynamicConstructorArgumentsClippedABIV2",
@ -830,6 +882,7 @@
},
"0.5.6": {
"bugs": [
"privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
@ -843,6 +896,7 @@
},
"0.5.7": {
"bugs": [
"privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
"SignedArrayStorageCopy",
"ABIEncoderV2StorageArrayWithMultiSlotElement",
@ -854,6 +908,7 @@
},
"0.5.8": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
"SignedArrayStorageCopy",
@ -864,6 +919,7 @@
},
"0.5.9": {
"bugs": [
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
"SignedArrayStorageCopy",

View File

@ -13,7 +13,7 @@ This can be done by using the ``abstract`` keyword as shown in the following exa
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
provided (no implementation body ``{ }`` was given).::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
abstract contract Feline {
function utterance() public virtual returns (bytes32);

View File

@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it.
::
pragma solidity >0.6.1 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
contract Test {
// This function is called for all messages sent to

View File

@ -154,7 +154,7 @@ A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
explicitly in the final override, but this function will bypass
``Base1.destroy``. The way around this is to use ``super``::
pragma solidity >=0.4.22 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract owned {
constructor() public { owner = msg.sender; }
@ -204,7 +204,7 @@ use the ``override`` keyword in the function header as shown in this example:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Base
{
@ -227,7 +227,7 @@ bases, it has to explicitly override it:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Base1
{
@ -253,7 +253,7 @@ that already overrides all other functions.
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract A { function f() public pure{} }
contract B is A {}
@ -293,7 +293,7 @@ of the variable:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract A
{
@ -324,7 +324,7 @@ and the ``override`` keyword must be used in the overriding modifier:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Base
{
@ -342,7 +342,7 @@ explicitly:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Base1
{
@ -498,7 +498,7 @@ One area where inheritance linearization is especially important and perhaps not
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
contract Base1 {
constructor() public {}

View File

@ -22,7 +22,7 @@ Interfaces are denoted by their own keyword:
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
interface Token {
enum TokenType { Fungible, NonFungible }
@ -42,7 +42,7 @@ inheritance.
::
pragma solidity >0.6.1 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
interface ParentA {
function test() external returns (uint256);

View File

@ -47,12 +47,14 @@ more advanced example to implement a set).
::
pragma solidity >=0.4.22 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
struct Data { mapping(uint => bool) flags; }
struct Data {
mapping(uint => bool) flags;
}
library Set {
// Note that the first parameter is of type "storage
@ -123,7 +125,7 @@ custom types without the overhead of external function calls:
::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
struct bigint {
uint[] limbs;
@ -237,7 +239,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow
::
pragma solidity >0.5.13 <0.8.0;
pragma solidity >=0.5.14 <0.8.0;
library L {
function f(uint256) external {}

View File

@ -29,7 +29,7 @@ may only be used inside a contract, not inside any of its functions.
Let us rewrite the set example from the
:ref:`libraries` in this way::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
// This is the same code as before, just without comments

View File

@ -68,7 +68,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
contract C {
uint private data;
@ -112,7 +112,7 @@ when they are declared.
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
contract C {
uint public data = 42;
@ -151,7 +151,7 @@ to write a function, for example:
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
contract arrayExample {
// public state variable

View File

@ -41,7 +41,7 @@ Internal Function Calls
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
this nonsensical example::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
contract C {
function g(uint a) public pure returns (uint ret) { return a + f(); }
@ -82,7 +82,7 @@ to the total balance of that contract:
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
contract InfoFeed {
function info() public payable returns (uint ret) { return 42; }
@ -160,7 +160,7 @@ Those parameters will still be present on the stack, but they are inaccessible.
::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
contract C {
// omitted name for parameter
@ -183,7 +183,7 @@ is compiled so recursive creation-dependencies are not possible.
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
contract D {
uint public x;
@ -238,7 +238,7 @@ which only need to be created if there is a dispute.
::
pragma solidity >0.6.1 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
contract D {
uint public x;
@ -307,7 +307,7 @@ groupings of expressions.
::
pragma solidity >0.4.23 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
contract C {
uint index;
@ -352,7 +352,7 @@ because only a reference and not a copy is passed.
::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
contract C {
uint[20] x;

View File

@ -24,7 +24,7 @@ to receive their money - contracts cannot activate themselves.
::
pragma solidity >=0.4.22 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
contract SimpleAuction {
// Parameters of the auction. Times are either

View File

@ -338,7 +338,7 @@ The full contract
::
pragma solidity >=0.4.24 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
contract SimplePaymentChannel {
address payable public sender; // The account sending payments.

View File

@ -19,7 +19,7 @@ and the sum of all balances is an invariant across the lifetime of the contract.
::
pragma solidity >=0.4.22 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
library Balances {
function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal {

View File

@ -25,7 +25,7 @@ you can use state machine-like constructs inside a contract.
::
pragma solidity >=0.4.22 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
contract Purchase {
uint public value;

View File

@ -17,7 +17,7 @@ Storage Example
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
contract SimpleStorage {
uint storedData;

View File

@ -284,7 +284,7 @@ for the two function parameters and two return variables.
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.21 <0.8.0;
/** @title Shape calculator. */
contract ShapeCalculator {

View File

@ -81,7 +81,7 @@ as it uses ``call`` which forwards all remaining gas by default:
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.6.2 <0.8.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund {
@ -277,7 +277,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The
::
pragma solidity >=0.5.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Map {
mapping (uint => uint)[] array;

View File

@ -109,7 +109,7 @@ Yes::
No::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
abstract contract A {
function spam() virtual pure public;
@ -326,7 +326,7 @@ Yes::
No::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract A {
@ -745,7 +745,7 @@ manner as modifiers if the function declaration is long or hard to read.
Yes::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
// Base contracts just to make this compile
contract B {
@ -777,7 +777,7 @@ Yes::
No::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
// Base contracts just to make this compile
@ -1000,7 +1000,7 @@ As shown in the example below, if the contract name is `Congress` and the librar
Yes::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
// Owned.sol
@ -1034,7 +1034,7 @@ and in ``Congress.sol``::
No::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
// owned.sol
@ -1138,7 +1138,7 @@ multiline comment starting with `/**` and ending with `*/`.
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
added looks like the one below::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.16 <0.8.0;
/// @author The Solidity Team

View File

@ -66,7 +66,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.4.22 <0.8.0;
contract MappingExample {
@ -120,7 +120,7 @@ the ``sum`` function iterates over to sum all the values.
::
pragma solidity >=0.5.99 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }

View File

@ -57,7 +57,7 @@ Data locations are not only relevant for persistency of data, but also for the s
::
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >=0.5.0 <0.8.0;
contract C {
// The data location of x is storage.
@ -268,7 +268,7 @@ Array Members
::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
@ -400,7 +400,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
::
pragma solidity >=0.4.99 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
contract Proxy {
/// Address of the client contract managed by proxy i.e., this contract
@ -437,7 +437,7 @@ shown in the following example:
::
pragma solidity >=0.4.11 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
// Defines a new type with two fields.
// Declaring a struct outside of a contract allows

View File

@ -300,6 +300,11 @@ All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-l
The ``gas`` option is available on all three methods, while the ``value`` option is not
supported for ``delegatecall``.
.. note::
It is best to avoid relying on hardcoded gas values in your smart contract code,
regardless of whether state is read from or written to, as this can have many pitfalls.
Also, access to gas might change in the future.
.. note::
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
current contract using ``address(this).balance``.
@ -645,7 +650,7 @@ External (or public) functions have the following members:
Example that shows how to use the members::
pragma solidity >=0.4.16 <0.8.0;
pragma solidity >=0.6.0 <0.8.0;
// This will report a warning
contract Example {
@ -665,7 +670,6 @@ Example that shows how to use internal function types::
pragma solidity >=0.4.16 <0.8.0;
library ArrayUtils {
// internal functions can be used in internal library functions because
// they will be part of the same code context

View File

@ -687,7 +687,7 @@ The command above applies all changes as shown below. Please review them careful
.. code-block:: none
pragma solidity >0.4.23;
pragma solidity >=0.6.0 <0.7.0;
abstract contract Updateable {
function run() public view virtual returns (bool);

View File

@ -111,7 +111,7 @@ Stand-Alone Usage
=================
You can use Yul in its stand-alone form in the EVM dialect using the Solidity compiler.
This will use the `Yul object notation <yul-objects>`_ so that it is possible to refer
This will use the :ref:`Yul object notation <yul-object>` so that it is possible to refer
to code as data to deploy contracts. This Yul mode is available for the commandline compiler
(use ``--strict-assembly``) and for the :ref:`standard-json interface <compiler-api>`:
@ -146,7 +146,7 @@ so you can e.g. use ``//`` and ``/* */`` to denote comments.
There is one exception: Identifiers in Yul can contain dots: ``.``.
Yul can specify "objects" that consist of code, data and sub-objects.
Please see `Yul Objects <yul-objects>`_ below for details on that.
Please see :ref:`Yul Objects <yul-object>` below for details on that.
In this section, we are only concerned with the code part of such an object.
This code part always consists of a curly-braces
delimited block. Most tools support specifying just a code block

View File

@ -153,7 +153,7 @@ void DocStringAnalyser::parseDocStrings(
appendError(
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" exceedes the number of return parameters."
" exceeds the number of return parameters."
);
else
{

View File

@ -328,6 +328,8 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (_variable.immutable() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
@ -394,7 +396,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage;
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||

View File

@ -480,6 +480,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
"Initial value for constant variable has to be compile-time constant."
);
}
else if (_variable.immutable())
if (!_variable.type()->isValueType())
m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type.");
if (!_variable.isStateVariable())
{
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
@ -641,16 +645,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
solAssert(var->type(), "Expected variable type!");
if (var->immutable())
{
m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported.");
return size_t(-1);
}
if (var->isConstant())
{
var = rootVariableDeclaration(*var);
var = rootConstVariableDeclaration(*var);
if (!var->value())
if (var && !var->value())
{
m_errorReporter.typeError(_identifier.location, "Constant has no value.");
return size_t(-1);
}
else if (!type(*var)->isValueType() || (
else if (!var || !type(*var)->isValueType() || (
dynamic_cast<Literal const*>(var->value().get()) == nullptr &&
type(*var->value())->category() != Type::Category::RationalNumber
))

View File

@ -508,8 +508,6 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Unspecified };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };

View File

@ -879,6 +879,7 @@ public:
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_constantness == Constantness::Constant; }
bool immutable() const { return m_constantness == Constantness::Immutable; }
ASTPointer<OverrideSpecifier> const& overrides() const { return m_overrides; }
Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable.

View File

@ -21,7 +21,7 @@
namespace solidity::frontend
{
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl)
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl)
{
solAssert(_varDecl.isConstant(), "Constant variable expected");
@ -30,7 +30,8 @@ VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _v
while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get())))
{
auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration);
solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration");
if (!referencedVarDecl || !referencedVarDecl->isConstant())
return nullptr;
rootDecl = referencedVarDecl;
}
return rootDecl;

View File

@ -22,8 +22,9 @@ namespace solidity::frontend
class VariableDeclaration;
/// Find the topmost referenced variable declaration when the given variable
/// Find the topmost referenced constant variable declaration when the given variable
/// declaration value is an identifier. Works only for constant variable declarations.
VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl);
/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration.
VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl);
}

View File

@ -1487,11 +1487,19 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
case DataLocation::Memory:
return TypeProvider::emptyTuple();
case DataLocation::Storage:
return m_isPointer ? nullptr : TypeProvider::emptyTuple();
return isPointer() ? nullptr : TypeProvider::emptyTuple();
}
return nullptr;
}
bool ReferenceType::isPointer() const
{
if (m_location == DataLocation::Storage)
return m_isPointer;
else
return true;
}
TypePointer ReferenceType::copyForLocationIfReference(Type const* _type) const
{
return TypeProvider::withLocationIfReference(m_location, _type);
@ -1502,7 +1510,7 @@ string ReferenceType::stringForReferencePart() const
switch (m_location)
{
case DataLocation::Storage:
return string("storage ") + (m_isPointer ? "pointer" : "ref");
return string("storage ") + (isPointer() ? "pointer" : "ref");
case DataLocation::CallData:
return "calldata";
case DataLocation::Memory:
@ -1868,6 +1876,7 @@ u256 ArrayType::memoryDataSize() const
std::unique_ptr<ReferenceType> ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const
{
auto copy = make_unique<ArrayType>(_location);
if (_location == DataLocation::Storage)
copy->m_isPointer = _isPointer;
copy->m_arrayKind = m_arrayKind;
copy->m_baseType = copy->copyForLocationIfReference(m_baseType);
@ -1988,7 +1997,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
vector<VariableDeclaration const*> variables;
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
for (VariableDeclaration const* variable: contract->stateVariables())
if (!variable->isConstant())
if (!(variable->isConstant() || variable->immutable()))
variables.push_back(variable);
TypePointers types;
for (auto variable: variables)
@ -2247,6 +2256,7 @@ TypeResult StructType::interfaceType(bool _inLibrary) const
std::unique_ptr<ReferenceType> StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{
auto copy = make_unique<StructType>(m_struct, _location);
if (_location == DataLocation::Storage)
copy->m_isPointer = _isPointer;
return copy;
}
@ -2954,7 +2964,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const
if (m_valueSet)
slots.emplace_back("value", TypeProvider::uint256());
if (m_saltSet)
slots.emplace_back("salt", TypeProvider::uint256());
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
if (bound())
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems())
slots.emplace_back("self_" + boundName, boundType);

View File

@ -701,7 +701,9 @@ public:
/// pointer type, state variables are bound references. Assignments to pointers or deleting
/// them will not modify storage (that will only change the pointer). Assignment from
/// non-storage objects to a variable of storage pointer type is not possible.
bool isPointer() const { return m_isPointer; }
/// For anything other than storage, this always returns true because assignments
/// never change the contents of the original value.
bool isPointer() const;
bool operator==(ReferenceType const& _other) const
{

View File

@ -525,7 +525,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
{
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant())
if (variable->value() && !variable->isConstant() && !variable->immutable())
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable);
}
@ -541,6 +541,8 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
if (_variableDeclaration.isConstant())
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals)
.appendConstStateVariableAccessor(_variableDeclaration);
else if (_variableDeclaration.immutable())
solUnimplementedAssert(false, "");
else
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals)
.appendStateVariableAccessor(_variableDeclaration);
@ -680,9 +682,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->immutable(), "");
if (variable->isConstant())
{
variable = rootVariableDeclaration(*variable);
variable = rootConstVariableDeclaration(*variable);
// If rootConstVariableDeclaration fails and returns nullptr,
// it should have failed in TypeChecker already, causing a compilation error.
// In such case we should not get here.
solAssert(variable, "");
u256 value;
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
{

View File

@ -88,7 +88,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{
solAssert(!_varDecl.isConstant(), "");
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
FunctionType accessorType(_varDecl);
@ -1800,6 +1800,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
{
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
_indexAccess.baseExpression().accept(*this);
// stack: offset length
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
@ -1815,27 +1816,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
else
m_context << u256(0);
// stack: offset length sliceStart
m_context << Instruction::SWAP1;
// stack: offset sliceStart length
if (_indexAccess.endExpression())
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
else
m_context << Instruction::DUP2;
m_context << Instruction::DUP1;
// stack: offset sliceStart length sliceEnd
m_context.appendInlineAssembly(
Whiskers(R"({
if gt(sliceStart, sliceEnd) { <revertStringStartEnd> }
if gt(sliceEnd, length) { <revertStringEndLength> }
m_context << Instruction::SWAP3;
// stack: sliceEnd sliceStart length offset
offset := add(offset, mul(sliceStart, <stride>))
length := sub(sliceEnd, sliceStart)
})")
("stride", toString(arrayType->calldataStride()))
("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end"))
("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length"))
.render(),
{"offset", "length", "sliceStart", "sliceEnd"}
);
m_context << Instruction::POP << Instruction::POP;
m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2);
return false;
}
@ -2438,10 +2433,12 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression)
{
if (!_variable.isConstant())
setLValueFromDeclaration(_variable, _expression);
else
if (_variable.isConstant())
acceptAndConvert(*_variable.value(), *_variable.annotation().type);
else if (_variable.immutable())
solUnimplemented("");
else
setLValueFromDeclaration(_variable, _expression);
}
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)

View File

@ -933,6 +933,28 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type
});
}
string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
{
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
solAssert(_type.isDynamicallySized(), "");
string functionName = "calldata_array_index_range_access_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd> }
if gt(endIndex, length) { <revertSliceGreaterThanLength> }
offsetOut := add(offset, mul(startIndex, <stride>))
lengthOut := sub(endIndex, startIndex)
}
)")
("functionName", functionName)
("stride", to_string(_type.calldataStride()))
("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end"))
("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length"))
.render();
});
}
string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
{
solAssert(_type.isDynamicallyEncoded(), "");
@ -1365,6 +1387,37 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
});
}
if (_from.category() == Type::Category::ArraySlice)
{
solAssert(_from.isDynamicallySized(), "");
solAssert(_from.dataStoredIn(DataLocation::CallData), "");
solAssert(_to.category() == Type::Category::Array, "");
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
solAssert(
*fromType.arrayType().baseType() == *targetType.baseType(),
"Converting arrays of different type is not possible"
);
string const functionName =
"convert_" +
_from.identifier() +
"_to_" +
_to.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(offset, length) -> outOffset, outLength {
outOffset := offset
outLength := length
}
)")
("functionName", functionName)
.render();
});
}
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
return conversionFunctionSpecial(_from, _to);

View File

@ -175,6 +175,11 @@ public:
/// signature: (baseRef, index) -> offset[, length]
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
/// @returns the name of a function that returns offset and length for array slice
/// for the given array offset, length and start and end indices for slice
/// signature: (arrayOffset, arrayLength, sliceStart, sliceEnd) -> offset, length
std::string calldataArrayIndexRangeAccess(ArrayType const& _type);
/// @returns the name of a function that follows a calldata tail while performing
/// bounds checks.
/// signature: (baseRef, tailPointer) -> offset[, length]

View File

@ -157,6 +157,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
Type const* type = _varDecl.annotation().type;
solAssert(!_varDecl.isConstant(), "");
solAssert(!_varDecl.immutable(), "");
solAssert(_varDecl.isStateVariable(), "");
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
@ -249,7 +250,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract)
IRGeneratorForStatements generator{m_context, m_utils};
for (VariableDeclaration const* variable: contract->stateVariables())
if (!variable->isConstant())
if (!variable->isConstant() && !variable->immutable())
generator.initializeStateVar(*variable);
out << generator.code();

View File

@ -140,6 +140,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
{
solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable.");
solAssert(!_varDecl.isConstant(), "");
solAssert(!_varDecl.immutable(), "");
if (_varDecl.value())
{
_varDecl.value()->accept(*this);
@ -739,6 +740,25 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
}
void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
{
FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type);
solUnimplementedAssert(!previousType.bound(), "");
// Copy over existing values.
for (auto const& item: previousType.stackItems())
define(IRVariable(_options).part(get<0>(item)), IRVariable(_options.expression()).part(get<0>(item)));
for (size_t i = 0; i < _options.names().size(); ++i)
{
string const& name = *_options.names()[i];
solAssert(name == "salt" || name == "gas" || name == "value", "");
define(IRVariable(_options).part(name), *_options.options()[i]);
}
}
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
ASTString const& member = _memberAccess.memberName();
@ -981,9 +1001,16 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
}
});
}
else if (baseType.category() == Type::Category::Array)
else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice)
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
ArrayType const& arrayType =
baseType.category() == Type::Category::Array ?
dynamic_cast<ArrayType const&>(baseType) :
dynamic_cast<ArraySliceType const&>(baseType).arrayType();
if (baseType.category() == Type::Category::ArraySlice)
solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), "");
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
switch (arrayType.location())
@ -1066,9 +1093,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
solAssert(false, "Index access only allowed for mappings or arrays.");
}
void IRGeneratorForStatements::endVisit(IndexRangeAccess const&)
void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess)
{
solUnimplementedAssert(false, "Index range accesses not yet implemented.");
Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type;
solAssert(
baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice,
"Index range accesses is available only on arrays and array slices."
);
ArrayType const& arrayType =
baseType.category() == Type::Category::Array ?
dynamic_cast<ArrayType const &>(baseType) :
dynamic_cast<ArraySliceType const &>(baseType).arrayType();
switch (arrayType.location())
{
case DataLocation::CallData:
{
solAssert(baseType.isDynamicallySized(), "");
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
if (_indexRangeAccess.startExpression())
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
else
define(sliceStart) << u256(0) << "\n";
IRVariable sliceEnd{
m_context.newYulVariable(),
*TypeProvider::uint256()
};
if (_indexRangeAccess.endExpression())
define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()});
else
define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length"));
IRVariable range{_indexRangeAccess};
define(range) <<
m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" <<
IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " <<
sliceStart.name() << ", " <<
sliceEnd.name() << ")\n";
break;
}
default:
solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays.");
}
}
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
@ -1104,6 +1172,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
// If the value is visited twice, `defineExpression` is called twice on
// the same expression.
solUnimplementedAssert(!varDecl->isConstant(), "");
solUnimplementedAssert(!varDecl->immutable(), "");
if (m_context.isLocalVariable(*varDecl))
setLValue(_identifier, IRLValue{
*varDecl->annotation().type,
@ -1519,7 +1588,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
solAssert(dynamic_cast<ReferenceType const*>(&_lvalue.type), "");
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), "");
m_code << "mstore(" + _memory.address + ", " + _value.name() + ")\n";
m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
}
},
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },

View File

@ -60,6 +60,7 @@ public:
void endVisit(UnaryOperation const& _unaryOperation) override;
bool visit(BinaryOperation const& _binOp) override;
void endVisit(FunctionCall const& _funCall) override;
void endVisit(FunctionCallOptions const& _funCallOptions) override;
void endVisit(MemberAccess const& _memberAccess) override;
bool visit(InlineAssembly const& _inlineAsm) override;
void endVisit(IndexAccess const& _indexAccess) override;

View File

@ -54,7 +54,7 @@ inline std::optional<RevertStrings> revertStringsFromString(std::string const& _
for (auto i: {RevertStrings::Default, RevertStrings::Strip, RevertStrings::Debug, RevertStrings::VerboseDebug})
if (revertStringsToString(i) == _str)
return i;
return {};
return std::nullopt;
}
}

View File

@ -730,8 +730,18 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{
if (_options.allowIndexed && token == Token::Indexed)
isIndexed = true;
else if (token == Token::Constant || token == Token::Immutable)
{
if (constantness != VariableDeclaration::Constantness::Mutable)
parserError(
string("Constantness already set to ") +
(constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"")
);
else if (token == Token::Constant)
constantness = VariableDeclaration::Constantness::Constant;
else if (token == Token::Immutable)
constantness = VariableDeclaration::Constantness::Immutable;
}
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Unspecified)

View File

@ -44,7 +44,6 @@ public:
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(T)); }
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(T)); }
template <class T2> explicit operator vector_ref<T2>() const { assert(m_count * sizeof(T) / sizeof(T2) * sizeof(T2) / sizeof(T) == m_count); return vector_ref<T2>(reinterpret_cast<T2*>(m_data), m_count * sizeof(T) / sizeof(T2)); }
operator vector_ref<T const>() const { return vector_ref<T const>(m_data, m_count); }
T* data() const { return m_data; }

View File

@ -324,6 +324,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
case Token::Byte:
case Token::Bool:
case Token::Address:
case Token::Var:
{
YulString literal{currentLiteral()};
if (m_dialect.builtin(literal))
@ -513,6 +514,7 @@ YulString Parser::expectAsmIdentifier()
case Token::Address:
case Token::Bool:
case Token::Identifier:
case Token::Var:
break;
default:
expectToken(Token::Identifier);

79
scripts/common_cmdline.sh Normal file
View File

@ -0,0 +1,79 @@
# ------------------------------------------------------------------------------
# vim:ts=4:et
# 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/>
#
# (c) 2016-2019 solidity contributors.
# ------------------------------------------------------------------------------
FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
OLDARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
function compileFull()
{
local expected_exit_code=0
local expect_output=0
if [[ $1 = '-e' ]]; then
expected_exit_code=1
expect_output=1
shift;
fi
if [[ $1 = '-w' ]]; then
expect_output=1
shift;
fi
if [[ $1 = '-o' ]]; then
expect_output=2
shift;
fi
local args=$FULLARGS
if [[ $1 = '-v' ]]; then
if (echo $2 | grep -Po '(?<=0.4.)\d+' >/dev/null); then
patch=$(echo $2 | grep -Po '(?<=0.4.)\d+')
if (( patch < 22 )); then
args=$OLDARGS
fi
fi
shift 2
fi
local files="$*"
local output
local stderr_path=$(mktemp)
set +e
"$SOLC" ${args} ${files} >/dev/null 2>"$stderr_path"
local exit_code=$?
local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path")
set -e
rm "$stderr_path"
if [[ \
("$exit_code" -ne "$expected_exit_code" || \
( $expect_output -eq 0 && -n "$errors" ) || \
( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" ))
]]
then
printError "Unexpected compilation result:"
printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output"
printError "Was failure: $exit_code"
echo "$errors"
printError "While calling:"
echo "\"$SOLC\" $ARGS $files"
printError "Inside directory:"
pwd
false
fi
}

View File

@ -0,0 +1,95 @@
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# 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/>
#
# (c) 2016 solidity contributors.
#------------------------------------------------------------------------------
# This script verifies that the examples compile with the oldest version mentioned in the pragma.
# It does not verify that it cannot be compiled with an older version
# and it also does not verify that it can be compiled with the newest version compatible with the pragma.
set -e
## GLOBAL VARIABLES
REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build}
source "${REPO_ROOT}/scripts/common.sh"
source "${REPO_ROOT}/scripts/common_cmdline.sh"
printTask "Verifying that all examples from the documentation have the correct version range..."
SOLTMPDIR=$(mktemp -d)
(
set -e
cd "$SOLTMPDIR"
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
for f in *.sol
do
# The contributors guide uses syntax tests, but we cannot
# really handle them here.
if grep -E 'DeclarationError:|// ----' "$f" >/dev/null
then
continue
fi
echo "$f"
opts=''
# We expect errors if explicitly stated, or if imports
# are used (in the style guide)
if ( ! grep -E "This will not compile after" "$f" >/dev/null && \
grep -E "This will not compile|import \"" "$f" >/dev/null )
then
opts="-e"
fi
# ignore warnings in this case
opts="$opts -o"
# Get minimum compiler version defined by pragma
if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null); then
version="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")"
if (echo $version | grep -Po '(?<=0.4.)\d+' >/dev/null); then
patch=$(echo $version | grep -Po '(?<=0.4.)\d+')
if (( patch < 11 )); then
version="0.4.11" # first available release on github
fi
fi
elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null); then
version="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")"
fi
opts="$opts -v $version"
solc_bin="solc-$version"
echo "$solc_bin"
if [[ ! -f "$solc_bin" ]]; then
echo "Downloading release from github..."
wget https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux
mv solc-static-linux $solc_bin
fi
ln -sf "$solc_bin" "solc"
chmod a+x solc
SOLC="$SOLTMPDIR/solc"
compileFull $opts "$SOLTMPDIR/$f"
done
)
rm -rf "$SOLTMPDIR"
echo "Done."

View File

@ -0,0 +1,22 @@
BASE_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )"
mkdir -p build
cd build || exit
cmake ../../../
make soltest
cd test/ || exit
echo "running soltest on 'semanticTests/extracted'..."
./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --testpath ${BASE_PATH}/../../test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/extracted-tests.trace
echo "running soltest on 'semanticTests/extracted'... done"
cd $BASE_PATH || exit
git clone git@github.com:ethereum/solidity.git solidity-develop
cd solidity-develop || exit
mkdir -p build
cd build || exit
cmake ..
make soltest
cd test/ || exit
echo "running soltest on 'SolidityEndToEndTest'..."
./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --testpath ${BASE_PATH}/solidity-develop/test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/endToEndExtraction-tests.trace
echo "running soltest on 'SolidityEndToEndTest'... done"

View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
# pylint: disable=consider-using-enumerate, import-error
import re
import os
import sys
import getopt
import tempfile
from getkey import getkey
def parse_call(call):
function = ''
arguments = ""
results = ""
search = re.search(r'// (.*):(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
if search:
function = search.group(1)
arguments = search.group(2)
results = search.group(3)
if results.find("#") != -1:
results = results[:results.find("#")]
else:
search = re.search(r'// (.*)(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
if search:
function = search.group(1)
arguments = search.group(2)
results = search.group(3)
if results.find("#") != -1:
results = results[:results.find("#")]
if function.find("wei") >= 0:
function = function[:function.find(",")]
return function.strip(), arguments.strip(), results.strip()
def colorize(left, right, id):
red = "\x1b[31m"
yellow = "\x1b[33m"
reset = "\x1b[0m"
colors = [red, yellow]
color = colors[id % len(colors)]
function, arguments, results = parse_call(right)
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
right = right.replace("constructor", color + "constructor" + reset)
if function:
left = left.replace(function, color + function + reset)
right = right.replace(function, color + function + reset)
if left.find(function):
bottom = " " * (left.find(function) - 4) + right
else:
bottom = " " + right
return " " + left + "\n" + bottom # " {:<90} {:<90}\n{}".format(left, right, bottom)
def get_checks(content, sol_file_path):
constructors = []
checks = []
for line in content.split("\n"):
line = line.strip()
if line.startswith("compileAndRun"):
constructors.append(line)
if line.startswith("ABI_CHECK") or line.startswith("BOOST_REQUIRE"):
checks.append(line)
sol_file = open(sol_file_path, "r")
sol_constructors = []
sol_checks = []
inside_expectations = False
for line in sol_file.readlines():
if line.startswith("// constructor()"):
sol_constructors.append(line)
elif inside_expectations and line.startswith("// "):
sol_checks.append(line)
if line.startswith("// ----"):
inside_expectations = True
sol_file.close()
if len(constructors) == len(sol_constructors) == 1:
checks.insert(0, constructors[0])
sol_checks.insert(0, sol_constructors[0])
return checks, sol_checks
def show_test(name, content, sol_file_path, current_test, test_count):
cpp_file = tempfile.NamedTemporaryFile(delete=False)
cpp_file.write(content.encode())
cpp_file.close()
os.system("clear")
print(str(current_test) + " / " + str(test_count) + " - " + name + "\n")
diff_env = os.getenv('DIFF', "/usr/local/bin/colordiff -a -d -w -y -W 200 ")
os.system(diff_env + " " + cpp_file.name + " " + sol_file_path)
os.unlink(cpp_file.name)
print("\n")
checks, sol_checks = get_checks(content, sol_file_path)
if len(checks) == len(sol_checks):
for i in range(0, len(checks)):
print(colorize(checks[i].strip(), sol_checks[i].strip(), i))
else:
print("warning: check count not matching. this should not happen!")
what = ""
print("\nContinue? (ENTER) Abort? (ANY OTHER KEY)")
while what != '\n':
what = getkey()
if what != '\n':
sys.exit(0)
print()
def get_tests(e2e_path):
tests = []
for f in os.listdir(e2e_path):
if f.endswith(".sol"):
tests.append(f.replace(".sol", ""))
return tests
def process_input_file(e2e_path, input_file, interactive):
tests = get_tests(e2e_path)
cpp_file = open(input_file, "r")
inside_test = False
test_name = ""
inside_extracted_test = False
new_lines = 0
count = 0
test_content = ""
for line in cpp_file.readlines():
test = re.search(r'BOOST_AUTO_TEST_CASE\((.*)\)', line, re.M | re.I)
if test:
test_name = test.group(1)
inside_test = True
inside_extracted_test = inside_test & (test_name in tests)
if inside_extracted_test:
count = count + 1
if interactive and inside_extracted_test:
test_content = test_content + line
if not inside_extracted_test:
if line == "\n":
new_lines = new_lines + 1
else:
new_lines = 0
if not interactive and new_lines <= 1:
sys.stdout.write(line)
if line == "}\n":
if interactive and inside_extracted_test:
show_test(test_name, test_content.strip(), e2e_path + "/" + test_name + ".sol", count, len(tests))
test_content = ""
inside_test = False
cpp_file.close()
sys.stdout.flush()
def main(argv):
interactive = False
input_file = None
try:
opts, args = getopt.getopt(argv, "if:")
except getopt.GetoptError:
print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
sys.exit(1)
for opt, arg in opts:
if opt == '-i':
interactive = True
elif opt in '-f':
input_file = arg
base_path = os.path.dirname(__file__)
if not input_file:
input_file = base_path + "/../../test/libsolidity/SolidityEndToEndTest.cpp"
e2e_path = base_path + "/../../test/libsolidity/semanticTests/extracted"
process_input_file(e2e_path, input_file, interactive)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@ -0,0 +1,215 @@
#!/usr/bin/env python3
#
# - SolidityEndToEndTest.trace was created with soltest with the following command on
# ./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --no-smt
# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > SolidityEndToEndTest.trace
# - a trace of the semantic tests can be created by using
# ./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --no-smt
# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > semanticTests.trace
#
# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests where
# identical with the tests specified in SolidityEndToEndTest.cpp.
#
# pylint: disable=too-many-instance-attributes
import re
import os
import sys
import getopt
import json
class Trace:
def __init__(self, kind, parameter):
self.kind = kind
self.parameter = parameter
self._input = ""
self._output = ""
self.value = ""
self.result = ""
self.gas = ""
def get_input(self):
return self._input
def set_input(self, input):
if self.kind == "create":
# remove cbor encoded metadata from bytecode
length = int(input[-4:], 16) * 2
self._input = input[:len(input) - length - 4]
def get_output(self):
return self._output
def set_output(self, output):
if self.kind == "create":
# remove cbor encoded metadata from bytecode
length = int(output[-4:], 16) * 2
self._output = output[:len(output) - length - 4]
def __str__(self):
# we ignore the used gas
result = str(
"kind='" + self.kind + "' parameter='" + self.parameter + "' input='" + self._input +
"' output='" + self._output + "' value='" + self.value + "' result='" + self.result + "'"
)
return result
class TestCase:
def __init__(self, name):
self.name = name
self.metadata = None
self.traces = []
def add_trace(self, kind, parameter):
trace = Trace(kind, parameter)
self.traces.append(trace)
return trace
class TraceAnalyser:
def __init__(self, file):
self.file = file
self.tests = {}
self.ready = False
def analyse(self):
trace_file = open(self.file, "r")
trace = None
test_case = None
for line in trace_file.readlines():
test = re.search(r'Entering test case "(.*)"', line, re.M | re.I)
if test:
test_name = test.group(1)
test_case = TestCase(test_name)
self.tests[test_name] = test_case
metadata = re.search(r'\s*metadata:\s*(.*)$', line, re.M | re.I)
if metadata:
test_case.metadata = json.loads(metadata.group(1))
del test_case.metadata["sources"]
del test_case.metadata["compiler"]["version"]
create = re.search(r'CREATE\s*([a-fA-F0-9]*):', line, re.M | re.I)
if create:
trace = test_case.add_trace("create", create.group(1))
call = re.search(r'CALL\s*([a-fA-F0-9]*)\s*->\s*([a-fA-F0-9]*):', line, re.M | re.I)
if call:
trace = test_case.add_trace("call", call.group(1)) # + "->" + call.group(2))
if not create and not call:
self.parse_parameters(line, trace)
trace_file.close()
print(self.file + ":", len(self.tests), "test-cases.")
self.ready = True
@staticmethod
def parse_parameters(line, trace):
input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I)
if input:
trace.input = input.group(1)
output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I)
if output:
trace.output = output.group(1)
result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I)
if result:
trace.result = result.group(1)
gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I)
if gas_used:
trace.gas = gas_used.group(1)
value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I)
if value:
trace.value = value.group(1)
def diff(self, analyser):
if not self.ready:
self.analyse()
if not analyser.ready:
analyser.analyse()
intersection = set(self.tests.keys()) & set(analyser.tests.keys())
mismatches = set()
for test_name in intersection:
left = self.tests[test_name]
right = analyser.tests[test_name]
if json.dumps(left.metadata) != json.dumps(right.metadata):
mismatches.add(
(test_name, "metadata where different: " + json.dumps(left.metadata) + " != " + json.dumps(
right.metadata)))
if len(left.traces) != len(right.traces):
mismatches.add((test_name, "trace count are different: " + str(len(left.traces)) +
" != " + str(len(right.traces))))
else:
self.check_traces(test_name, left, right, mismatches)
for mismatch in mismatches:
print(mismatch[0])
print(mismatch[1])
print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)")
def check_traces(self, test_name, left, right, mismatches):
for trace_id in range(0, len(left.traces)):
left_trace = left.traces[trace_id]
right_trace = right.traces[trace_id]
assert (left_trace.kind == right_trace.kind)
if str(left_trace) != str(right_trace):
mismatch_info = " " + str(left_trace) + "\n"
mismatch_info += " " + str(right_trace) + "\n"
mismatch_info += " "
for ch in range(0, len(str(left_trace))):
if ch < len(str(left_trace)) and ch < len(str(right_trace)):
if str(left_trace)[ch] != str(right_trace)[ch]:
mismatch_info += "|"
else:
mismatch_info += " "
else:
mismatch_info += "|"
mismatch_info += "\n"
mismatches.add((test_name, mismatch_info))
def main(argv):
extracted_tests_trace_file = None
end_to_end_trace_file = None
try:
opts, args = getopt.getopt(argv, "s:e:")
except getopt.GetoptError:
print("verify-testcases.py [-s <path to semantic-trace>] [-e <path to endToEndExtraction-trace>]")
sys.exit(2)
for opt, arg in opts:
if opt in '-s':
extracted_tests_trace_file = arg
elif opt in '-e':
end_to_end_trace_file = arg
base_path = os.path.dirname(__file__)
if not extracted_tests_trace_file:
extracted_tests_trace_file = base_path + "/extracted-tests.trace"
if not end_to_end_trace_file:
end_to_end_trace_file = base_path + "/endToEndExtraction-tests.trace"
for f in [extracted_tests_trace_file, end_to_end_trace_file]:
if not os.path.isfile(f):
print("trace file '" + f + "' not found. aborting.")
sys.exit(1)
if not os.path.isfile(extracted_tests_trace_file):
print("semantic trace file '" + extracted_tests_trace_file + "' not found. aborting.")
sys.exit(1)
semantic_trace = TraceAnalyser(extracted_tests_trace_file)
end_to_end_trace = TraceAnalyser(end_to_end_trace_file)
semantic_trace.diff(end_to_end_trace)
if __name__ == "__main__":
main(sys.argv[1:])

101
scripts/test_antlr_grammar.sh Executable file
View File

@ -0,0 +1,101 @@
#!/usr/bin/env bash
set -e
ROOT_DIR="$(dirname "$0")"/..
WORKDIR="${ROOT_DIR}/build/antlr"
ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar"
ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar"
GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")"
SGR_RESET="\033[0m"
SGR_BOLD="\033[1m"
SGR_GREEN="\033[32m"
SGR_RED="\033[31m"
SGR_BLUE="\033[34m"
vt_cursor_up() { echo -ne "\033[A"; }
vt_cursor_begin_of_line() { echo -ne "\r"; }
download_antlr4()
{
if [[ ! -e "$ANTLR_JAR" ]]
then
curl -o "${ANTLR_JAR}" "${ANTLR_JAR_URI}"
fi
}
prepare_workdir()
{
mkdir -p "${ROOT_DIR}/build/deps"
mkdir -p "${WORKDIR}"
mkdir -p "${WORKDIR}/src"
mkdir -p "${WORKDIR}/target"
}
prepare_workdir
download_antlr4
if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \
[ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ]
then
echo "Creating parser"
# Create lexer/parser from grammar
java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/"
# Compile lexer/parser sources
javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/"
fi
# Run tests
failed_count=0
test_file()
{
local SOL_FILE
SOL_FILE="$(readlink -m "${1}")"
local cur=${2}
local max=${3}
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..."
local output
output=$(
java \
-classpath "${ANTLR_JAR}:${WORKDIR}/target/" \
"org.antlr.v4.gui.TestRig" \
Solidity \
sourceUnit <"${SOL_FILE}" 2>&1
)
vt_cursor_up
vt_cursor_begin_of_line
if [[ "${output}" == "" ]]
then
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}"
else
echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}"
echo "${output}"
failed_count=$((failed_count + 1))
exit 1
fi
}
# we only want to use files that do not contain errors or multi-source files.
SOL_FILES=()
while IFS='' read -r line
do
SOL_FILES+=("$line")
done < <(
grep -riL -E \
"^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \
"${ROOT_DIR}/test/libsolidity/syntaxTests" \
"${ROOT_DIR}/test/libsolidity/semanticTests" \
)
test_count=0
for SOL_FILE in "${SOL_FILES[@]}"
do
test_count=$((test_count + 1))
test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]}
done
echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed."
exit ${failed_count}

View File

@ -13,6 +13,8 @@ set(sources
Metadata.h
TestCase.cpp
TestCase.h
TestCaseReader.cpp
TestCaseReader.h
)
detect_stray_source_files("${sources}" ".")
@ -139,12 +141,17 @@ set(libyul_sources
detect_stray_source_files("${libyul_sources}" "libyul/")
set(yul_phaser_sources
yulPhaser/Common.h
yulPhaser/TestHelpers.h
yulPhaser/TestHelpers.cpp
yulPhaser/TestHelpersTest.cpp
yulPhaser/Common.cpp
yulPhaser/CommonTest.cpp
yulPhaser/Chromosome.cpp
yulPhaser/FitnessMetrics.cpp
yulPhaser/AlgorithmRunner.cpp
yulPhaser/GeneticAlgorithms.cpp
yulPhaser/Mutations.cpp
yulPhaser/PairSelections.cpp
yulPhaser/Phaser.cpp
yulPhaser/Population.cpp
yulPhaser/Program.cpp
yulPhaser/Selections.cpp
@ -153,9 +160,14 @@ set(yul_phaser_sources
# FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries().
# My current workaround is just to include its source files here but this introduces
# unnecessary duplication. Create a library or find a way to reuse the list in both places.
../tools/yulPhaser/AlgorithmRunner.cpp
../tools/yulPhaser/Common.cpp
../tools/yulPhaser/Chromosome.cpp
../tools/yulPhaser/FitnessMetrics.cpp
../tools/yulPhaser/GeneticAlgorithms.cpp
../tools/yulPhaser/Mutations.cpp
../tools/yulPhaser/PairSelections.cpp
../tools/yulPhaser/Phaser.cpp
../tools/yulPhaser/Population.cpp
../tools/yulPhaser/Program.cpp
../tools/yulPhaser/Selections.cpp

View File

@ -96,7 +96,8 @@ CommonOptions::CommonOptions(std::string _caption):
("optimize", po::bool_switch(&optimize), "enables optimization")
("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization")
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
("show-messages", po::bool_switch(&showMessages), "enables message output");
("show-messages", po::bool_switch(&showMessages), "enables message output")
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");
}
void CommonOptions::validate() const

View File

@ -50,6 +50,7 @@ struct CommonOptions: boost::noncopyable
bool disableSMT = false;
bool useABIEncoderV2 = false;
bool showMessages = false;
bool showMetadata = false;
langutil::EVMVersion evmVersion() const;

View File

@ -56,37 +56,37 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
}
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion)
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
EVMVersionRestrictedTestCase(_filename),
m_evmVersion(_evmVersion)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_sources = parseSourcesAndSettings(file);
m_expectations = parseExpectations(file);
m_sources = m_reader.sources();
m_expectations = parseExpectations(m_reader.stream());
}
TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
{
parseAndAnalyze();
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
return conclude(_stream, _linePrefix, _formatted);
}
bool CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted)
TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted)
{
if (m_expectations == m_errorList)
return TestResult::Success;
printExpectationAndError(_stream, _linePrefix, _formatted);
return TestResult::Failure;
}
void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted)
{
if (m_expectations != m_errorList)
{
string nextIndentLevel = _linePrefix + " ";
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
printErrorList(_stream, m_expectations, nextIndentLevel, _formatted);
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
printErrorList(_stream, m_errorList, nextIndentLevel, _formatted);
return false;
}
return true;
}
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const

View File

@ -73,7 +73,8 @@ protected:
bool _formatted = false
);
virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
TestResult conclude(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
void printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false);
static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);

View File

@ -18,16 +18,9 @@
#include <test/Common.h>
#include <test/TestCase.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/cxx11/none_of.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/range/adaptor/map.hpp>
#include <stdexcept>
#include <iostream>
using namespace std;
@ -35,13 +28,14 @@ using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::frontend::test;
void TestCase::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool)
void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const bool)
{
if (m_validatedSettings.empty())
auto& settings = m_reader.settings();
if (settings.empty())
return;
_stream << _linePrefix << "// ====" << endl;
for (auto const& setting: m_validatedSettings)
for (auto const& setting: settings)
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
}
@ -53,108 +47,12 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
!boost::starts_with(_filename.string(), ".");
}
void TestCase::validateSettings()
{
if (!m_settings.empty())
throw runtime_error(
"Unknown setting(s): " +
util::joinHumanReadable(m_settings | boost::adaptors::map_keys)
);
}
bool TestCase::shouldRun()
{
m_reader.ensureAllSettingsRead();
return m_shouldRun;
}
pair<map<string, string>, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream)
{
map<string, string> sources;
string currentSourceName;
string currentSource;
string line;
size_t lineNumber = 1;
static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("====");
static string const comment("// ");
static string const settingsDelimiter("// ====");
static string const delimiter("// ----");
bool sourcePart = true;
while (getline(_stream, line))
{
lineNumber++;
if (boost::algorithm::starts_with(line, delimiter))
break;
else if (boost::algorithm::starts_with(line, settingsDelimiter))
sourcePart = false;
else if (sourcePart)
{
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
if (!(currentSourceName.empty() && currentSource.empty()))
sources[currentSourceName] = std::move(currentSource);
currentSource = {};
currentSourceName = boost::trim_copy(line.substr(
sourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
));
if (sources.count(currentSourceName))
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
}
else
currentSource += line + "\n";
}
else if (boost::algorithm::starts_with(line, comment))
{
size_t colon = line.find(':');
if (colon == string::npos)
throw runtime_error(string("Expected \":\" inside setting."));
string key = line.substr(comment.size(), colon - comment.size());
string value = line.substr(colon + 1);
boost::algorithm::trim(key);
boost::algorithm::trim(value);
m_settings[key] = value;
}
else
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
}
sources[currentSourceName] = currentSource;
return {sources, lineNumber};
}
map<string, string> TestCase::parseSourcesAndSettings(istream& _stream)
{
return get<0>(parseSourcesAndSettingsWithLineNumbers(_stream));
}
pair<string, size_t> TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream)
{
auto [sourceMap, lineOffset] = parseSourcesAndSettingsWithLineNumbers(_stream);
if (sourceMap.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
return {std::move(sourceMap.begin()->second), lineOffset};
}
string TestCase::parseSourceAndSettings(istream& _stream)
{
return parseSourceAndSettingsWithLineNumbers(_stream).first;
}
string TestCase::parseSimpleExpectations(std::istream& _file)
{
string result;
string line;
while (getline(_file, line))
if (boost::algorithm::starts_with(line, "// "))
result += line.substr(3) + "\n";
else if (line == "//")
result += "\n";
else
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
return result;
}
void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c)
{
if (_it == _end || *_it != _c)
@ -162,18 +60,11 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu
++_it;
}
void EVMVersionRestrictedTestCase::validateSettings()
EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename):
TestCase(_filename)
{
if (!m_settings.count("EVMVersion"))
return;
string versionString = m_settings["EVMVersion"];
m_validatedSettings["EVMVersion"] = versionString;
m_settings.erase("EVMVersion");
TestCase::validateSettings();
if (versionString.empty())
string versionString = m_reader.stringSetting("EVMVersion", "any");
if (versionString == "any")
return;
string comparator;

View File

@ -17,16 +17,13 @@
#pragma once
#include <test/TestCaseReader.h>
#include <liblangutil/EVMVersion.h>
#include <boost/filesystem.hpp>
#include <functional>
#include <iosfwd>
#include <memory>
#include <string>
#include <vector>
#include <map>
namespace solidity::frontend::test
{
@ -60,31 +57,27 @@ public:
/// If @arg _formatted is true, color-coding may be used to indicate
/// error locations in the contract, if applicable.
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
/// Outputs the updated settings.
virtual void printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
/// Outputs settings.
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
/// Outputs test expectations to @arg _stream that match the actual results of the test.
/// Each line of output is prefixed with @arg _linePrefix.
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;
static bool isTestFilename(boost::filesystem::path const& _filename);
/// Validates the settings, i.e. moves them from m_settings to m_validatedSettings.
/// Throws a runtime exception if any setting is left at this class (i.e. unknown setting).
virtual void validateSettings();
/// Returns true, if the test case is supported in the current environment and false
/// otherwise which causes this test to be skipped.
/// This might check e.g. for restrictions on the EVM version.
/// The function throws an exception if there are unread settings.
bool shouldRun();
protected:
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file);
std::map<std::string, std::string> parseSourcesAndSettings(std::istream& _file);
std::pair<std::string, std::size_t> parseSourceAndSettingsWithLineNumbers(std::istream& _file);
std::string parseSourceAndSettings(std::istream& _file);
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
// Used by ASTJSONTest, the only TestCase class with a custom parser of the test files.
TestCase() = default;
static std::string parseSimpleExpectations(std::istream& _file);
TestCase(std::string const& _filename): m_reader(_filename) {}
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);
template<typename IteratorType>
static void skipWhitespace(IteratorType& _it, IteratorType _end)
@ -100,18 +93,14 @@ protected:
++_it;
}
/// Parsed settings.
std::map<std::string, std::string> m_settings;
/// Updated settings after validation.
std::map<std::string, std::string> m_validatedSettings;
TestCaseReader m_reader;
bool m_shouldRun = true;
};
class EVMVersionRestrictedTestCase: public TestCase
{
public:
void validateSettings() override;
protected:
EVMVersionRestrictedTestCase(std::string const& _filename);
};
}

164
test/TestCaseReader.cpp Normal file
View File

@ -0,0 +1,164 @@
/*
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/>.
*/
#include <test/TestCaseReader.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/throw_exception.hpp>
using namespace std;
using namespace solidity::frontend::test;
TestCaseReader::TestCaseReader(string const& _filename):
m_file(_filename)
{
if (!m_file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
m_file.exceptions(ios::badbit);
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file);
m_unreadSettings = m_settings;
}
string const& TestCaseReader::source()
{
if (m_sources.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources."));
return m_sources.begin()->second;
}
string TestCaseReader::simpleExpectations()
{
return parseSimpleExpectations(m_file);
}
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
{
if (m_settings.count(_name) == 0)
return _defaultValue;
m_unreadSettings.erase(_name);
string value = m_settings.at(_name);
if (value == "false")
return false;
if (value == "true")
return true;
BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + "."));
}
size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue)
{
if (m_settings.count(_name) == 0)
return _defaultValue;
m_unreadSettings.erase(_name);
static_assert(sizeof(unsigned long) <= sizeof(size_t));
return stoul(m_settings.at(_name));
}
string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue)
{
if (m_settings.count(_name) == 0)
return _defaultValue;
m_unreadSettings.erase(_name);
return m_settings.at(_name);
}
void TestCaseReader::ensureAllSettingsRead() const
{
if (!m_unreadSettings.empty())
throw runtime_error(
"Unknown setting(s): " +
util::joinHumanReadable(m_unreadSettings | boost::adaptors::map_keys)
);
}
pair<map<string, string>, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
{
map<string, string> sources;
string currentSourceName;
string currentSource;
string line;
size_t lineNumber = 1;
static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("====");
static string const comment("// ");
static string const settingsDelimiter("// ====");
static string const delimiter("// ----");
bool sourcePart = true;
while (getline(_stream, line))
{
lineNumber++;
if (boost::algorithm::starts_with(line, delimiter))
break;
else if (boost::algorithm::starts_with(line, settingsDelimiter))
sourcePart = false;
else if (sourcePart)
{
if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
if (!(currentSourceName.empty() && currentSource.empty()))
sources[currentSourceName] = std::move(currentSource);
currentSource = {};
currentSourceName = boost::trim_copy(line.substr(
sourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size()
));
if (sources.count(currentSourceName))
throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".");
}
else
currentSource += line + "\n";
}
else if (boost::algorithm::starts_with(line, comment))
{
size_t colon = line.find(':');
if (colon == string::npos)
throw runtime_error(string("Expected \":\" inside setting."));
string key = line.substr(comment.size(), colon - comment.size());
string value = line.substr(colon + 1);
boost::algorithm::trim(key);
boost::algorithm::trim(value);
m_settings[key] = value;
}
else
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
}
sources[currentSourceName] = currentSource;
return { sources, lineNumber };
}
string TestCaseReader::parseSimpleExpectations(istream& _file)
{
string result;
string line;
while (getline(_file, line))
if (boost::algorithm::starts_with(line, "// "))
result += line.substr(3) + "\n";
else if (line == "//")
result += "\n";
else
BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \"."));
return result;
}

59
test/TestCaseReader.h Normal file
View File

@ -0,0 +1,59 @@
/*
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/>.
*/
#include <fstream>
#include <map>
#include <string>
#pragma once
namespace solidity::frontend::test
{
/**
* A reader for test case data file, which parses source, settings and (optionally) simple expectations.
*/
class TestCaseReader
{
public:
TestCaseReader() = default;
explicit TestCaseReader(std::string const& _filename);
std::map<std::string, std::string> const& sources() { return m_sources; }
std::string const& source();
std::size_t lineNumber() { return m_lineNumber; }
std::map<std::string, std::string> const& settings() { return m_settings; }
std::ifstream& stream() { return m_file; }
std::string simpleExpectations();
bool boolSetting(std::string const& _name, bool _defaultValue);
size_t sizetSetting(std::string const& _name, size_t _defaultValue);
std::string stringSetting(std::string const& _name, std::string const& _defaultValue);
void ensureAllSettingsRead() const;
private:
std::pair<std::map<std::string, std::string>, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
static std::string parseSimpleExpectations(std::istream& _file);
std::ifstream m_file;
std::map<std::string, std::string> m_sources;
std::size_t m_lineNumber = 0;
std::map<std::string, std::string> m_settings;
std::map<std::string, std::string> m_unreadSettings; ///< tracks which settings are left unread
};
}

View File

@ -94,7 +94,6 @@ int registerTests(
{
stringstream errorStream;
auto testCase = _testCaseCreator(config);
testCase->validateSettings();
if (testCase->shouldRun())
switch (testCase->run(errorStream))
{

View File

@ -33,6 +33,7 @@ set -e
REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build}
source "${REPO_ROOT}/scripts/common.sh"
source "${REPO_ROOT}/scripts/common_cmdline.sh"
case "$OSTYPE" in
msys)
@ -45,6 +46,7 @@ case "$OSTYPE" in
SOLC="$REPO_ROOT/${SOLIDITY_BUILD_DIR}/solc/solc"
;;
esac
echo "${SOLC}"
INTERACTIVE=true
if ! tty -s || [ "$CI" ]
@ -52,8 +54,6 @@ then
INTERACTIVE=""
fi
FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
# extend stack size in case we run via ASAN
if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]]; then
ulimit -s 16384
@ -62,57 +62,6 @@ fi
## FUNCTIONS
function compileFull()
{
local expected_exit_code=0
local expect_output=0
if [[ $1 = '-e' ]]
then
expected_exit_code=1
expect_output=1
shift;
fi
if [[ $1 = '-w' ]]
then
expect_output=1
shift;
fi
if [[ $1 = '-o' ]]
then
expect_output=2
shift;
fi
local files="$*"
local output
local stderr_path=$(mktemp)
set +e
"$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path"
local exit_code=$?
local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path")
set -e
rm "$stderr_path"
if [[ \
"$exit_code" -ne "$expected_exit_code" || \
( $expect_output -eq 0 && -n "$errors" ) || \
( $expect_output -eq 1 && -z "$errors" ) \
]]
then
printError "Unexpected compilation result:"
printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output"
printError "Was failure: $exit_code"
echo "$errors"
printError "While calling:"
echo "\"$SOLC\" $FULLARGS $files"
printError "Inside directory:"
pwd
false
fi
}
function ask_expectation_update()
{
if [ $INTERACTIVE ]

View File

@ -36,15 +36,11 @@ using namespace solidity::util;
using namespace solidity::frontend;
using namespace solidity::frontend::test;
ABIJsonTest::ABIJsonTest(string const& _filename)
ABIJsonTest::ABIJsonTest(string const& _filename):
TestCase(_filename)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
m_expectation = parseSimpleExpectations(file);
m_source = m_reader.source();
m_expectation = m_reader.simpleExpectations();
}
TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)

View File

@ -54,7 +54,7 @@
"storageLocation": "memory",
"typeDescriptions":
{
"typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr",
"typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr",
"typeString": "uint256[][]"
},
"typeName":

View File

@ -36,35 +36,14 @@ using namespace std;
namespace fs = boost::filesystem;
using namespace boost::unit_test;
GasTest::GasTest(string const& _filename)
GasTest::GasTest(string const& _filename):
TestCase(_filename)
{
ifstream file(_filename);
if (!file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file);
if (m_settings.count("optimize"))
{
m_optimise = true;
m_validatedSettings["optimize"] = "true";
m_settings.erase("optimize");
}
if (m_settings.count("optimize-yul"))
{
m_optimiseYul = true;
m_validatedSettings["optimize-yul"] = "true";
m_settings.erase("optimize-yul");
}
if (m_settings.count("optimize-runs"))
{
m_optimiseRuns = stoul(m_settings["optimize-runs"]);
m_validatedSettings["optimize-runs"] = m_settings["optimize-runs"];
m_settings.erase("optimize-runs");
}
parseExpectations(file);
m_source = m_reader.source();
m_optimise = m_reader.boolSetting("optimize", false);
m_optimiseYul = m_reader.boolSetting("optimize-yul", false);
m_optimiseRuns = m_reader.sizetSetting("optimize-runs", 200);
parseExpectations(m_reader.stream());
}
void GasTest::parseExpectations(std::istream& _stream)

View File

@ -135,7 +135,7 @@ TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _li
}
}
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
return conclude(_stream, _linePrefix, _formatted);
}
vector<string> SMTCheckerJSONTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib)

View File

@ -28,9 +28,7 @@ using namespace solidity::frontend::test;
SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion)
{
if (m_settings.count("SMTSolvers"))
{
auto const& choice = m_settings.at("SMTSolvers");
auto const& choice = m_reader.stringSetting("SMTSolvers", "any");
if (choice == "any")
m_enabledSolvers = smt::SMTSolverChoice::All();
else if (choice == "z3")
@ -41,9 +39,6 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _ev
m_enabledSolvers = smt::SMTSolverChoice::None();
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice."));
}
else
m_enabledSolvers = smt::SMTSolverChoice::All();
auto available = ModelChecker::availableSolvers();
if (!available.z3)
@ -62,5 +57,5 @@ TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePr
parseAndAnalyze();
filterObtainedErrors();
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
return conclude(_stream, _linePrefix, _formatted);
}

View File

@ -37,59 +37,42 @@ namespace fs = boost::filesystem;
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion):
SolidityExecutionFramework(_evmVersion)
SolidityExecutionFramework(_evmVersion),
EVMVersionRestrictedTestCase(_filename)
{
ifstream file(_filename);
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
file.exceptions(ios::badbit);
m_source = m_reader.source();
m_lineOffset = m_reader.lineNumber();
std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file);
if (m_settings.count("compileViaYul"))
string choice = m_reader.stringSetting("compileViaYul", "false");
if (choice == "also")
{
if (m_settings["compileViaYul"] == "also")
{
m_validatedSettings["compileViaYul"] = m_settings["compileViaYul"];
m_runWithYul = true;
m_runWithoutYul = true;
}
else
else if (choice == "true")
{
m_validatedSettings["compileViaYul"] = "only";
m_runWithYul = true;
m_runWithoutYul = false;
}
m_settings.erase("compileViaYul");
}
if (m_settings.count("ABIEncoderV1Only"))
else if (choice == "false")
{
if (m_settings["ABIEncoderV1Only"] == "true")
{
m_validatedSettings["ABIEncoderV1Only"] = "true";
m_runWithABIEncoderV1Only = true;
}
m_settings.erase("ABIEncoderV1Only");
m_runWithYul = false;
m_runWithoutYul = true;
}
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + "."));
m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
m_shouldRun = false;
if (m_settings.count("revertStrings"))
{
auto revertStrings = revertStringsFromString(m_settings["revertStrings"]);
if (revertStrings)
m_revertStrings = *revertStrings;
m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings);
m_settings.erase("revertStrings");
}
auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
soltestAssert(revertStrings, "Invalid revertStrings setting.");
m_revertStrings = revertStrings.value();
if (m_settings.count("allowNonExistingFunctions"))
{
m_validatedSettings["allowNonExistingFunctions"] = true;
m_settings.erase("allowNonExistingFunctions");
}
m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false);
parseExpectations(file);
parseExpectations(m_reader.stream());
soltestAssert(!m_tests.empty(), "No tests specified in " + _filename);
}
@ -152,7 +135,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
else
{
soltestAssert(
m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);

View File

@ -65,6 +65,7 @@ private:
bool m_runWithYul = false;
bool m_runWithoutYul = true;
bool m_runWithABIEncoderV1Only = false;
bool m_allowNonExistingFunctions = false;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
*/
#include <cstdlib>
#include <iostream>
#include <boost/test/framework.hpp>
#include <test/libsolidity/SolidityExecutionFramework.h>
@ -60,6 +61,7 @@ bytes SolidityExecutionFramework::compileContract(
formatter.printErrorInformation(*error);
BOOST_ERROR("Compiling contract failed");
}
std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
evmasm::LinkerObject obj;
if (m_compileViaYul)
{
@ -70,9 +72,7 @@ bytes SolidityExecutionFramework::compileContract(
// get code that does not exhaust the stack.
OptimiserSettings::full()
);
if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(
_contractName.empty() ? m_compiler.lastContractName() : _contractName
)))
if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)))
{
langutil::SourceReferenceFormatter formatter(std::cerr);
@ -84,7 +84,9 @@ bytes SolidityExecutionFramework::compileContract(
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
}
else
obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
obj = m_compiler.object(contractName);
BOOST_REQUIRE(obj.linkReferences.empty());
if (m_showMetadata)
cout << "metadata: " << m_compiler.metadata(contractName) << endl;
return obj.bytecode;
}

View File

@ -41,9 +41,9 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework
{
public:
SolidityExecutionFramework() {}
SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {}
explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion):
ExecutionFramework(_evmVersion)
ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata)
{}
virtual bytes const& compileAndRunWithoutCheck(
@ -68,6 +68,7 @@ public:
protected:
solidity::frontend::CompilerStack m_compiler;
bool m_compileViaYul = false;
bool m_showMetadata = false;
RevertStrings m_revertStrings = RevertStrings::Default;
};

View File

@ -37,20 +37,7 @@ namespace fs = boost::filesystem;
SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion)
{
if (m_settings.count("optimize-yul"))
{
if (m_settings["optimize-yul"] == "true")
{
m_validatedSettings["optimize-yul"] = "true";
m_settings.erase("optimize-yul");
}
else if (m_settings["optimize-yul"] == "false")
{
m_validatedSettings["optimize-yul"] = "false";
m_settings.erase("optimize-yul");
m_optimiseYul = false;
}
}
m_optimiseYul = m_reader.boolSetting("optimize-yul", true);
m_parserErrorRecovery = _parserErrorRecovery;
}
@ -60,7 +47,7 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix
parseAndAnalyze();
filterObtainedErrors();
return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure;
return conclude(_stream, _linePrefix, _formatted);
}
void SyntaxTest::setupCompiler()

View File

@ -14,9 +14,9 @@ contract C {
}
// ----
// creation:
// codeDepositCost: 1120000
// executionCost: 1160
// totalCost: 1121160
// codeDepositCost: 1094400
// executionCost: 1134
// totalCost: 1095534
// external:
// a(): 1130
// b(uint256): infinite

View File

@ -0,0 +1,8 @@
contract C {
function f(bytes calldata data) external pure returns (uint256[] memory) {
return abi.decode(data, (uint256[]));
}
}
// ----
// f(bytes): 0x20, 0xc0, 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 -> 0x20, 0x4, 0x3, 0x4, 0x5, 0x6

View File

@ -0,0 +1,12 @@
contract C {
function f(bytes calldata data)
external
pure
returns (uint256[2][3] memory)
{
return abi.decode(data, (uint256[2][3]));
}
}
// ----
// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6

View File

@ -0,0 +1,15 @@
pragma experimental ABIEncoderV2;
contract C {
function f(bytes calldata data)
external
pure
returns (uint256[2][3] memory)
{
return abi.decode(data, (uint256[2][3]));
}
}
// ----
// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6

View File

@ -0,0 +1,8 @@
contract C {
function f(bytes memory data) public pure returns (uint256) {
return abi.decode(data, (uint256));
}
}
// ----
// f(bytes): 0x20, 0x20, 0x21 -> 33

View File

@ -0,0 +1,22 @@
pragma experimental ABIEncoderV2;
contract C {
struct S {
uint256 a;
uint256[] b;
}
function f() public pure returns (S memory) {
S memory s;
s.a = 8;
s.b = new uint256[](3);
s.b[0] = 9;
s.b[1] = 10;
s.b[2] = 11;
return abi.decode(abi.encode(s), (S));
}
}
// ----
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb

View File

@ -0,0 +1,16 @@
pragma experimental ABIEncoderV2;
contract C {
struct S {
uint256 a;
uint256[] b;
}
function f(bytes calldata data) external pure returns (S memory) {
return abi.decode(data, (S));
}
}
// ----
// f(bytes): 0x20, 0xe0, 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc -> 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc

View File

@ -0,0 +1,24 @@
pragma experimental ABIEncoderV2;
contract C {
bytes data;
struct S {
uint256 a;
uint256[] b;
}
function f() public returns (S memory) {
S memory s;
s.a = 8;
s.b = new uint256[](3);
s.b[0] = 9;
s.b[1] = 10;
s.b[2] = 11;
data = abi.encode(s);
return abi.decode(data, (S));
}
}
// ----
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb

View File

@ -0,0 +1,36 @@
contract C {
function f0() public returns (bytes memory) {
return abi.encode();
}
function f1() public returns (bytes memory) {
return abi.encode(1, 2);
}
function f2() public returns (bytes memory) {
string memory x = "abc";
return abi.encode(1, x, 2);
}
function f3() public returns (bytes memory r) {
// test that memory is properly allocated
string memory x = "abc";
r = abi.encode(1, x, 2);
bytes memory y = "def";
require(y[0] == "d");
y[0] = "e";
require(y[0] == "e");
}
function f4() public returns (bytes memory) {
bytes4 x = "abcd";
return abi.encode(bytes2(x));
}
}
// ----
// f0() -> 0x20, 0x0
// f1() -> 0x20, 0x40, 0x1, 0x2
// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
// f4() -> 0x20, 0x20, "ab"

View File

@ -0,0 +1,26 @@
contract C {
bool x;
function c(uint256 a, uint256[] memory b) public {
require(a == 5);
require(b.length == 2);
require(b[0] == 6);
require(b[1] == 7);
x = true;
}
function f() public returns (bool) {
uint256 a = 5;
uint256[] memory b = new uint256[](2);
b[0] = 6;
b[1] = 7;
(bool success, ) = address(this).call(
abi.encodeWithSignature("c(uint256,uint256[])", a, b)
);
require(success);
return x;
}
}
// ----
// f() -> true

View File

@ -0,0 +1,9 @@
contract C {
function f() public pure returns (uint256, bytes memory) {
bytes memory arg = "abcdefg";
return abi.decode(abi.encode(uint256(33), arg), (uint256, bytes));
}
}
// ----
// f() -> 0x21, 0x40, 0x7, "abcdefg"

View File

@ -0,0 +1,9 @@
// Tests that rational numbers (even negative ones) are encoded properly.
contract C {
function f() public pure returns (bytes memory) {
return abi.encode(1, -2);
}
}
// ----
// f() -> 0x20, 0x40, 0x1, -2

View File

@ -0,0 +1,13 @@
// Tests that this will not end up using a "bytes0" type
// (which would assert)
pragma experimental ABIEncoderV2;
contract C {
function f() public pure returns (bytes memory, bytes memory) {
return (abi.encode(""), abi.encodePacked(""));
}
}
// ----
// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0

View File

@ -0,0 +1,12 @@
// Tests that rational numbers (even negative ones) are encoded properly.
pragma experimental ABIEncoderV2;
contract C {
function f() public pure returns (bytes memory) {
return abi.encode(1, -2);
}
}
// ----
// f() -> 0x20, 0x40, 0x1, -2

View File

@ -0,0 +1,53 @@
pragma experimental ABIEncoderV2;
contract C {
struct S {
uint256 a;
uint256[] b;
}
function f0() public pure returns (bytes memory) {
return abi.encode();
}
function f1() public pure returns (bytes memory) {
return abi.encode(1, 2);
}
function f2() public pure returns (bytes memory) {
string memory x = "abc";
return abi.encode(1, x, 2);
}
function f3() public pure returns (bytes memory r) {
// test that memory is properly allocated
string memory x = "abc";
r = abi.encode(1, x, 2);
bytes memory y = "def";
require(y[0] == "d");
y[0] = "e";
require(y[0] == "e");
}
S s;
function f4() public returns (bytes memory r) {
string memory x = "abc";
s.a = 7;
s.b.push(2);
s.b.push(3);
r = abi.encode(1, x, s, 2);
bytes memory y = "def";
require(y[0] == "d");
y[0] = "e";
require(y[0] == "e");
}
}
// ----
// f0() -> 0x20, 0x0
// f1() -> 0x20, 0x40, 0x1, 0x2
// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc"
// f4() -> 0x20, 0x160, 0x1, 0x80, 0xc0, 0x2, 0x3, "abc", 0x7, 0x40, 0x2, 0x2, 0x3

View File

@ -0,0 +1,6 @@
contract Lotto {
uint256 public constant ticketPrice = 555;
}
// ----
// ticketPrice() -> 555

View File

@ -0,0 +1,8 @@
contract Lotto {
uint256 public ticketPrice = 500;
}
// ====
// compileViaYul: also
// ----
// ticketPrice() -> 500

View File

@ -0,0 +1,12 @@
contract C {
function test() public returns (uint256) {
// Note that this only works because computation on literals is done using
// unbounded integers.
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1;
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2;
return 0;
}
}
// ----
// test() -> 0

View File

@ -0,0 +1,24 @@
contract C {
function f(uint256 d) public pure returns (uint256) {
addmod(1, 2, d);
return 2;
}
function g(uint256 d) public pure returns (uint256) {
mulmod(1, 2, d);
return 2;
}
function h() public pure returns (uint256) {
mulmod(0, 1, 2);
mulmod(1, 0, 2);
addmod(0, 1, 2);
addmod(1, 0, 2);
return 2;
}
}
// ----
// f(uint256): 0 -> FAILURE
// g(uint256): 0 -> FAILURE
// h() -> 2

Some files were not shown because too many files have changed in this diff Show More