Skip to content

Commit

Permalink
Make parser accept transient as location (but not a keyword)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusaaguiar committed May 7, 2024
1 parent 38ea398 commit aff3909
Show file tree
Hide file tree
Showing 59 changed files with 471 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/grammar/SolidityLexer.g4
Expand Up @@ -85,6 +85,7 @@ SignedIntegerType:
Storage: 'storage';
String: 'string';
Struct: 'struct';
Transient: 'transient'; // not a real keyword
True: 'true';
Try: 'try';
Type: 'type';
Expand Down
16 changes: 12 additions & 4 deletions docs/grammar/SolidityParser.g4
Expand Up @@ -262,7 +262,7 @@ userDefinedValueTypeDefinition:
* The declaration of a state variable.
*/
stateVariableDeclaration
locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false]
locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false, boolean transientLocationSet = false]
:
type=typeName
(
Expand All @@ -272,8 +272,9 @@ locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean
| {!$constantnessSet}? Constant {$constantnessSet = true;}
| {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;}
| {!$constantnessSet}? Immutable {$constantnessSet = true;}
| {!$transientLocationSet}? Transient {$transientLocationSet = true;}
)*
name=identifier
(transientName=Transient | name=identifier)
(Assign initialValue=expression)?
Semicolon;

Expand Down Expand Up @@ -364,7 +365,14 @@ locals [boolean visibilitySet = false, boolean mutabilitySet = false]
/**
* The declaration of a single variable.
*/
variableDeclaration: type=typeName location=dataLocation? name=identifier;
variableDeclaration
:
type=typeName
(
transientLocation=Transient name=identifier?
| location=dataLocation? name=identifier
);

dataLocation: Memory | Storage | Calldata;

/**
Expand Down Expand Up @@ -419,7 +427,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
/**
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
*/
identifier: Identifier | From | Error | Revert | Global;
identifier: Identifier | From | Error | Revert | Global | Transient;

literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;

Expand Down
27 changes: 25 additions & 2 deletions libsolidity/analysis/DeclarationTypeChecker.cpp
Expand Up @@ -407,6 +407,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
{
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::Transient: return "\"transient\"";
case Location::CallData: return "\"calldata\"";
case Location::Unspecified: return "none";
}
Expand Down Expand Up @@ -456,8 +457,24 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
switch (varLoc)
{
case Location::Unspecified:
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
break;
case Location::Transient:
if (_variable.isConstant() || _variable.immutable())
m_errorReporter.declarationError(
2197_error,
_variable.location(),
"Transient cannot be used as data location for constant or immutable variables."
);
typeLoc = DataLocation::Transient;
break;
default:
solAssert(false, "");
break;
}
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
Expand All @@ -477,6 +494,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Transient:
solUnimplementedAssert(false, "Transient data location cannot be used in this kind of variable or parameter declaration.");
break;
case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
Expand All @@ -497,6 +517,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
}

if (!type->isValueType())
solUnimplementedAssert(typeLoc != DataLocation::Transient, "Transient data location is only supported for value types.");

_variable.annotation().type = type;
}

Expand Down
4 changes: 3 additions & 1 deletion libsolidity/ast/AST.cpp
Expand Up @@ -816,7 +816,9 @@ std::set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocation
{
using Location = VariableDeclaration::Location;

if (!hasReferenceOrMappingType() || isStateVariable() || isEventOrErrorParameter())
if (isStateVariable())
return std::set<Location>{Location::Unspecified, Location::Transient};
else if (!hasReferenceOrMappingType() || isEventOrErrorParameter())
return std::set<Location>{ Location::Unspecified };
else if (isCallableOrCatchParameter())
{
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/AST.h
Expand Up @@ -1052,7 +1052,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen
class VariableDeclaration: public Declaration, public StructurallyDocumented
{
public:
enum Location { Unspecified, Storage, Memory, CallData };
enum Location { Unspecified, Storage, Transient, Memory, CallData };
enum class Mutability { Mutable, Immutable, Constant };
static std::string mutabilityToString(Mutability _mutability)
{
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/ast/ASTJsonExporter.cpp
Expand Up @@ -1058,6 +1058,8 @@ std::string ASTJsonExporter::location(VariableDeclaration::Location _location)
return "memory";
case VariableDeclaration::Location::CallData:
return "calldata";
case VariableDeclaration::Location::Transient:
return "transient";
}
// To make the compiler happy
return {};
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/ast/ASTJsonImporter.cpp
Expand Up @@ -1190,6 +1190,8 @@ VariableDeclaration::Location ASTJsonImporter::location(Json const& _node)
return VariableDeclaration::Location::Memory;
else if (storageLocStr == "calldata")
return VariableDeclaration::Location::CallData;
else if (storageLocStr == "transient")
return VariableDeclaration::Location::Transient;
else
astAssert(false, "Unknown location declaration");

Expand Down
17 changes: 17 additions & 0 deletions libsolidity/ast/Types.cpp
Expand Up @@ -1544,6 +1544,8 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
return TypeProvider::emptyTuple();
case DataLocation::Storage:
return isPointer() ? nullptr : TypeProvider::emptyTuple();
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
}
return nullptr;
}
Expand Down Expand Up @@ -1571,6 +1573,9 @@ std::string ReferenceType::stringForReferencePart() const
return "calldata";
case DataLocation::Memory:
return "memory";
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
return "";
Expand All @@ -1584,6 +1589,9 @@ std::string ReferenceType::identifierLocationSuffix() const
case DataLocation::Storage:
id += "_storage";
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
id += "_memory";
break;
Expand Down Expand Up @@ -1748,6 +1756,9 @@ BoolResult ArrayType::validForLocation(DataLocation _loc) const
if (storageSizeUpperBound() >= bigint(1) << 256)
return BoolResult::err("Type too large for storage.");
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
return true;
}
Expand Down Expand Up @@ -1828,6 +1839,9 @@ std::vector<std::tuple<std::string, Type const*>> ArrayType::makeStackItems() co
case DataLocation::Storage:
// byte offset inside storage value is omitted
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
}
Expand Down Expand Up @@ -2568,6 +2582,9 @@ std::vector<std::tuple<std::string, Type const*>> StructType::makeStackItems() c
return {std::make_tuple("mpos", TypeProvider::uint256())};
case DataLocation::Storage:
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
}
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/Types.h
Expand Up @@ -70,7 +70,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
return rational(_numerator, _denominator);
}

enum class DataLocation { Storage, CallData, Memory };
enum class DataLocation { Storage, Transient, CallData, Memory };


/**
Expand Down
6 changes: 6 additions & 0 deletions libsolidity/codegen/ArrayUtils.cpp
Expand Up @@ -1021,6 +1021,9 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
if (_arrayType.isByteArrayOrString())
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
}
}
Expand Down Expand Up @@ -1118,6 +1121,9 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
m_context << endTag;
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
}

Expand Down
9 changes: 9 additions & 0 deletions libsolidity/codegen/CompilerUtils.cpp
Expand Up @@ -1024,6 +1024,9 @@ void CompilerUtils::convertType(
"Invalid conversion to storage type."
);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
{
// Copy the array to a free position in memory, unless it is already in memory.
Expand Down Expand Up @@ -1168,6 +1171,9 @@ void CompilerUtils::convertType(
"Invalid conversion to storage type."
);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
// Copy the array to a free position in memory, unless it is already in memory.
switch (typeOnStack.location())
Expand Down Expand Up @@ -1207,6 +1213,9 @@ void CompilerUtils::convertType(
conversionImpl(m_context);
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::CallData:
{
if (typeOnStack.isDynamicallyEncoded())
Expand Down
6 changes: 6 additions & 0 deletions libsolidity/codegen/ExpressionCompiler.cpp
Expand Up @@ -2047,6 +2047,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
ArrayUtils(m_context).retrieveLength(type);
m_context << Instruction::SWAP1 << Instruction::POP;
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
m_context << Instruction::MLOAD;
break;
Expand Down Expand Up @@ -2193,6 +2196,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
else
setLValueToStorageItem(_indexAccess);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
ArrayUtils(m_context).accessIndex(arrayType);
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArrayOrString());
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/YulUtilFunctions.cpp
Expand Up @@ -2525,6 +2525,9 @@ std::string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
templ("advance", toCompactHexWithPrefix(size));
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::CallData:
{
u256 size = _type.calldataStride();
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Expand Up @@ -2314,6 +2314,9 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)

break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
{
std::string const indexAccessFunction = m_utils.memoryArrayIndexAccessFunction(arrayType);
Expand Down
15 changes: 15 additions & 0 deletions libsolidity/parsing/Parser.cpp
Expand Up @@ -886,6 +886,21 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
}
}
else if (
(_options.kind == VarDeclKind::State || _options.allowLocationSpecifier) &&
token == Token::Identifier &&
m_scanner->currentLiteral() == "transient" &&
m_scanner->peekNextToken() != Token::Assign &&
m_scanner->peekNextToken() != Token::Semicolon &&
m_scanner->peekNextToken() != Token::Comma &&
m_scanner->peekNextToken() != Token::RParen
)
{
if (location != VariableDeclaration::Location::Unspecified)
parserError(ErrorId{3548}, "Location already specified.");
else
location = VariableDeclaration::Location::Transient;
}
else
break;
nodeFactory.markEndPosition();
Expand Down
16 changes: 16 additions & 0 deletions test/cmdlineTests/storage_layout_transient_value_types/input.json
@@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint transient x; bytes32 transient b; address transient addr; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

0 comments on commit aff3909

Please sign in to comment.