This is a similar problem to 0006812, the compiler reads cmis it shouldn't, resulting in inconsistent assumptions. But if the cmi doesn't exist, the compiler doesn't read it and so succeeds (at giving a type error).

I'm not sure what to do here.
This is not a bug of the compiler: as always, it returns the first error it found, and having a wrong cmi in your path is indeed an error.
Your point seems to be that since not having a cmi is actually legal, we should not fail in that case, just ignore the broken file. But wouldn't it be even more confusing, if you were actually expecting a cmi for that file?
To me this looks more like a hygiene problem than a compiler feature.

Oh, wait a minute. Does the problem only occur with -short-paths?
In that case it might be related to the way -short-paths explores its environment before reporting errors. The code was written before the introduction of module aliases, and there may be bad interactions.

This bites people occasionally. Instead of finding every place in the typer that might be forcing aliases to compilation units that the input program doesn't use, how about:
- adding 'val Env.can_load_cmis : bool ref = ref false'
- using it in Env.find_pers_struct to refuse to load cmis
- setting it to true around the part of the typer where it does error reporting

This assumes the typer doesn't have legitimate reasons to load compilation units that weren't loaded at the time the type error was found. Not sure if this is true.

Alternatively, I think (haven't tested) that something like https://github.com/ocaml/ocaml/pull/682 [^] would allow to change the way we build to not produce these forward references anymore.
And without forward references, this problem (and at least one other I have reported and was fixed) go away.

The idea would be that instead of creating, for each library, a module aliasing all the modules of the library, and make every module of the library open the aliases module, I would have a preprocessor that would create just the aliases a given module needs.
In the example above, b.ml would contain (after preprocessing):
open struct module A = Lib__A end (* from preprocessor *)
module C = struct
type t = { a : A.t }
let f t = t.a
end
and so the compiler would have no reason to look for lib__C.cmi.

(and this would also require a similar feature in signatures: open sig .. end)

http://caml.inria.fr/mantis/view.php?id=7134#c16151 [^] makes sense to me.
The compiler indeed has no good reason to load cmis when reporting error: if it found an error, it has already loaded all the relevant cmis that are involved in the error.
I'll see if it can be done cleanly.

Your workaround might work, but we don't want to force all users to use such an approach.

The tip of the feature is broken because the second commit sets the boolean to true instead of false, effectively doing nothing.
But other than that:
- the test in the description passes
- the problem that made me ping this PR in July gives a type error rather than inconsistent assumptions (when I backport the patch, I can't use a new compiler version)

I haven't tested the problem that made me create this PR, I'm going to try now if it's not too hard.