What it infers depends on whether or not the signal is an external signal (one which goes to/from an I/O port), or an internal one.
Take the following construct:
module test (
assign bidirPin = direction ? 1'bz : toPin;
assign fromPin = bidirPin;
This says, when the
direction signal is low, then
bidirPin is an output and the signal
toPin is connected to the pin. When
direction in high, then
bidirPin becomes an input. In all cases the signal
fromPin represents the current data of the pin.
This construct requires the FPGA to have bidirectional (tristate) I/O buffers. Many (most?) do, but not all pins on all devices support it.
If the same construct were to instead be used internally (e.g. you have several instances of the above module and connect all of the inout pins to a single wire) then clearly what is inferred must be different to if it were an I/O pin. Most if not all FPGAs do not have internal tristate routing networks. In other words, High-Z is not possible.
The logic is however synthesisable. What happens is the synthesizer looks at all the
inout signals connecting to the same net, and simply infers multiplexer logic. This represents effectively the same thing, but requires additional arbitration logic. Generally any possible bus contention is solved using priority encoders on the multiplexer so that only one of the ports can deliver its signal at a time.
This can be quite a useful way of connecting many peripherals (e.g. SPI, UART, RAM, etc.) to a shared data bus if you are designing something with a similar architecture as a microcontroller. Using the high-Z lets the synthesiser take care of any arbitration logic. This can make debugging more difficult as you have to follow the additional logic added in any RTL netlist viewers, but it is a perfectly valid way of doing things.
In simulation, High-Z can be either an acceptable state (e.g. if you are observing a tristate I/O pin), or an indicator of a major problem (e.g. if you use a generate loop and miss a bit in a data bus it will go High-Z).
High-Z can help you identify problems causes by non-connected signals from things like generate loops, or if you had say, misspelt a variable name resulting in two things not being properly connected. If you are expecting a signal in the simulation to be able to go High-Z, then there is no issue. If you are not expecting it, then further investigation of the cause is warranted.
Internal High-Z buses (ones where arbitration is inferred) may present a further simulation headache. If you simulate a post-synthesis netlist for example you may never see High-Z on the bus as arbitration logic has already been inferred. If you simulate from a source level, then you will likely see the High-Z appearing on the bus as the simulator is quite happy to allow High-Z as it doesn't know what the synthesis tools are going to infer so the simulator sticks to exactly what you have written in the code.