diff options
author | Kostis Sagonas <[email protected]> | 2015-05-07 14:43:02 +0200 |
---|---|---|
committer | Hans Bolinder <[email protected]> | 2015-10-08 15:09:28 +0200 |
commit | 6e93fb788aebb9050da2166749b41ff54197e049 (patch) | |
tree | 0fff9e486f879ffd67b35c8b708e0a0b425275f2 /lib/dialyzer/test/opaque_SUITE_data/results | |
parent | 2e3490364359254d68c45bae7d920e70c895c9de (diff) | |
download | otp-6e93fb788aebb9050da2166749b41ff54197e049.tar.gz otp-6e93fb788aebb9050da2166749b41ff54197e049.tar.bz2 otp-6e93fb788aebb9050da2166749b41ff54197e049.zip |
Take out automatic insertion of 'undefined' from typed record fields
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.
Diffstat (limited to 'lib/dialyzer/test/opaque_SUITE_data/results')
-rw-r--r-- | lib/dialyzer/test/opaque_SUITE_data/results/crash | 10 | ||||
-rw-r--r-- | lib/dialyzer/test/opaque_SUITE_data/results/simple | 2 |
2 files changed, 6 insertions, 6 deletions
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/crash b/lib/dialyzer/test/opaque_SUITE_data/results/crash index 69bdc00257..d63389f79c 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/crash +++ b/lib/dialyzer/test/opaque_SUITE_data/results/crash @@ -1,6 +1,6 @@ -crash_1.erl:45: Record construction #targetlist{list::[]} violates the declared type of field list::'undefined' | crash_1:target() -crash_1.erl:48: The call crash_1:get_using_branch2(Branch::maybe_improper_list(),L::'undefined' | crash_1:target()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),maybe_improper_list()) -crash_1.erl:50: The pattern <_Branch, []> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()> -crash_1.erl:52: The pattern <Branch, [H = {'target', _, _} | _T]> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()> -crash_1.erl:54: The pattern <Branch, [{'target', _, _} | T]> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()> +crash_1.erl:45: Record construction #targetlist{list::[]} violates the declared type of field list::crash_1:target() +crash_1.erl:48: The call crash_1:get_using_branch2(Branch::maybe_improper_list(),L::crash_1:target()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),maybe_improper_list()) +crash_1.erl:50: The pattern <_Branch, []> can never match the type <maybe_improper_list(),crash_1:target()> +crash_1.erl:52: The pattern <Branch, [H = {'target', _, _} | _T]> can never match the type <maybe_improper_list(),crash_1:target()> +crash_1.erl:54: The pattern <Branch, [{'target', _, _} | T]> can never match the type <maybe_improper_list(),crash_1:target()> diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/simple b/lib/dialyzer/test/opaque_SUITE_data/results/simple index 1a7a139d6e..5f58f69730 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/simple +++ b/lib/dialyzer/test/opaque_SUITE_data/results/simple @@ -18,7 +18,7 @@ rec_api.erl:104: Matching of pattern {'r2', 10} tagged with a record name violat rec_api.erl:113: The attempt to match a term of type #r3{f1::queue:queue(_)} against the pattern {'r3', 'a'} breaks the opaqueness of queue:queue(_) rec_api.erl:118: Record construction #r3{f1::10} violates the declared type of field f1::queue:queue(_) rec_api.erl:123: The attempt to match a term of type #r3{f1::10} against the pattern {'r3', 10} breaks the opaqueness of queue:queue(_) -rec_api.erl:24: Record construction #r1{f1::10} violates the declared type of field f1::'undefined' | rec_api:a() +rec_api.erl:24: Record construction #r1{f1::10} violates the declared type of field f1::rec_api:a() rec_api.erl:29: Matching of pattern {'r1', 10} tagged with a record name violates the declared type of #r1{f1::10} rec_api.erl:33: The attempt to match a term of type rec_adt:r1() against the pattern {'r1', 'a'} breaks the opaqueness of the term rec_api.erl:35: Invalid type specification for function rec_api:adt_t1/1. The success typing is (#r1{f1::'a'}) -> #r1{f1::'a'} |