Age | Commit message (Collapse) | Author |
|
|
|
Optimization: A few dictionaries are maps instead of dicts.
|
|
A bug is fixed, but there are more problems.
Modify erl_types.erl like this:
-define(EXPAND_LIMIT, 500).
and bogus warnings are output (again).
Callbacks and specs are compared (subtype) in dialyzer_behaviour. If
they are expanded to different depths, then invalid warnings can be
generated.
|
|
Opaque singleton keys have the unfortunate property, unlike any other
singleton type, to overlap with other singleton types that do not have
the same internal representation. Therefore, we must not keep opaque
singletons in the Pairs list in a map type.
|
|
t_subtract/2 would break its postcondition by always returning the
underapproximation none() when given a variable on the right hand side.
This broke map type parsing, since it relied on t_subtract/2 to tell it
when map keys would shadow each other.
|
|
Opaque keys in maps broke an assumption in
erl_types:mapmerge_otherv/3 (that the infinimum of a singleton type and
some other type would either be none() or that same singleton type),
causing a case_clause crash.
|
|
* maps:from_list/1
* maps:get/2
* maps:is_key/2
* maps:merge/2
* maps:put/3
* maps:size/1
* maps:to_list/1
* maps:update/3
|
|
The type of a map is represented as a three-tuple {Pairs, DefaultKey,
DefaultValue}. DefaultKey and DefaultValue are types. Pairs is a list of
three-tuples {Key, mandatory | optional, Value}, where Key and Value are
types. All types Key must be singleton, or "known at compile time," as
the EEP put it. Examples:
#{integer()=>list()} {[], integer(), list()}
#{a=>char(), b=>atom()} {[{a, optional, char()},
{b, optional, atom()}],
none(), none()}
map() {[], any(), any()}
A more formal description of the representation and its invariants can
be found in erl_types.erl
Special thanks to Daniel S. McCain (@dsmccain) that co-authored a very
early version of this with me back in April 2014, although only the
singleton type logic remains from that version.
|
|
Parameterized modules are no longer supported, so module() can only be
an atom().
|
|
|
|
|
|
* maint:
dialyzer: Correct handling of parameters of opaque types
|
|
Correction of commit d57f5e.
|
|
* lucafavatella/dialyzer-remote-type:
Delete remote types-related dead code in erl_types
OTP-13104
|
|
Code became dead in 854ee8b.
|
|
Background
-----------
In record fields with a type declaration but without an initializer, the
Erlang parser inserted automatically the singleton type 'undefined' to
the list of declared types, if that value was not present there.
I.e. the record declaration:
-record(rec, {f1 :: float(),
f2 = 42 :: integer(),
f3 :: some_mod:some_typ()}).
was translated by the parser to:
-record(rec, {f1 :: float() | 'undefined',
f2 = 42 :: integer(),
f3 :: some_mod:some_typ() | 'undefined'}).
The rationale for this was that creation of a "dummy" #rec{} record
should not result in a warning from dialyzer that e.g. the implicit
initialization of the #rec.f1 field violates its type declaration.
Problems
---------
This seemingly innocent action has some unforeseen consequences.
For starters, there is no way for programmers to declare that e.g. only
floats make sense for the f1 field of #rec{} records when there is no
`obvious' default initializer for this field. (This also affects tools
like PropEr that use these declarations produced by the Erlang parser to
generate random instances of records for testing purposes.)
It also means that dialyzer does not warn if e.g. an is_atom/1 test or
something more exotic like an atom_to_list/1 call is performed on the
value of the f1 field.
Similarly, there is no way to extend dialyzer to warn if it finds record
constructions where f1 is not initialized to some float.
Last but not least, it is semantically problematic when the type of the
field is an opaque type: creating a union of an opaque and a structured
type is very problematic for analysis because it fundamentally breaks
the opacity of the term at that point.
Change
-------
To solve these problems the parser will not automatically insert the
'undefined' value anymore; instead the user has the option to choose the
places where this value makes sense (for the field) and where it does
not and insert the | 'undefined' there manually.
Consequences of this change
----------------------------
This change means that dialyzer will issue a warning for all places
where records with uninitialized fields are created and those fields have
a declared type that is incompatible with 'undefined' (e.g. float()).
This warning can be suppressed easily by adding | 'undefined' to the
type of this field. This also adds documentation that the user really
intends to create records where this field is uninitialized.
|
|
Prior to this commit, the fact that parameters of opaque types are
expanded differently depending on the current values of limits used
during expansion, caused problems later when the types of parameters
are used for determining if opaque types are comparable.
|
|
Opaque recursive parameters are expanded faster.
|
|
Expand parameters when needed only.
The opaqueness is removed from types expanded to any().
|
|
|
|
Add more information about the caller of t_from_form(). Instead of
just the module, also provide name of the type, spec, or record where
the type form resides.
|
|
Thanks to ILYA Khlopotov for pointing the bug out.
|
|
The example is provided by James Fish in
http://erlang.org/pipermail/erlang-questions/2014-December/082204.html.
Note that warnings with text such as "the _ variable breaks
opaqueness" are still possible.
|
|
|
|
The test t_is_none() does not check for opaque types (and this is most
likely how it should be), why t_opaque() should never be called with
none().
|
|
Comparing two operands for (in)equality is allowed if both operands
are of the same unknown opaque type. Since OTP 17, there is a warning
if the types of the operands have nothing in common (this cannot
happen before OTP 17). However, the warning says there is a test
between opaque types, which is wrong. The warning now states that the
comparison cannot evaluate to 'true', which is more consistent.
|
|
In OTP 17 it is possible to mix types such as dict:dict() and
dict:dict(_, _) outside of the dict module (and similarly for some
other opaque types in STDLIB), but the results are unfortunately
possibly invalid warnings in users' code. In OTP 18 parameterized
opaque types with the same name but with different number of
parameters are no longer compatible when seen from outside of the
module where the types are declared.
The types in STDLIB have been updated accordingly; for instance
-opaque dict() :: dict(_, _).
has been replaced by
-type dict() :: dict(_, _).
|
|
The check that a modified type of a field is a subtype of the declared
type has been moved outside of the expansion of forms to avoid loops.
|
|
|
|
An opaque type ?opaque(_) was put in a list where #opaque{} was
expected.
|
|
|
|
|
|
In particular fix handling of records.
|
|
* maint:
dialyzer, hipe: Fix a bug concerning is_record/2,3
|
|
Also fixed some cases where Dialyzer could crash due to reaching
system limits.
|
|
* maint:
[dialyzer] Correct a doc bug introduced in 0b041238
[dialyzer] Use the option 'dialyzer' to control the compiler
[dialyzer] Fix handling of literal records
|
|
This ticket is about records in Erlang code, and when to check the
fields against the (optional) types given when defining records.
Dialyzer operates on the Erlang Core format, where there are no trace
of records. The fix implemented is a Real Hack:
Given the new option 'dialyzer' erl_expand_records marks the line
number of records in a way that is undone by v3_core, which in turn
inserts annotations that can be recognized by Dialyzer.
|
|
* maint:
hipe: fix a bug concerning typed record fields
|
|
|
|
* maint:
hipe: fix a bug concerning typed record fields
|
|
When checking typed record fields Dialyzer failed to handle
types containing remote types.
Thanks to Erik Søe Sørensen for reporting this bug.
|
|
The pre-defined types array(), dict(), digraph(), gb_set(), gb_tree(),
queue(), set(), and tid() have been removed.
|
|
product/_, union/_, range/2 as well as tuple/N (N > 0), map/N (N > 0),
atom/1, integer/1, binary/2, record/_, and 'fun'/_ can now be used as
type names.
|
|
Dialyzer sometimes output warnings like
Attempt to test for inequality between a term of type 'false' and a
term of opaque type 'false' | gb_set()
The reason was that erl_types:t_inf/3 when called from
erl_types:t_find_unknown_opaque() did not return 'false' but found the
mismatch. It should not return the mismatch unless the intersection is
empty ('none').
Thanks to Shayan Pooya [[email protected]] for pointing out the bug.
|
|
Namely, extend the HiPE tagging scheme info, properly handle the translation
of the (is_)map type test to Icode and RTL and support handling of the map()
type in the type system.
While at it, also performed some clean up of things that needed small fixes.
|
|
* aronisstav/hipe/opaque_fix:
Don't 'opaque-decorate' a success typing using an incompatible spec
|
|
Without this patch Dialyzer crashes when analyzing the supplemented test case.
|
|
Remove debug printouts.
|
|
|
|
|