aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md3
-rw-r--r--libdevcore/CommonIO.cpp20
-rw-r--r--libdevcore/CommonIO.h6
-rw-r--r--libevmasm/RuleList.h2
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp214
-rw-r--r--libsolidity/analysis/TypeChecker.cpp15
-rw-r--r--libsolidity/analysis/TypeChecker.h1
-rw-r--r--libsolidity/ast/AST.cpp5
-rw-r--r--libsolidity/ast/AST.h1
-rw-r--r--libsolidity/ast/Types.cpp18
-rw-r--r--libsolidity/ast/Types.h4
-rw-r--r--libsolidity/interface/CompilerStack.cpp25
-rw-r--r--libsolidity/interface/CompilerStack.h7
-rw-r--r--solc/CommandLineInterface.cpp8
-rwxr-xr-xtest/cmdlineTests.sh50
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp80
-rw-r--r--test/libsolidity/SolidityTypes.cpp3
-rw-r--r--test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol9
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/enum_from_interface.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol12
-rw-r--r--test/libsolidity/syntaxTests/parsing/enum_from_library.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol9
24 files changed, 354 insertions, 166 deletions
diff --git a/Changelog.md b/Changelog.md
index e00f74b3..0790c0ca 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -69,6 +69,7 @@ Language Features:
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
* General: Support ``pop()`` for storage arrays.
* General: Scoping rules now follow the C99-style.
+ * General: Allow ``enum``s in interfaces.
Compiler Features:
* C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods.
@@ -79,10 +80,12 @@ Compiler Features:
Bugfixes:
* Tests: Fix chain parameters to make ipc tests work with newer versions of cpp-ethereum.
* Code Generator: Fix allocation of byte arrays (zeroed out too much memory).
+ * Code Generator: Properly handle negative number literals in ABIEncoderV2.
* Commandline Interface: Correctly handle paths with backslashes on windows.
* Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions.
* References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own.
* References Resolver: Enforce ``storage`` as data location for mappings.
+ * References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``.
* References Resolver: Report error instead of assertion fail when FunctionType has an undeclared type as parameter.
* Type Checker: Disallow assignments to mappings within tuple assignments as well.
* Type Checker: Allow assignments to local variables of mapping types.
diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp
index 0063a8d4..9693d02a 100644
--- a/libdevcore/CommonIO.cpp
+++ b/libdevcore/CommonIO.cpp
@@ -187,3 +187,23 @@ boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::pa
return head / tail;
}
}
+
+string dev::absolutePath(string const& _path, string const& _reference)
+{
+ boost::filesystem::path p(_path);
+ // Anything that does not start with `.` is an absolute path.
+ if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != ".."))
+ return _path;
+ boost::filesystem::path result(_reference);
+ result.remove_filename();
+ for (boost::filesystem::path::iterator it = p.begin(); it != p.end(); ++it)
+ if (*it == "..")
+ result = result.parent_path();
+ else if (*it != ".")
+ result /= *it;
+ return result.generic_string();
+}
+
+string dev::sanitizePath(string const& _path) {
+ return boost::filesystem::path(_path).generic_string();
+}
diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h
index 9ba68e74..928b6d15 100644
--- a/libdevcore/CommonIO.h
+++ b/libdevcore/CommonIO.h
@@ -62,4 +62,10 @@ std::string toString(_T const& _t)
/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped.
boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path);
+/// @returns the absolute path corresponding to @a _path relative to @a _reference.
+std::string absolutePath(std::string const& _path, std::string const& _reference);
+
+/// Helper function to return path converted strings.
+std::string sanitizePath(std::string const& _path);
+
}
diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h
index 7a2bc484..0573856b 100644
--- a/libevmasm/RuleList.h
+++ b/libevmasm/RuleList.h
@@ -46,7 +46,7 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
/// @returns a list of simplification rules given certain match placeholders.
/// A, B and C should represent constants, X and Y arbitrary expressions.
-/// The simplifications should neven change the order of evaluation of
+/// The simplifications should never change the order of evaluation of
/// arbitrary operations.
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleList(
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 5458c1b0..501521f5 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -297,134 +297,138 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
if (_variable.annotation().type)
return;
+ if (!_variable.typeName())
+ {
+ // This can still happen in very unusual cases where a developer uses constructs, such as
+ // `var a;`, however, such code will have generated errors already.
+ // However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
+ // invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
+ // after this step.
+ return;
+ }
+
TypePointer type;
- if (_variable.typeName())
+ type = _variable.typeName()->annotation().type;
+ using Location = VariableDeclaration::Location;
+ Location varLoc = _variable.referenceLocation();
+ DataLocation typeLoc = DataLocation::Memory;
+ // References are forced to calldata for external function parameters (not return)
+ // and memory for parameters (also return) of publicly visible functions.
+ // They default to memory for function parameters and storage for local variables.
+ // As an exception, "storage" is allowed for library functions.
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
- type = _variable.typeName()->annotation().type;
- using Location = VariableDeclaration::Location;
- Location varLoc = _variable.referenceLocation();
- DataLocation typeLoc = DataLocation::Memory;
- // References are forced to calldata for external function parameters (not return)
- // and memory for parameters (also return) of publicly visible functions.
- // They default to memory for function parameters and storage for local variables.
- // As an exception, "storage" is allowed for library functions.
- if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+ bool isPointer = true;
+ if (_variable.isExternalCallableParameter())
{
- bool isPointer = true;
- if (_variable.isExternalCallableParameter())
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
+ if (contract.isLibrary())
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(
- *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
- );
- if (contract.isLibrary())
- {
- if (varLoc == Location::Memory)
- fatalTypeError(_variable.location(),
- "Location has to be calldata or storage for external "
- "library functions (remove the \"memory\" keyword)."
- );
- }
- else
- {
- // force location of external function parameters (not return) to calldata
- if (varLoc != Location::CallData && varLoc != Location::Default)
- fatalTypeError(_variable.location(),
- "Location has to be calldata for external functions "
- "(remove the \"memory\" or \"storage\" keyword)."
- );
- }
- if (varLoc == Location::Default)
- typeLoc = DataLocation::CallData;
- else
- typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ if (varLoc == Location::Memory)
+ fatalTypeError(_variable.location(),
+ "Location has to be calldata or storage for external "
+ "library functions (remove the \"memory\" keyword)."
+ );
}
- else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
+ else
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(
- *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ // force location of external function parameters (not return) to calldata
+ if (varLoc != Location::CallData && varLoc != Location::Default)
+ fatalTypeError(_variable.location(),
+ "Location has to be calldata for external functions "
+ "(remove the \"memory\" or \"storage\" keyword)."
+ );
+ }
+ if (varLoc == Location::Default)
+ typeLoc = DataLocation::CallData;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ }
+ else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
+ {
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
+ // force locations of public or external function (return) parameters to memory
+ if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary())
+ fatalTypeError(_variable.location(),
+ "Location has to be memory for publicly visible functions "
+ "(remove the \"storage\" or \"calldata\" keyword)."
);
- // force locations of public or external function (return) parameters to memory
- if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary())
+ if (varLoc == Location::Default || !contract.isLibrary())
+ typeLoc = DataLocation::Memory;
+ else
+ {
+ if (varLoc == Location::CallData)
fatalTypeError(_variable.location(),
- "Location has to be memory for publicly visible functions "
- "(remove the \"storage\" or \"calldata\" keyword)."
+ "Location cannot be calldata for non-external functions "
+ "(remove the \"calldata\" keyword)."
+ );
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ }
+ }
+ else
+ {
+ if (_variable.isConstant())
+ {
+ if (varLoc != Location::Default && varLoc != Location::Memory)
+ fatalTypeError(
+ _variable.location(),
+ "Data location has to be \"memory\" (or unspecified) for constants."
);
- if (varLoc == Location::Default || !contract.isLibrary())
+ typeLoc = DataLocation::Memory;
+ }
+ else if (varLoc == Location::Default)
+ {
+ if (_variable.isCallableParameter())
typeLoc = DataLocation::Memory;
else
{
- if (varLoc == Location::CallData)
- fatalTypeError(_variable.location(),
- "Location cannot be calldata for non-external functions "
- "(remove the \"calldata\" keyword)."
+ typeLoc = DataLocation::Storage;
+ if (_variable.isLocalVariable())
+ typeError(
+ _variable.location(),
+ "Data location must be specified as either \"memory\" or \"storage\"."
);
- typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
}
else
{
- if (_variable.isConstant())
+ switch (varLoc)
{
- if (varLoc != Location::Default && varLoc != Location::Memory)
- fatalTypeError(
- _variable.location(),
- "Data location has to be \"memory\" (or unspecified) for constants."
- );
+ case Location::Memory:
typeLoc = DataLocation::Memory;
+ break;
+ case Location::Storage:
+ typeLoc = DataLocation::Storage;
+ break;
+ case Location::CallData:
+ fatalTypeError(_variable.location(),
+ "Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)."
+ );
+ break;
+ default:
+ solAssert(false, "Unknown data location");
}
- else if (varLoc == Location::Default)
- {
- if (_variable.isCallableParameter())
- typeLoc = DataLocation::Memory;
- else
- {
- typeLoc = DataLocation::Storage;
- if (_variable.isLocalVariable())
- typeError(
- _variable.location(),
- "Data location must be specified as either \"memory\" or \"storage\"."
- );
- }
- }
- else
- {
- switch (varLoc)
- {
- case Location::Memory:
- typeLoc = DataLocation::Memory;
- break;
- case Location::Storage:
- typeLoc = DataLocation::Storage;
- break;
- case Location::CallData:
- fatalTypeError(_variable.location(),
- "Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)."
- );
- break;
- default:
- solAssert(false, "Unknown data location");
- }
- }
- isPointer = !_variable.isStateVariable();
}
- type = ref->copyForLocation(typeLoc, isPointer);
- }
- else if (dynamic_cast<MappingType const*>(type.get()))
- {
- if (_variable.isLocalVariable() && varLoc != Location::Storage)
- typeError(
- _variable.location(),
- "Data location for mappings must be specified as \"storage\"."
- );
+ isPointer = !_variable.isStateVariable();
}
- else if (varLoc != Location::Default && !ref)
- typeError(_variable.location(), "Data location can only be given for array or struct types.");
-
- _variable.annotation().type = type;
+ type = ref->copyForLocation(typeLoc, isPointer);
}
- else if (!_variable.canHaveAutoType())
- typeError(_variable.location(), "Explicit type needed.");
- // otherwise we have a "var"-declaration whose type is resolved by the first assignment
+ else if (dynamic_cast<MappingType const*>(type.get()))
+ {
+ if (_variable.isLocalVariable() && varLoc != Location::Storage)
+ typeError(
+ _variable.location(),
+ "Data location for mappings must be specified as \"storage\"."
+ );
+ }
+ else if (varLoc != Location::Default && !ref)
+ typeError(_variable.location(), "Data location can only be given for array or struct types.");
+
+ _variable.annotation().type = type;
}
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 7cec7c43..38331a43 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -751,13 +751,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
return false;
}
-bool TypeChecker::visit(EnumDefinition const& _enum)
-{
- if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- m_errorReporter.typeError(_enum.location(), "Enumerable cannot be declared in interfaces.");
- return false;
-}
-
void TypeChecker::visitManually(
ModifierInvocation const& _modifier,
vector<ContractDefinition const*> const& _bases
@@ -867,6 +860,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return size_t(-1);
Declaration const* declaration = ref->second.declaration;
solAssert(!!declaration, "");
+ bool requiresStorage = ref->second.isSlot || ref->second.isOffset;
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
if (var->isConstant())
@@ -874,7 +868,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly.");
return size_t(-1);
}
- else if (ref->second.isSlot || ref->second.isOffset)
+ else if (requiresStorage)
{
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
{
@@ -906,6 +900,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return size_t(-1);
}
}
+ else if (requiresStorage)
+ {
+ m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
+ return size_t(-1);
+ }
else if (_context == julia::IdentifierContext::LValue)
{
m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 47892a3f..b696de85 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -96,7 +96,6 @@ private:
virtual bool visit(StructDefinition const& _struct) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(VariableDeclaration const& _variable) override;
- virtual bool visit(EnumDefinition const& _enum) override;
/// We need to do this manually because we want to pass the bases of the current contract in
/// case this is a base constructor call.
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 7c3869a8..23797d52 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -466,11 +466,6 @@ bool VariableDeclaration::isExternalCallableParameter() const
return false;
}
-bool VariableDeclaration::canHaveAutoType() const
-{
- return isLocalVariable() && !isCallableParameter();
-}
-
TypePointer VariableDeclaration::type() const
{
return annotation().type;
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index d0d3b1ef..69c6fa05 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -696,7 +696,6 @@ public:
bool isExternalCallableParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
/// in the body of a function or modifier.
- bool canHaveAutoType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 7bcae812..3eccc6d4 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -169,15 +169,6 @@ pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const
return nullptr;
}
-MemberList& MemberList::operator=(MemberList&& _other)
-{
- solAssert(&_other != this, "");
-
- m_memberTypes = move(_other.m_memberTypes);
- m_storageOffsets = move(_other.m_storageOffsets);
- return *this;
-}
-
void MemberList::combine(MemberList const & _other)
{
m_memberTypes += _other.m_memberTypes;
@@ -1146,7 +1137,14 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
string RationalNumberType::richIdentifier() const
{
- return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str();
+ // rational seemingly will put the sign always on the numerator,
+ // but let just make it deterministic here.
+ bigint numerator = abs(m_value.numerator());
+ bigint denominator = abs(m_value.denominator());
+ if (m_value < 0)
+ return "t_rational_minus_" + numerator.str() + "_by_" + denominator.str();
+ else
+ return "t_rational_" + numerator.str() + "_by_" + denominator.str();
}
bool RationalNumberType::operator==(Type const& _other) const
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 89b8f170..09323d05 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -95,9 +95,7 @@ public:
using MemberMap = std::vector<Member>;
- MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
- MemberList& operator=(MemberList&& _other);
void combine(MemberList const& _other);
TypePointer memberType(std::string const& _name) const
{
@@ -132,6 +130,8 @@ private:
mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
};
+static_assert(std::is_nothrow_move_constructible<MemberList>::value, "MemberList should be noexcept move constructible");
+
/**
* Abstract base class that forms the root of the type hierarchy.
*/
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 29f9ce7d..5cfd1758 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -577,7 +577,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
for (auto const& node: _ast.nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{
- string importPath = absolutePath(import->path(), _sourcePath);
+ string importPath = dev::absolutePath(import->path(), _sourcePath);
// The current value of `path` is the absolute path as seen from this source file.
// We first have to apply remappings before we can store the actual absolute path
// as seen globally.
@@ -620,8 +620,8 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
for (auto const& redir: m_remappings)
{
- string context = sanitizePath(redir.context);
- string prefix = sanitizePath(redir.prefix);
+ string context = dev::sanitizePath(redir.context);
+ string prefix = dev::sanitizePath(redir.prefix);
// Skip if current context is closer
if (context.length() < longestContext)
@@ -638,7 +638,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
longestContext = context.length();
longestPrefix = prefix.length();
- bestMatchTarget = sanitizePath(redir.target);
+ bestMatchTarget = dev::sanitizePath(redir.target);
}
string path = bestMatchTarget;
path.append(_path.begin() + longestPrefix, _path.end());
@@ -675,23 +675,6 @@ void CompilerStack::resolveImports()
swap(m_sourceOrder, sourceOrder);
}
-string CompilerStack::absolutePath(string const& _path, string const& _reference)
-{
- using path = boost::filesystem::path;
- path p(_path);
- // Anything that does not start with `.` is an absolute path.
- if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != ".."))
- return _path;
- path result(_reference);
- result.remove_filename();
- for (path::iterator it = p.begin(); it != p.end(); ++it)
- if (*it == "..")
- result = result.parent_path();
- else if (*it != ".")
- result /= *it;
- return result.generic_string();
-}
-
namespace
{
bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index b7d07ca7..2234a8c9 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -36,7 +36,6 @@
#include <json/json.h>
#include <boost/noncopyable.hpp>
-#include <boost/filesystem.hpp>
#include <ostream>
#include <string>
@@ -267,12 +266,6 @@ private:
std::string applyRemapping(std::string const& _path, std::string const& _context);
void resolveImports();
- /// @returns the absolute path corresponding to @a _path relative to @a _reference.
- static std::string absolutePath(std::string const& _path, std::string const& _reference);
-
- /// Helper function to return path converted strings.
- static std::string sanitizePath(std::string const& _path) { return boost::filesystem::path(_path).generic_string(); }
-
/// @returns true if the contract is requested to be compiled.
bool isRequestedContract(ContractDefinition const& _contract) const;
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 14793759..429bd637 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -402,11 +402,11 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings()
{
if (!ignoreMissing)
{
- cerr << "\"" << infile << "\" is not found" << endl;
+ cerr << infile << " is not found." << endl;
return false;
}
else
- cerr << "\"" << infile << "\" is not found. Skipping." << endl;
+ cerr << infile << " is not found. Skipping." << endl;
continue;
}
@@ -415,11 +415,11 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings()
{
if (!ignoreMissing)
{
- cerr << "\"" << infile << "\" is not a valid file" << endl;
+ cerr << infile << " is not a valid file." << endl;
return false;
}
else
- cerr << "\"" << infile << "\" is not a valid file. Skipping." << endl;
+ cerr << infile << " is not a valid file. Skipping." << endl;
continue;
}
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index d0296515..7256386d 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -94,6 +94,56 @@ printTask "Testing unknown options..."
fi
)
+# General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr.
+# An failure is expected.
+test_solc_file_input_failures() {
+ local filename="${1}"
+ local solc_args="${2}"
+ local stdout_expected="${3}"
+ local stderr_expected="${4}"
+ local stdout_path=`mktemp`
+ local stderr_path=`mktemp`
+
+ set +e
+ "$SOLC" "${filename}" ${solc_args} 1>$stdout_path 2>$stderr_path
+ exitCode=$?
+ set -e
+
+ if [[ $exitCode -eq 0 ]]; then
+ printError "Incorrect exit code. Expected failure (non-zero) but got success (0)."
+ rm -f $stdout_path $stderr_path
+ exit 1
+ fi
+
+ if [[ "$(cat $stdout_path)" != "${stdout_expected}" ]]; then
+ printError "Incorrect output on stderr received. Expected:"
+ echo -e "${stdout_expected}"
+
+ printError "But got:"
+ cat $stdout_path
+ rm -f $stdout_path $stderr_path
+ exit 1
+ fi
+
+ if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]]; then
+ printError "Incorrect output on stderr received. Expected:"
+ echo -e "${stderr_expected}"
+
+ printError "But got:"
+ cat $stderr_path
+ rm -f $stdout_path $stderr_path
+ exit 1
+ fi
+
+ rm -f $stdout_path $stderr_path
+}
+
+printTask "Testing passing files that are not found..."
+test_solc_file_input_failures "file_not_found.sol" "" "" "\"file_not_found.sol\" is not found."
+
+printTask "Testing passing files that are not files..."
+test_solc_file_input_failures "." "" "" "\".\" is not a valid file."
+
printTask "Compiling various other contracts and libraries..."
(
cd "$REPO_ROOT"/test/compilationTests/
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 9c287e5e..a6c1372b 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -4577,6 +4577,50 @@ BOOST_AUTO_TEST_CASE(constructing_enums_from_ints)
ABI_CHECK(callContractFunction("test()"), encodeArgs(1));
}
+BOOST_AUTO_TEST_CASE(enum_referencing)
+{
+ char const* sourceCode = R"(
+ interface I {
+ enum Direction { A, B, Left, Right }
+ }
+ library L {
+ enum Direction { Left, Right }
+ function f() public pure returns (Direction) {
+ return Direction.Right;
+ }
+ function g() public pure returns (I.Direction) {
+ return I.Direction.Right;
+ }
+ }
+ contract C is I {
+ function f() public pure returns (Direction) {
+ return Direction.Right;
+ }
+ function g() public pure returns (I.Direction) {
+ return I.Direction.Right;
+ }
+ function h() public pure returns (L.Direction) {
+ return L.Direction.Right;
+ }
+ function x() public pure returns (L.Direction) {
+ return L.f();
+ }
+ function y() public pure returns (I.Direction) {
+ return L.g();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "L");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(1));
+ ABI_CHECK(callContractFunction("g()"), encodeArgs(3));
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}});
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(3));
+ ABI_CHECK(callContractFunction("g()"), encodeArgs(3));
+ ABI_CHECK(callContractFunction("h()"), encodeArgs(1));
+ ABI_CHECK(callContractFunction("x()"), encodeArgs(1));
+ ABI_CHECK(callContractFunction("y()"), encodeArgs(3));
+}
+
BOOST_AUTO_TEST_CASE(inline_member_init)
{
char const* sourceCode = R"(
@@ -12527,6 +12571,42 @@ BOOST_AUTO_TEST_CASE(abi_encode_empty_string_v2)
0x00
));
}
+
+BOOST_AUTO_TEST_CASE(abi_encode_rational)
+{
+ char const* sourceCode = R"(
+ // Tests that rational numbers (even negative ones) are encoded properly.
+ contract C {
+ function f() public pure returns (bytes memory) {
+ return abi.encode(1, -2);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(
+ 0x20,
+ 0x40, u256(1), u256(-2)
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_rational_v2)
+{
+ char const* sourceCode = R"(
+ // 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);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(
+ 0x20,
+ 0x40, u256(1), u256(-2)
+ ));
+}
+
BOOST_AUTO_TEST_CASE(abi_encode_call)
{
char const* sourceCode = R"T(
diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp
index b8b537e5..db0e3b66 100644
--- a/test/libsolidity/SolidityTypes.cpp
+++ b/test/libsolidity/SolidityTypes.cpp
@@ -157,6 +157,9 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
BOOST_CHECK_EQUAL(RationalNumberType(rational(7, 1)).identifier(), "t_rational_7_by_1");
BOOST_CHECK_EQUAL(RationalNumberType(rational(200, 77)).identifier(), "t_rational_200_by_77");
BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77");
+ BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, -2 * 77)).identifier(), "t_rational_200_by_77");
+ BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, 2 * 77)).identifier(), "t_rational_minus_200_by_77");
+ BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, -2 * 77)).identifier(), "t_rational_minus_200_by_77");
BOOST_CHECK_EQUAL(
StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared<string>("abc - def"))).identifier(),
"t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4"
diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol
new file mode 100644
index 00000000..9165654f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol
@@ -0,0 +1,9 @@
+contract C {
+ function f() pure public {
+ assembly {
+ let x := f_slot
+ }
+ }
+}
+// ----
+// TypeError: (84-90): The suffixes _offset and _slot can only be used on storage variables.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol
index 5513817d..1533e7ff 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol
@@ -2,4 +2,3 @@ interface I {
enum A { B, C }
}
// ----
-// TypeError: (18-33): Enumerable cannot be declared in interfaces.
diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol b/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol
new file mode 100644
index 00000000..0fe0fbae
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol
@@ -0,0 +1,9 @@
+interface I {
+ enum Direction { Left, Right }
+}
+
+contract D {
+ function f() public pure returns (I.Direction) {
+ return I.Direction.Left;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol b/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol
new file mode 100644
index 00000000..8d9003eb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol
@@ -0,0 +1,12 @@
+interface I {
+ enum Direction { Left, Right }
+}
+
+library L {
+ function f() public pure returns (I.Direction) {
+ return I.Direction.Left;
+ }
+ function g() internal pure returns (I.Direction) {
+ return I.Direction.Left;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_library.sol b/test/libsolidity/syntaxTests/parsing/enum_from_library.sol
new file mode 100644
index 00000000..ab762a82
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/enum_from_library.sol
@@ -0,0 +1,9 @@
+library L {
+ enum Direction { Left, Right }
+}
+
+contract D {
+ function f() public pure returns (L.Direction) {
+ return L.Direction.Left;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol b/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol
new file mode 100644
index 00000000..e5b98352
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol
@@ -0,0 +1,9 @@
+contract C {
+ enum Direction { Left, Right }
+}
+
+contract D is C {
+ function f() public pure returns (Direction) {
+ return Direction.Left;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol b/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol
new file mode 100644
index 00000000..75858744
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol
@@ -0,0 +1,9 @@
+interface I {
+ enum Direction { Left, Right }
+}
+
+contract D is I {
+ function f() public pure returns (Direction) {
+ return Direction.Left;
+ }
+}