Consider the following code:
wire unpacked_wire[3:0];
wire [3:0] packed_wire;
assign unpacked_wire = packed_wire; // Error: Cannot assign packed type to unpacked type
assign {>>{unpacked_wire}} = packed_wire; // No errors
My first assignment statement errors out. However, the second one doesn't. However, according to the IEEE Std 1800-2017, sec 11.4.14:
When used in the left-hand side, the streaming operators perform the reverse operation, i.e., unpack a stream of bits into one or more variables.
So isn't the streaming operator on the LHS redundant since its converting an unpacked type... to an unpacked type? So shouldn't this second assignment still be illegal? To me, this code reads like the streaming operator is actually packing the data.
It is unpacking in the sense that it is taking a single integral packed value and breaking it up into the individual elements of an unpacked array. When it comes to the streaming operator, whether it is packing or unpacking is not so relevant—it is reorganizing the layout of the bits. You could also write this as
assign unpacked_wire = {>>{packed_wire}};
When there is only one object on either side of the assignment, it doesn't matter which side you put the streaming operator on.
The LRM considers unpacked types a strong type. You can only make assignments from/to equivalent types. Packed types on the other hand are weak. You can make assignments between packed types of different sizes and it silently truncates or pads the data. The streaming operator is more like an explicit cast that facilitates assignments between unpacked and other packed or unpacked types.