Whether or not two namespaces (struct, enum, union or opaque) are equivalent is based on the AST nodes they capture. If you have written opaque {} on line 7, column 9, then every usage of that exact span of source text is going to reference the exact same namespace. To get a unique namespace, the namespace needs to capture a unique AST node. This is described in more detail in the 0.12.0 release notes under Namespace Type Equivalence (though it should probably be added to the langref too).
As you noted with @rpkak’s solution, it isn’t enough to just capture a loop index since that would only make each element distinct. To make each call to foo distinct you need to capture an AST node unique to the function call. There is no way to declare this “unique thing” entirely from the inside of your function; it must be passed as a parameter by the caller:
const std = @import("std");
fn foo(comptime n: usize, comptime brand: anytype) []const type {
// I like to call this parameter used solely as an AST capture a "brand",
// a term borrowed from a similar concept in TypeScript.
return comptime a: {
var a: [n]type = undefined;
for (&a, 0..) |*t, i| t.* = opaque {
comptime {
_ = brand; // unique per function call (if the caller behaves)
_ = i; // unique per iteration
}
};
break :a &a;
};
}
pub fn main() void {
const fa = comptime foo(3, opaque {}); // or '@src()' or anything unique
const fb = comptime foo(3, opaque {});
inline for (fa, fb) |i, j|
std.debug.print("fa:{} fb:{}\n", .{ i, j });
}
fa:z.foo__opaque_687 fb:z.foo__opaque_691
fa:z.foo__opaque_688 fb:z.foo__opaque_692
fa:z.foo__opaque_689 fb:z.foo__opaque_693
opaque {} or @src() are good candidates for “brands”, but anything unique works (and you might want to throw in comptime assertions to help guide your users toward passing a value likely to be unique, instead of e.g. 1 or "abc"). And yes, this means that a naughty caller could do e.g.
const x = opaque {};
const fa = comptime foo(3, x);
const fb = comptime foo(3, x);
inline for (fa, fb) |a, b| assert(a == b);
but that’s their problem to worry about, not yours.