Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
use num_bigint::BigInt;
use slang_solidity_v2_semantic::types::Number;
use slang_solidity_v2_semantic::types::{Number, literals};

use super::super::HexNumberExpressionStruct;
use super::common::{integer_value_of_node, number_value_of_node};
use super::common::integer_value_of_node;

impl HexNumberExpressionStruct {
/// Returns the integer value of this literal, or `None` if the literal
/// cannot be evaluated (e.g. a malformed hex digit sequence).
pub fn integer_value(&self) -> Option<BigInt> {
// A 40-digit literal is typed `address`, which carries no value: read it from the text.
integer_value_of_node(&self.semantic, self.ir_node.id())
.or_else(|| literals::value_of_hex_number_expression(&self.ir_node))
}

/// Returns the literal number carried by this node's type. Hex number
/// Returns the literal number this node spells. Hex number
/// expressions only ever fold to integers.
pub fn number_value(&self) -> Option<Number> {
number_value_of_node(&self.semantic, self.ir_node.id())
self.integer_value().map(Number::Integer)
}
}
1 change: 0 additions & 1 deletion crates/solidity-v2/outputs/cargo/ast/src/ast/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ impl LiteralType {
Some(Type::FixedPointNumber(FixedPointNumberType { inner }))
}
types::Type::String(inner) => Some(Type::String(StringType { inner })),
types::Type::Address(inner) => Some(Type::Address(AddressType { inner })),
_ => None,
})
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use slang_solidity_v2_common::nodes::NodeId;
use super::binder::{Binder, Definition, Typing};
use super::types::{
AddressType, ArrayType, BytesType, ContractType, DataLocation, FunctionTypeVisibility,
LiteralKind, MetaType, TupleType, Type, TypeId, TypeRegistry, UserDefinedValueType,
UserMetaType,
MetaType, TupleType, Type, TypeId, TypeRegistry, UserDefinedValueType, UserMetaType,
};

mod availability;
Expand Down Expand Up @@ -339,9 +338,6 @@ impl<'a> BuiltInsResolver<'a> {
}
}
Type::Integer(_) => None,
Type::Literal(LiteralKind::Address { .. }) => {
Self::lookup_member_of_address(symbol, false)
}
Type::Literal(_) => None,
Type::Mapping(_) => None,
// The meta-type of `bytes`/`string` exposes the `concat` built-in.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ impl Pass<'_> {
Type::Address(AddressType { is_payable }) => {
self.address_explicitly_convertible_to(*is_payable, to_type)
}
Type::Literal(LiteralKind::Address { .. }) => {
self.address_explicitly_convertible_to(false, to_type)
}

Type::Integer(IntegerType { is_signed, bits }) => {
integer_explicitly_convertible_to(*is_signed, *bits, to_type)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ruint::aliases::{U160, U256};
use ruint::aliases::U256;
use slang_solidity_v2_common::diagnostics::DiagnosticCollection;
use slang_solidity_v2_common::diagnostics::kinds::resolution::{
AmbiguousReference, MemberNotFound, NoMatchingCallableDeclaration,
Expand Down Expand Up @@ -1475,21 +1475,17 @@ impl Pass<'_> {
Type::Literal(kind)
}

pub(super) fn hex_number_literal_kind(
pub(super) fn type_of_hex_number_expression(
hex_number_expression: &ir::HexNumberExpression,
) -> Option<LiteralKind> {
) -> Option<Type> {
let mut hex_number = hex_number_expression.literal.unparse().to_owned();
hex_number.retain(|character| character != '_');
// Source-text byte width: `0x` prefix is stripped
let digits = u32::try_from(hex_number.len().saturating_sub(2)).ok()?;
if digits == 40 {
// TODO(validation) SDR[38]: verify the address is valid (ie. has a valid checksum)
// We need at least an implementation of SHA3 to compute the checksum

// Skip `0x` prefix and parse the hexadecimal number.
// `U160::from_str_radix` ignores `_` separators.
let value = U160::from_str_radix(&hex_number[2..], 16).ok()?;
return Some(LiteralKind::Address { value });
return Some(Type::Address(AddressType { is_payable: false }));
}
let value = Number::from_hex_number_expression(hex_number_expression)?
.into_integer()
Expand All @@ -1499,14 +1495,14 @@ impl Pass<'_> {
// Each pair of hex digits is one byte. An odd digit count spans no
// whole number of bytes, so the literal is a plain integer.
if digits % 2 == 1 {
return Some(LiteralKind::Integer {
return Some(Type::Literal(LiteralKind::Integer {
value: value.into(),
});
}));
}
Some(LiteralKind::HexInteger {
Some(Type::Literal(LiteralKind::HexInteger {
value,
bytes: digits / 2,
})
}))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ impl Visitor for Pass<'_> {
}

fn leave_hex_number_expression(&mut self, node: &ir::HexNumberExpression) {
let type_id = Self::hex_number_literal_kind(node)
.map(|kind| self.types.register_type(Type::Literal(kind)));
let type_id =
Self::type_of_hex_number_expression(node).map(|type_| self.types.register_type(type_));
self.binder.set_node_type(node.id(), type_id);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ impl Pass<'_> {

// Whether the expression is a number that carries no type of its own,
// which is what arithmetic over literals evaluates to. Integers, hex
// numbers, rationals and addresses count as numbers here. String
// literals do not, and neither does a value with a concrete type.
// numbers and rationals count as numbers here. String literals do not,
// and neither does a value with a concrete type.
fn is_untyped_number_expression(&self, expression: &ir::Expression) -> bool {
let node_id = expression.node_id().expect("expressions have node ids");
let Typing::Resolved(type_id) = self.binder.node_typing(node_id) else {
Expand All @@ -307,7 +307,6 @@ impl Pass<'_> {
LiteralKind::Integer { .. }
| LiteralKind::HexInteger { .. }
| LiteralKind::Rational { .. }
| LiteralKind::Address { .. }
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,24 @@ fn test_static_library_call_is_not_partially_applied() {
let typings = statement_types(&analysis, "Test", "__test");
assert_eq!(typings, vec![Some(Type::Boolean)]);
}

#[test]
fn test_address_literal_receives_functions_attached_to_address() {
let source = r#"
pragma solidity *;
library L {
function tag(address a) internal pure returns (bool) { a; return true; }
}
contract Test {
using L for address;
function __test() internal pure {
0xb701a286753ADe3704e1DcFD93507454A2307641.tag();
}
}
"#;
let analysis = Analysis::of_source(source)
.run(Analyse::References)
.expect_no_diagnostics();
let typings = statement_types(&analysis, "Test", "__test");
assert_eq!(typings, vec![Some(Type::Boolean)]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use slang_solidity_v2_ir::ir;

use super::{Analyse, Analysis, diagnostic_kind, expression};
use crate::binder::Definition;
use crate::types::{FixedSizeArrayType, LiteralKind, Type};
use crate::types::{AddressType, FixedSizeArrayType, LiteralKind, Type};

/// Runs the full pipeline over `source` and returns contract `name`'s folded
/// storage base slot together with the diagnostic emitted, if any. A rejected base
Expand Down Expand Up @@ -139,6 +139,22 @@ fn test_value_bearing_integer_literal_types() {
);
}

#[test]
fn test_address_literal_types_as_address() {
let address = Some(Type::Address(AddressType { is_payable: false }));
let (type_, _) =
expression("0xb701a286753ADe3704e1DcFD93507454A2307641").into_type_and_diagnostic();
assert_eq!(type_, address);
let (type_, _) =
expression("(0xb701a286753ADe3704e1DcFD93507454A2307641)").into_type_and_diagnostic();
assert_eq!(type_, address);

// `address` has no arithmetic, so the literal does not fold as a number.
let (type_, _) =
expression("0xb701a286753ADe3704e1DcFD93507454A2307641 + 1").into_type_and_diagnostic();
assert_eq!(type_, None);
}

#[test]
fn test_binary_arithmetic_folds_to_narrowed_literal() {
// Addition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,60 @@ fn test_overload_resolution_rejects_byte_array_narrowing() {
);
}

#[test]
fn test_overload_resolution_selects_address_overload_for_address_literal() {
let setup = "
function pick(address a, uint256 v) internal pure returns (uint8) { a; v; return 1; }
function pick(uint256 u, uint256 v) internal pure returns (uint16) { u; v; return 2; }
";
let (type_, _) = expression("pick(0xb701a286753ADe3704e1DcFD93507454A2307641, 1)")
.with_members(setup)
.into_resolved_type();
assert_eq!(
type_,
Type::Integer(IntegerType {
is_signed: false,
bits: 8,
})
);
}

#[test]
fn test_member_overload_resolution_selects_address_overload_for_address_literal() {
let setup = "
function pick(address a) external pure returns (uint8) { a; return 1; }
function pick(uint256 v) external pure returns (uint16) { v; return 2; }
";
let (type_, _) = expression("this.pick(0xb701a286753ADe3704e1DcFD93507454A2307641)")
.with_members(setup)
.into_resolved_type();
assert_eq!(
type_,
Type::Integer(IntegerType {
is_signed: false,
bits: 8,
})
);
}

#[test]
fn test_address_literal_does_not_match_address_payable_overload() {
let setup = "
function pick(address a) internal pure returns (uint8) { a; return 1; }
function pick(address payable a) internal pure returns (uint16) { a; return 2; }
";
let (type_, _) = expression("pick(0xb701a286753ADe3704e1DcFD93507454A2307641)")
.with_members(setup)
.into_resolved_type();
assert_eq!(
type_,
Type::Integer(IntegerType {
is_signed: false,
bits: 8,
})
);
}

#[test]
fn test_overloaded_call_operand_narrows_to_selected_overload() {
// When an overloaded callee resolves to a single overload through the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) mod numbers;
pub(crate) mod strings;
pub(crate) mod yul;

pub use numbers::value_of_hex_number_expression;
pub use strings::{
value_of_hex_string_literals, value_of_string_literals, value_of_unicode_string_literals,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ pub enum Number {
Rational(BigRational),
}

/// The integer a hex number literal spells, whatever its type (a 40-digit one is an `address`).
pub fn value_of_hex_number_expression(
hex_number_expression: &ir::HexNumberExpression,
) -> Option<BigInt> {
let hex = hex_number_expression.literal.unparse();
// Skip `0x` prefix and parse the hexadecimal number.
// `BigInt::from_str_radix` can handle `_` separators.
BigInt::from_str_radix(&hex[2..], 16).ok()
}

impl Number {
pub(crate) fn from_hex_number_expression(
hex_number_expression: &ir::HexNumberExpression,
) -> Option<Self> {
let hex = hex_number_expression.literal.unparse();
// Skip `0x` prefix and parse the hexadecimal number.
// `BigInt::from_str_radix` can handle `_` separators.
let value = BigInt::from_str_radix(&hex[2..], 16).ok()?;
Some(Self::Integer(value))
value_of_hex_number_expression(hex_number_expression).map(Self::Integer)
}

pub(crate) fn from_decimal_number_expression(
Expand Down Expand Up @@ -68,7 +74,6 @@ impl Number {

pub fn from_literal_kind(kind: &LiteralKind) -> Option<Self> {
match kind {
LiteralKind::Address { value } => Some(Self::Integer(value.into())),
LiteralKind::Integer { value } => Some(Self::Integer(value.clone())),
LiteralKind::HexInteger { value, .. } => Some(Self::Integer(value.clone().into())),
LiteralKind::Rational { value } => Some(Self::Rational(value.clone())),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use literals::numbers;
use num_bigint::{BigInt, BigUint};
use num_rational::BigRational;
use ruint::aliases::{U160, U256};
use ruint::aliases::U256;
use slang_solidity_v2_common::nodes::NodeId;
use slang_solidity_v2_ir::ir;

Expand Down Expand Up @@ -199,9 +199,6 @@ pub enum LiteralKind {
String {
value: Box<[u8]>,
},
Address {
value: U160,
},
}

impl LiteralKind {
Expand All @@ -223,7 +220,6 @@ impl LiteralKind {
location: DataLocation::Memory,
}))
}
LiteralKind::Address { .. } => Some(Type::Address(AddressType { is_payable: false })),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@ fn type_display(type_: &Type) -> String {
LiteralKind::Rational { value } => format!("lit-rational({value})"),
LiteralKind::HexString { value } => format!("lit-hexstring({})", value.len()),
LiteralKind::String { value } => format!("lit-string({})", value.len()),
LiteralKind::Address { value } => format!("lit-address({value})"),
},
Type::Mapping(mapping) => {
format!(
Expand Down
Loading
Loading