CodingStyle-StrongTypeViolations

At assignment sites, the strong types of left and right side should match

Required inputs: IR

The compiler usually ignores typedefs, replacing them with the underlying type. In contrast, this rule will warn on implicit conversions that would be needed when typedefs were respected as individual types. Typedefs checked in this way are called strong types.
The rule allows to select the typedefs that should be seen as strong, and it allows to limit the operations (like assignment sites, comparison sites etc.) that should be checked. Both is done with the parameter checks which is an ordered dictionary of entries that provide typedef names (as globbing patterns) and the checks to perform for these typedefs.
The checks to be done are chosen from the below list as provided in module strong_types. They can be freely combined with Python operations to create iterables (e.g. lists or sets). The default setup only configures
('*', strong_types.all_checks),
which means that all typedefs (globbing pattern *) should be seen as strong, and being checked with all checks. An example custom configuration could be
checks = collections.OrderedDict([
('MY_INT', set(strong_types.all_checks) - set(strong_types.all_extracts)),
('Other*', strong_types.all_assignments),
    ])
In this case the typedef MY_INT would be assumed to be strong, and all checks except those at extracting operations would be applied. Also, all typedefs having a name that matches Other* would be strong and checked at assignments.
The following list summarizes the available predefined elementary checks (from module strong_types) as well as predefined groups of such checks which simplify typical selections of similar operations:
  • Assignments to strong types (lint: A flag with modifiers)
    • all_assignments = [assignment_lhs, field_init_lhs, init_lhs, return_lhs, param_lhs]
      The elementary checks like assignment_lhs each cover a certain kind of assignment (assignment operator, field initialization, variable initialization, return statement, parameter passing). This includes the below special categories with non-zero/const right-hand side
    • all_nonzero_assignments = [nonzero_assignment_lhs, nonzero_field_init_lhs, nonzero_init_lhs, nonzero_return_lhs, nonzero_param_lhs]
      This checks assignments to a strong type except where the assigned value is literally given as 0
    • all_nonconst_assignments = [nonconst_assignment_lhs, nonconst_field_init_lhs, nonconst_init_lhs, nonconst_return_lhs, nonconst_param_lhs]
      This checks assignments to a strong type except where the assigned value is an integer constant expression, string literal, or address of a stack or global object
  • Assignments from strong types (extracts, lint: X flag with modifiers)
    all_extracts = [assignment_rhs, field_init_rhs, init_rhs, return_rhs, param_rhs]
  • Use of strong types as operand in binary/ternary operators (joins, lint: J flag with modifiers)
    • all_joins = [conditional, equality, relational, multiplicative, additive, bitwise] The elementary checks cover the conditional operator (?:), (in)equality comparisons, relational comparisons, multiplicative operators, additive operators, and bitwise operators. This includes the below special categories with non-zero/const right-hand side
    • all_nonzero_joins = [nonzero_conditional, nonzero_equality, nonzero_relational, nonzero_multiplicative, nonzero_additive, nonzero_bitwise] This checks uses of strong types as operand in binary/ternary operators except where the assigned value is literally given as 0
    • all_nonconst_joins = [nonconst_conditional, nonconst_equality, nonconst_relational, nonconst_multiplicative, nonconst_additive, nonconst_bitwise] This checks uses of strong types as operand in binary/ternary operators except where the assigned value is an integer constant expression, string literal, or address of a stack or global object
  • Operands that should be boolean (similar to lint: B flag)
    all_bools = [binary_logical, unary_logical] The elementary checks inspect logical operators and check that the operands are of boolean type (native bool, if present in the language, and those configured via /Analysis/types.user_bool_types)
    Notice that Misra checks exist as well to check for proper use of bool.
  • Combination of all checks
    all_checks = all_assignments + all_extracts + all_joins + all_bools
An even more symbolical checking can be achieved with dimensional analysis. Strong typedefs being marked as dimensions behave differently at multiplication sites; for example, a multiplication where both operands are of type Meter will have result type Meter with strong types only, but it will have result type Meter*Meter when Meter is marked as a physical dimension. Dimensions can also provide conversion formulas, e.g. you could have a typedef Area, declare it as a dimension, and provide the formula Area = Meter*Meter so that the rule allows assigning the result of the multiplication to a variable of type Area.

Possible Messages

Key

Text

Severity

Disabled

arithmetic_strong_type_violation

Mismatched operand type in arithmetic operator causes implicit strong type conversion from {} to {}

None

False

assignment_strong_type_violation

Assignment causes implicit strong type conversion from {} to {}

None

False

bitwise_strong_type_violation

Mismatched operand type in bitwise operator causes implicit strong type conversion from {} to {}

None

False

comparison_strong_type_violation

Comparison causes implicit strong type conversion from {} to {}

None

False

conditional_strong_type_violation

Mismatched operand type in conditional operator causes implicit strong type conversion from {} to {}

None

False

field_init_strong_type_violation

Field initialization causes implicit strong type conversion from {} to {}

None

False

init_strong_type_violation

Initialization of {} causes implicit strong type conversion from {} to {}

None

False

logical_strong_type_violation

Mismatched operand type in logical operator causes implicit strong type conversion from {} to {}

None

False

parameter_strong_type_violation

Passing {} as argument for parameter {} causes implicit strong type conversion from {} to {}

None

False

return_strong_type_violation

Return statement causes implicit strong type conversion from {} to {}

None

False

switch_strong_type_violation

Switch case causes implicit strong type conversion from {} to {}

None

False

Options

checks

checks

Type: dict[str, list[typing.Callable[[typing.Union[bauhaus.ir.common.expressions.assignments.AssignmentSite, Expression], bool, typing.Union[Physical_IR_Root, bauhaus.ir.NoNode[Physical_IR_Root]], bauhaus.ir.common.types.strong_types.StrongType], bool]]]

Default:

{
   '*': [<function bauhaus.ir.common.types.strong_types.assignment_lhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.field_init_lhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.init_lhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.return_lhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.param_lhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.assignment_rhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.field_init_rhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.init_rhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.return_rhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.param_rhs(site: 'Check_Site', is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.conditional(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.equality(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.relational(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.multiplicative(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.additive(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.bitwise(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.binary_logical(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>, <function bauhaus.ir.common.types.strong_types.unary_logical(site: 'Check_Site', _is_lhs: 'bool', _other: 'Check_RHS', _other_type: 'StrongType') -> 'bool'>]
}
Configuration of checks to apply. This maps globbing patterns for the (qualified) typedef names to a list of checks, using an ordered dict to retain the order (last matching entry for a typedef wins). The checks are functions from module strong_types, or your own predicates that should take (site, is_lhs, rhs) as arguments.
 

dimensions

dimensions : dict[str, str] = {}

Dictionary using names of strong types that should be treated as physical dimensions as keys, mapping them to a string for a formula how this type depends on other dimensions. For example, the entries
'Meter' : '',
'Area' : 'Meter*Meter'
would declare the strong types Meter and Area to be dimensions, and the formula allows assigning the product of two variables of type Meter to a variable of type Area.