Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions src/solidity_parser/ast/ast2builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, create_state, quiet_errors=True):

# whether to throw a caught error immediately or store it in the caught_errors list for a client to consume
# later, this is used for testing where fast errors can be caught and fail the test quickly
self.quiet_errors = quiet_errors
self.quiet_errors = False
self.caught_errors = []
# current state of the builder
self.state = None
Expand Down Expand Up @@ -201,6 +201,7 @@ def get_expr_type(self, expr: solnodes1.Expr | soltypes.Type, allow_multiple=Fal
:return: The AST2 type of the expression or a list of types if allow_multiple is True
"""


if isinstance(expr, soltypes.Type):
# already have a type, either return it or resolve it to make sure it's AST2 and not AST1 only
return self.map_type(expr)
Expand Down Expand Up @@ -893,17 +894,29 @@ def map_type(self, ttype: soltypes.Type) -> solnodes2.Types:
return soltypes.StringType()
elif isinstance(ttype, soltypes.VariableLengthArrayType):
base_type = self.map_type(ttype.base_type)
size_type = self.get_expr_type(ttype.size)
if isinstance(ttype.size, solnodes2.Expr):
size_type = ttype.size.type_of()
elif isinstance(ttype.size, solnodes2.Types):
size_type = ttype.size
else:
size_type = self.get_expr_type(ttype.size)

# Fix for some weird grammar parsing issues where a fixed length array type is parsed as a variable length
# array type with a literal passed as the size expr, so here we change it to a fixed one
# Check for isinstance(ttype.size, solnodes1.Literal) as a compile time constant expr, e.g. Record[2**253]
# is a fixed sized but FixedLengthArrayType needs an int not an expr that computes an int.
if size_type.is_int() and size_type.is_literal_type() and isinstance(ttype.size, solnodes1.Literal):
is_const_int = size_type.is_int() and size_type.is_literal_type() and isinstance(ttype.size, solnodes1.Literal)

if is_const_int:
size = ttype.size.value
assert isinstance(size, int)
return soltypes.FixedLengthArrayType(base_type, size)
else:
return soltypes.VariableLengthArrayType(base_type, self.builder.refine_expr(ttype.size))
if isinstance(ttype.size, solnodes2.Expr):
size = deepcopy(ttype.size)
else:
size = self.builder.refine_expr(ttype.size)
return soltypes.VariableLengthArrayType(base_type, size)
elif isinstance(ttype, soltypes.FixedLengthArrayType):
return soltypes.FixedLengthArrayType(self.map_type(ttype.base_type), ttype.size)
elif isinstance(ttype, soltypes.ArrayType):
Expand Down Expand Up @@ -2002,6 +2015,9 @@ def z():
if isinstance(sym, symtab.BuiltinValue):
if isinstance(base_type, soltypes.BuiltinType):
# e.g. msg.gas, where the base is a builtin object
# TODO: should we use the given mname here or should we use the canonical name tagged to the
# BuiltinValue? In most cases it's the same but not all, e.g. aliased symbols: block.prevrandao and
# block.difficulty
return solnodes2.GlobalValue(f'{base_type.name}.{mname}', self.type_helper.map_type(sym.ttype))
else:
# e.g. myarray.length, 'length' is builtin to the array type(i.e. not a concrete field)
Expand Down Expand Up @@ -2399,7 +2415,10 @@ def _make_new_node(n):
ast2_node.values = [solnodes2.EnumMember(solnodes2.Ident(n.text)) for n in ast1_node.values]

if isinstance(ast1_node, (solnodes1.StateVariableDeclaration, solnodes1.ConstantVariableDeclaration)):
ast2_node.ttype = self.type_helper.map_type(ast1_node.var_type)
ast2_node.ttype = (
self.type_helper.get_expr_type(ast1_node.initial_value) if ast1_node.initial_value
else self.type_helper.map_type(ast1_node.var_type)
)

if isinstance(ast1_node, solnodes1.EventDefinition):
ast2_node.inputs = [solnodes2.EventParameter(solnodes2.Ident(p.var_name.text if p.var_name else f'<unnamed:{i}'), self.type_helper.map_type(p.var_type), p.is_indexed)
Expand Down Expand Up @@ -2493,7 +2512,8 @@ def refine_node(n):
if hasattr(ast1_node, 'modifiers'):
ast2_node.modifiers = [self.modifier(x) for x in ast1_node.modifiers]
if ast1_node.initial_value:
ast2_node.initial_value = self.refine_expr(ast1_node.initial_value, is_assign_rhs=True)
self.error_handler.assert_error("expected type with 'value' field", isinstance(ast2_node, (solnodes2.ConstantVariableDeclaration, solnodes2.StateVariableDeclaration)))
ast2_node.value = self.refine_expr(ast1_node.initial_value, is_assign_rhs=True)
return refine_node(ast2_node)

if isinstance(ast1_node, solnodes1.EventDefinition):
Expand Down
4 changes: 2 additions & 2 deletions src/solidity_parser/ast/symtab.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ def __init__(self, name: str, input_types: list[soltypes.Type] | None, output_ty


class BuiltinValue(Symbol):
def __init__(self, name: str, ttype: soltypes.Type):
def __init__(self, name: Aliases, ttype: soltypes.Type):
ScopeAndSymbol.__init__(self, name, None)
self.ttype = ttype

Expand Down Expand Up @@ -707,7 +707,7 @@ def __init__(self, parser_version: version_util.Version):
block_object.add(BuiltinValue('basefee', uint()))
block_object.add(BuiltinValue('chainid', uint()))
block_object.add(BuiltinValue('coinbase', soltypes.AddressType(True)))
block_object.add(BuiltinValue('difficulty', uint()))
block_object.add(BuiltinValue(['difficulty', 'prevrandao'], uint()))
block_object.add(BuiltinValue('gaslimit', uint()))
block_object.add(BuiltinValue('number', uint()))
# Add blobbasefee only for Solidity 0.8.24 and above
Expand Down
8 changes: 7 additions & 1 deletion test/solidity_parser/ast/snapshots/snap_test_ast2builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,13 @@
"CallFunction(callee=New(type_name=UserType(name=Ident(text='AllTypesExample'))), special_call_options=[], args=[]) :: ResolvedUserType(AllTypesExample)",
"New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)",
"CallFunction(callee=New(type_name=UserType(name=Ident(text='AllTypesExample'))), special_call_options=[], args=[]) :: ResolvedUserType(AllTypesExample)",
"New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)"
"New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)",
"GetMember(obj_base=Ident(text='block'), name=Ident(text='prevrandao')) :: [IntType(is_signed=False, size=256)]",
"GetMember(obj_base=Ident(text='block'), name=Ident(text='difficulty')) :: [IntType(is_signed=False, size=256)]",
"BinaryOp(left=Ident(text='x'), right=Ident(text='y'), op=<BinaryOpCode.ADD: '+'>) :: uint256",
"GetMember(obj_base=Ident(text='block'), name=Ident(text='prevrandao')) :: [IntType(is_signed=False, size=256)]",
"GetMember(obj_base=Ident(text='block'), name=Ident(text='difficulty')) :: [IntType(is_signed=False, size=256)]",
"BinaryOp(left=Ident(text='x'), right=Ident(text='y'), op=<BinaryOpCode.ADD: '+'>) :: uint256"
]

snapshots['TestSolidityTypeHelper::test_get_function_expr_type 1'] = [
Expand Down
6 changes: 6 additions & 0 deletions testcases/type_tests/basic_types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,10 @@ contract AllTypesExample {
function new_deploy_contract() {
AllTypesExample e = new AllTypesExample();
}

function blockDifficultyAndPrevRandaoAlias() public returns (uint) {
uint x = block.prevrandao;
uint y = block.difficulty;
return x + y;
}
}
Loading