dmd/compiler/test/compilable/staticforeach.d
Nicholas Wilson 57c5b071af
Rebase stable6 (#16967)
* Document template instance duplication status as part of its field documentation. (#16643)

* Fix Bugzilla 24599 - Wrongly elided TypeInfo emission (#15868)

Reverting #14844, which caused such missing TypeInfos, *and* making
sure the special TypeInfo members are fully analyzed and ready for
codegen (otherwise hitting an assertion for the real-world project).

* Reorganize backend build files to match target and make more similar per line (#16672)

* Remove redundant suggestions on linker errors (#16711)

* Fix bugzilla 24337 - Segfault when printing an int[] cast from a string (#16729)

* Add BitFieldStyle.Gcc_Clang_ARM

Required for 32-bit ARM, and non-Apple 64-bit ARM targets.

The only difference to `Gcc_Clang` is that anonymous and 0-length
bit-fields do contribute to the aggregate alignment.

Caught by existing proper C interop tests in
runnable_cxx/testbitfields.d on such targets. The hardcoded bad tests
in runnable/{bitfieldsposix64.c,dbitfieldsposix64.d} however now fail
after the fix, on such targets again.

* [refactor to `TargetC.contributesToAggregateAlignment(BitFieldDeclaration)` hook]

* Fix Bugzilla Issue 24687 - [REG2.110] Cannot cast string-imports to select overload anymore

* Also make deprecationSupplemental adhere to error limit (#16779)

Co-authored-by: Dennis Korpel <dennis@sarc.nl>

* Fix bugzilla 24699 - [REG2.108] No short-circuit evaluation of mixing template bool argument

* Fix bugzilla 24731 - IFTI cannot handle integer expressions (#16822)

* Fix Bugzilla Issue 24760 - ICE on variadic after default argument

* Fix bugzilla 24790 - -vcg-ast ICE on lowered assign exp (#16914)

Co-authored-by: Dennis Korpel <dennis@sarc.nl>

* Fix bugzilla 24764 - ICE when -vcg-ast prints imported invariant (#16917)

Co-authored-by: Dennis Korpel <dennis@sarc.nl>

* Fix bugzilla 24431 - dmd -vcg-ast crashes printing failed template in… (#16916)

---------

Co-authored-by: Richard (Rikki) Andrew Cattermole <richard@cattermole.co.nz>
Co-authored-by: Martin Kinkelin <kinke@users.noreply.github.com>
Co-authored-by: Dennis <dkorpel@users.noreply.github.com>
Co-authored-by: Martin Kinkelin <mkinkelin@symmetryinvestments.com>
Co-authored-by: Martin Kinkelin <noone@nowhere.com>
Co-authored-by: RazvanN7 <razvan.nitu1305@gmail.com>
Co-authored-by: Dennis Korpel <dennis@sarc.nl>
Co-authored-by: Dennis Korpel <dkorpel@gmail.com>
2024-10-07 12:00:14 +03:00

880 lines
18 KiB
D

// REQUIRED_ARGS: -o-
// EXTRA_FILES: imports/imp12242a1.d imports/imp12242a2.d
// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
9
8
7
6
5
4
3
2
1
0
S(1, 2, 3, [0, 1, 2])
x0: 1
x1: 2
x2: 3
a: [0, 1, 2]
(int[], char[], bool[], Object[])
[0, 0]
x0: int
x1: double
x2: char
test(0) 0
test(1) 1
test(2) 2
test(3) 3
test(4) 4
test(5) 5
test(6) 6
test(7) 7
test(8) 8
test(9) 9
test(10) -1
test(11) -1
test(12) -1
test(13) -1
test(14) -1
1
[1, 2, 3]
2
[1, 2, 3]
3
[1, 2, 3]
0 1
1 2
2 3
1
3
4
object
Tuple
tuple
main
front
popFront
empty
back
popBack
Iota
iota
map
to
text
all
any
join
S
s
Seq
Overloads
Parameters
forward
foo
A
B
C
D
E
Types
Visitor
testVisitor
staticMap
arrayOf
StaticForeachLoopVariable
StaticForeachScopeExit
StaticForeachReverseHiding
UnrolledForeachReverse
StaticForeachReverse
StaticForeachByAliasDefault
NestedStaticForeach
TestAliasOutsideFunctionScope
OpApplyMultipleStaticForeach
OpApplyMultipleStaticForeachLowered
RangeStaticForeach
OpApplySingleStaticForeach
TypeStaticForeach
AliasForeach
EnumForeach
TestUninterpretable
SeqForeachConstant
SeqForeachBreakContinue
TestStaticForeach
testtest
fun
testEmpty
bug17660
breakContinueBan
MixinTemplate
testToStatement
bug17688
T
foo2
T2
TestStaticForeach2
issue22007
1 2 3
2 3 4
0 1
1 2
2 3
---
*/
module staticforeach;
struct Tuple(T...){
T expand;
alias expand this;
}
auto tuple(T...)(T t){ return Tuple!T(t); }
/+struct TupleStaticForeach{ // should work, but is not the fault of the static foreach implementation.
//pragma(msg, [tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x));
static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){
pragma(msg,a," ",b," ",c);
}
}+/
void main(){
static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){
pragma(msg, a," ",b," ",c);
}
static struct S{
// (aggregate scope, forward referencing possible)
static assert(stripA("123")==1);
static assert(stripA([1],2)==2);
static foreach(i;0..2){
mixin(`import imports.imp12242a`~text(i+1)~`;`);
static assert(stripA("123")==1);
static assert(stripA([1],2)==2);
}
static assert(stripA("123")==1);
static assert(stripA([1],2)==2);
}
static foreach(i;0..2){
// (function scope, no forward referencing)
mixin(`import imports.imp12242a`~text(i+1)~`;`);
static assert(stripA("123")==1);
static if(i) static assert(stripA([1],2)==2);
}
static assert(stripA("123")==1);
static assert(stripA([1],2)==2);
}
auto front(T)(T[] a){ return a[0]; }
auto popFront(T)(ref T[] a){ a=a[1..$]; }
auto empty(T)(T[] a){ return !a.length; }
auto back(T)(T[] a){ return a[$-1]; }
auto popBack(T)(ref T[] a){ a=a[0..$-1]; }
struct Iota(T){
T s,e;
@property bool empty(){ return s>=e; }
@property T front(){ return s; }
@property T back(){ return cast(T)(e-1); }
void popFront(){ s++; }
void popBack(){ e--; }
}
auto iota(T)(T s, T e){ return Iota!T(s,e); }
template map(alias a){
struct Map(R){
R r;
@property front(){ return a(r.front); }
@property back(){ return a(r.back); }
@property bool empty(){ return r.empty; }
void popFront(){ r.popFront(); }
void popBack(){ r.popBack(); }
}
auto map(R)(R r){ return Map!R(r); }
}
template to(T:string){
string to(S)(S x)if(is(S:int)||is(S:size_t)||is(S:char)){
static if(is(S==char)) return cast(string)[x];
if(x<0) return "-"~to(-1 * x);
if(x==0) return "0";
return (x>=10?to(x/10):"")~cast(char)(x%10+'0');
}
}
auto text(T)(T arg){ return to!string(arg); };
template all(alias a){
bool all(R)(R r){
foreach(x;r) if(!a(x)) return false;
return true;
}
}
template any(alias a){
bool any(R)(R r){
foreach(x;r) if(a(x)) return true;
return false;
}
}
auto join(R)(R r,string sep=""){
string a;
int first=0;
foreach(x;r){
if(first++) a~=sep;
a~=x;
}
return a;
}
static foreach_reverse(x;iota(0,10).map!(to!string)){
pragma(msg, x);
}
// create struct members iteratively
struct S{
static foreach(i;a){
mixin("int x"~to!string(i)~";");
}
immutable int[] a = [0,1,2];
}
enum s=S(1,2,3);
pragma(msg, s);
// loop over struct members
static foreach(member;__traits(allMembers,S)){
pragma(msg, member,": ",mixin("s."~member));
}
// print prime numbers using overload sets as state variables.
/+
static assert(is(typeof(bad57)));
static assert(!is(typeof(bad53)));
static foreach(x;iota(2,100)){
static foreach(y;iota(2,x)){
static if(!(x%y)){
mixin("void bad"~to!string(x)~"();");
}
}
static if(!is(typeof(mixin("bad"~to!string(x))))){
static assert(iota(2,x).all!(y=>!!(x%y)));
pragma(msg, x);
}else{
static assert(iota(2,x).any!(y=>!(x%y)));
}
}
+/
alias Seq(T...)=T;
alias Overloads(alias a) = Seq!(__traits(getOverloads, __traits(parent, a), __traits(identifier, a)));
template Parameters(alias f){
static if(is(typeof(f) P == function)) alias Parameters=P;
}
template forward(alias a){
enum x=2;
static foreach(f;Overloads!a){
auto ref forward(Parameters!f args){
return f(args);
}
}
enum y=3;
}
int foo(int x){ return x; }
string foo(string x){ return x; }
static assert(forward!foo(2)==2 && forward!foo("hi") == "hi");
// simple boilerplate-free visitor pattern
static foreach(char T;'A'..'F'){
mixin("class "~T~q{{
void accept(Visitor v){
return v.visit(this);
}
}});
}
alias Types = Seq!(mixin("Seq!("~iota('A','F').map!(to!string).join(", ")~")"));
abstract class Visitor{
static foreach(T;Types){
abstract void visit(T);
}
}
string testVisitor(){
string r;
void writeln(T...)(T args){
static foreach(x;args) r~=x;
r~='\n';
}
class Visitor: .Visitor{
static foreach(T;Types){
override void visit(T){
writeln("visited: ",T.stringof);
}
}
}
void main(){
auto v=new Visitor;
static foreach(T;Types){
v.visit(new T);
}
}
main();
return r;
}
static assert(testVisitor()=="visited: A
visited: B
visited: C
visited: D
visited: E
");
// iterative computation over AliasSeq:
template staticMap(alias F,T...){
alias state0=Seq!();
static foreach(i,A;T){
mixin("alias state"~to!string(i+1)~" = Seq!(state"~to!string(i)~",F!A);");
}
alias staticMap = Seq!(mixin("state"~to!string(T.length)));
}
alias arrayOf(T)=T[];
static assert(is(staticMap!(arrayOf,int,char,bool,Object)==Seq!(int[], char[], bool[], Object[])));
pragma(msg, staticMap!(arrayOf,int,char,bool,Object));
struct StaticForeachLoopVariable{
int x;
static foreach(i;0..1){
mixin("enum x"~text(i)~" = i;");
}
int y;
static assert(__traits(allMembers, StaticForeachLoopVariable).length==3);
static assert(!is(typeof(StaticForeachLoopVariable.i)));
static assert(!is(typeof(__traits(getMember, StaticForeachLoopVariable, "i"))));
}
struct StaticForeachScopeExit{
static:
int[] test(){
int[] r;
scope(exit) r ~= 1234;
{
static foreach(i;0..5){
scope(exit) r ~= i;
}
r ~= 5;
}
return r;
}
static assert(test()==[5,4,3,2,1,0]);
}
struct StaticForeachReverseHiding{
static foreach(i;[0]){
enum i = 1; // TODO: disallow?
static assert(i==0);
}
}
struct UnrolledForeachReverse{
static:
alias Seq(T...)=T;
int[] test(){
int[] r;
foreach_reverse(i;Seq!(0,1,2,3)){
r~=i;
}
return r;
}
static assert(test()==[3,2,1,0]);
}
struct StaticForeachReverse{
static:
alias Seq(T...)=T;
int[] test(){
int[] r;
static foreach_reverse(i;0..4){
r~=i;
}
return r;
}
static assert(test()==[3,2,1,0]);
int[] test2(){
int[] r;
static foreach_reverse(i;[0,1,2,3]){
r~=i;
}
return r;
}
static assert(test2()==[3,2,1,0]);
int[] test3(){
static struct S{
int opApplyReverse(scope int delegate(int) dg){
foreach_reverse(i;0..4) if(auto r=dg(i)) return r;
return 0;
}
}
int[] r;
static foreach_reverse(i;S()){
r~=i;
}
return r;
}
static assert(test3()==[3,2,1,0]);
int[] test4(){
int[] r;
static foreach_reverse(i;Seq!(0,1,2,3)){
r~=i;
}
return r;
}
static assert(test()==[3,2,1,0]);
}
struct StaticForeachByAliasDefault{
static:
alias Seq(T...)=T;
int[] test(){
int a,b,c;
static foreach(i,x;Seq!(a,b,c)) x=i;
return [a,b,c];
}
static assert(test()==[0,1,2]);
int[] test2(){
int x=0;
int foo(){ return ++x; }
static foreach(y;Seq!foo)
return [y,y,y];
}
static assert(test2()==[1,2,3]);
void test3(){
int x=0;
int foo(){ return ++x; }
static assert(!is(typeof({
static foreach(enum y;Seq!foo)
return [y,y,y];
})));
}
}
struct NestedStaticForeach{
static:
static foreach(i,name;["a"]){
static foreach(j,name2;["d"]){
mixin("enum int[] "~name~name2~"=[i, j];");
}
}
pragma(msg, ad);
}
struct TestAliasOutsideFunctionScope{
static:
alias Seq(T...)=T;
int a;
static foreach(alias x;Seq!(a)){
}
}
struct OpApplyMultipleStaticForeach{
static:
struct OpApply{
int opApply(scope int delegate(int,int) dg){
foreach(i;0..10) if(auto r=dg(i,i*i)) return r;
return 0;
}
}
static foreach(a,b;OpApply()){
mixin(`enum x`~cast(char)('0'+a)~"=b;");
}
static foreach(i;0..10){
static assert(mixin(`x`~cast(char)('0'+i))==i*i);
}
}
struct OpApplyMultipleStaticForeachLowered{
static:
struct OpApply{
int opApply(scope int delegate(int,int) dg){
foreach(i;0..10) if(auto r=dg(i,i*i)) return r;
return 0;
}
}
static foreach(x;{
static struct S(T...){ this(T k){ this.x=k; } T x; }
static s(T...)(T a){ return S!T(a); }
typeof({ foreach(a,b;OpApply()){ return s(a,b); } assert(0);}())[] r;
foreach(a,b;OpApply()) r~=s(a,b);
return r;
}()){
mixin(`enum x`~cast(char)('0'+x.x[0])~"=x.x[1];");
}
static foreach(i;0..10){
static assert(mixin(`x`~cast(char)('0'+i))==i*i);
}
}
struct RangeStaticForeach{
static:
struct Range{
int x=0;
this(int x){ this.x=x; }
@property int front(){ return x; }
void popFront(){ x += 2; }
@property bool empty(){ return x>=10; }
}
static foreach(i;Range()){
mixin(`enum x`~cast(char)('0'+i)~"=i;");
}
static foreach(i;0..5){
static assert(mixin(`x`~cast(char)('0'+2*i))==2*i);
}
static assert(!is(typeof({
struct S{
static foreach(i,k;Range()){}
}
})));
static foreach(k;Range()){} // ok
}
struct OpApplySingleStaticForeach{
static:
struct OpApply{
int opApply(scope int delegate(int) dg){
foreach(i;0..10) if(auto r=dg(i)) return r;
return 0;
}
}
static foreach(b;OpApply()){
mixin(`enum x`~cast(char)('0'+b)~"=b;");
}
static foreach(i;0..10){
static assert(mixin(`x`~cast(char)('0'+i))==i);
}
}
struct TypeStaticForeach{
static:
alias Seq(T...)=T;
static foreach(i,alias T;Seq!(int,double,char)){
mixin(`T x`~cast(char)('0'+i)~";");
}
pragma(msg, "x0: ",typeof(x0));
pragma(msg, "x1: ",typeof(x1));
pragma(msg, "x2: ",typeof(x2));
static assert(is(typeof(x0)==int));
static assert(is(typeof(x1)==double));
static assert(is(typeof(x2)==char));
}
struct AliasForeach{
static:
alias Seq(T...)=T;
int[] test(){
int a,b,c;
static foreach(x;Seq!(a,b,c,2)){
static if(is(typeof({x=2;}))) x=2;
}
int x,y,z;
static foreach(alias k;Seq!(x,y,z,2)){
static if(is(typeof({k=2;}))) k=2;
}
int j,k,l;
static assert(!is(typeof({
static foreach(ref x;Seq!(j,k,l,2)){
static if(is(typeof({x=2;}))) x=2;
}
})));
return [x,y,z];
}
static assert(test()==[2,2,2]);
}
struct EnumForeach{
static:
alias Seq(T...)=T;
int a=1;
int fun(){ return 1; }
int gun(){ return 2; }
int hun(){ return 3;}
auto test(){
static foreach(i,enum x;Seq!(fun,gun,hun)){
static assert(i+1==x);
}
foreach(i,enum x;Seq!(fun,gun,hun)){
static assert(i+1==x);
}
}
}
struct TestUninterpretable{
static:
alias Seq(T...)=T;
auto test(){
int k;
static assert(!is(typeof({
static foreach(x;[k]){}
})));
static assert(!is(typeof({
foreach(enum x;[1,2,3]){}
})));
static assert(!is(typeof({
foreach(alias x;[1,2,3]){}
})));
foreach(enum x;Seq!(1,2,3)){} // ok
foreach(alias x;Seq!(1,2,3)){} // ok
static foreach(enum x;[1,2,3]){} // ok
static foreach(alias x;[1,2,3]){} // ok
static assert(!is(typeof({
static foreach(enum alias x;[1,2,3]){}
})));
int x;
static foreach(i;Seq!x){ } // ok
static foreach(i,j;Seq!(1,2,x)){ } // ok
static assert(!is(typeof({
static foreach(ref x;[1,2,3]){}
})));
}
}
struct SeqForeachConstant{
static:
alias Seq(T...)=T;
static assert(!is(typeof({
foreach(x;Seq!1) x=2;
})));
int test2(){
int r=0;
foreach(x;Seq!(1,2,3)){
enum k=x;
r+=k;
}
return r;
}
static assert(test2()==6);
}
struct SeqForeachBreakContinue{
static:
alias Seq(T...)=T;
int[] test(){
int[] r;
foreach(i;Seq!(0,1,2,3,4,5)){
if(i==2) continue;
if(i==4) break;
r~=i;
}
return r;
}
static assert(test()==[0,1,3]);
}
struct TestStaticForeach{
static:
int test(int x){
int r=0;
label: switch(x){
static foreach(i;0..10){
case i: r=i; break label; // TODO: remove label when restriction is lifted
}
default: r=-1; break label;
}
return r;
}
static foreach(i;0..15){
pragma(msg, "test(",i,")→ ",test(i));
static assert(test(i)==(i<10?i:-1));
}
enum x=[1,2,3];
static foreach(i;x){
mixin("enum x"~cast(char)('0'+i)~"="~cast(char)('0'+i)~";");
}
static foreach(i;x){
pragma(msg, mixin("x"~cast(char)('0'+i)));
pragma(msg,x);
}
int[] noBreakNoContinue(){
int[] r;
static foreach(i;0..1){
// if(i==3) continue; // TODO: error?
// if(i==7) break; // TODO: error?
r~=i;
}
return r;
}
mixin("enum k=3;");
}
static foreach(i,j;[1,2,3]){
pragma(msg, int(i)," ",j);
}
void testtest(){
static foreach(i,v;[1,2,3]){
pragma(msg, int(i)," ",v);
static assert(i+1 == v);
}
}
static foreach(i;Seq!(1,2,3,4,int)){
static if(!is(i) && i!=2){
pragma(msg, i);
}
}
int fun(int x){
int r=0;
label: switch(x){
static foreach(i;Seq!(0,1,2,3,4,5,6)){
static if (i < 5)
case i: r=i; break label; // TODO: remove label when restriction is lifted
}
default: r=-1; break label;
}
return r;
}
static foreach(i;0..10) static assert(fun(i)==(i<5?i:-1));
static foreach(i;0..0) { }
void testEmpty(){
static foreach(i;0..0) { }
}
auto bug17660(){
int x;
static foreach (i; 0 .. 1) { return 3; }
return x;
}
static assert(bug17660()==3);
int breakContinueBan(){
static assert(!is(typeof({
for(;;){
static foreach(i;0..1){
break;
}
}
})));
static assert(!is(typeof({
for(;;){
static foreach(i;0..1){
continue;
}
}
})));
Louter1: for(;;){
static foreach(i;0..1){
break Louter1;
}
}
Louter2: foreach(i;0..10){
static foreach(j;0..1){
continue Louter2;
}
return 0;
}
static foreach(i;0..1){
for(;;){ break; } // ok
}
return 1;
}
static assert(breakContinueBan()==1);
mixin template MixinTemplate(){
static foreach(i;0..2){
mixin(`enum x`~cast(char)('0'+i)~"=i;");
}
static foreach(i;[0,1]){
mixin(`enum y`~cast(char)('0'+i)~"=i;");
}
}
void testToStatement(){
mixin MixinTemplate;
static assert(x0==0 && x1==1);
static assert(y0==0 && y1==1);
}
void bug17688(){
final switch(1) static foreach(x;0..1){ int y=3; case 1: return; }
static assert(!is(typeof(y)));
}
struct T{ enum n = 1; }
T foo(T v)@nogc{
static foreach(x;0..v.n){ }
return T.init;
}
T foo2(T v)@nogc{
static foreach(_;0..typeof(return).n){ }
return T.init;
}
//https://issues.dlang.org/show_bug.cgi?id=18698
static foreach(m; __traits(allMembers, staticforeach))
{
pragma(msg, m);
}
//https://issues.dlang.org/show_bug.cgi?id=20072
struct T2{
static foreach(i;0..1)
struct S{}
}
static assert(is(__traits(parent,T2.S)==T2));
struct TestStaticForeach2
{
static:
// StringExp
char[] test(string str)()
{
char[] s;
static foreach (c; str)
{
s ~= c;
}
return s;
}
static assert(test!"tёstñ" == ['t', '\xd1', '\x91', 's', 't', '\xc3', '\xb1']);
static foreach (c; "")
{
static assert(0);
}
// NullExp
enum int[] a = null;
static foreach (c; a)
{
static assert(0);
}
}
//https://issues.dlang.org/show_bug.cgi?id=22007
void issue22007()
{
immutable int[32] array = 1;
foreach (size_t a, int b; array) {}
static foreach (size_t a, int b; array) { }
}