]>
git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/arrayop.c
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/arrayop.c
15 #include "root/rmem.h"
19 #include "expression.h"
20 #include "statement.h"
22 #include "declaration.h"
29 void buildArrayIdent(Expression
*e
, OutBuffer
*buf
, Expressions
*arguments
);
30 Expression
*buildArrayLoop(Expression
*e
, Parameters
*fparams
);
31 Expression
*semantic(Expression
*e
, Scope
*sc
);
33 /**************************************
34 * Hash table of array op functions already generated or known about.
39 /**************************************
40 * Structure to contain information needed to insert an array op call
43 FuncDeclaration
*buildArrayOp(Identifier
*ident
, BinExp
*exp
, Scope
*sc
)
45 Parameters
*fparams
= new Parameters();
46 Expression
*loopbody
= buildArrayLoop(exp
, fparams
);
48 /* Construct the function body:
49 * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
54 Parameter
*p
= (*fparams
)[0];
55 // foreach (i; 0 .. p.length)
56 Statement
*s1
= new ForeachRangeStatement(Loc(), TOKforeach
,
57 new Parameter(0, NULL
, Id::p
, NULL
),
58 new IntegerExp(Loc(), 0, Type::tsize_t
),
59 new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p
->ident
)),
60 new ExpStatement(Loc(), loopbody
),
62 //printf("%s\n", s1->toChars());
63 Statement
*s2
= new ReturnStatement(Loc(), new IdentifierExp(Loc(), p
->ident
));
64 //printf("s2: %s\n", s2->toChars());
65 Statement
*fbody
= new CompoundStatement(Loc(), s1
, s2
);
67 // Built-in array ops should be @trusted, pure, nothrow and nogc
68 StorageClass stc
= STCtrusted
| STCpure
| STCnothrow
| STCnogc
;
70 /* Construct the function
72 TypeFunction
*ftype
= new TypeFunction(fparams
, exp
->e1
->type
, 0, LINKc
, stc
);
73 //printf("fd: %s %s\n", ident->toChars(), ftype->toChars());
74 FuncDeclaration
*fd
= new FuncDeclaration(Loc(), Loc(), ident
, STCundefined
, ftype
);
76 fd
->protection
= Prot(PROTpublic
);
80 sc
->_module
->importedFrom
->members
->push(fd
);
83 sc
->parent
= sc
->_module
->importedFrom
;
88 unsigned errors
= global
.startGagging();
90 if (global
.endGagging(errors
))
92 fd
->type
= Type::terror
;
101 /**********************************************
102 * Check that there are no uses of arrays without [].
104 bool isArrayOpValid(Expression
*e
)
106 if (e
->op
== TOKslice
)
108 if (e
->op
== TOKarrayliteral
)
110 Type
*t
= e
->type
->toBasetype();
111 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
112 t
= t
->nextOf()->toBasetype();
113 return (t
->ty
!= Tvoid
);
115 Type
*tb
= e
->type
->toBasetype();
116 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
118 if (isUnaArrayOp(e
->op
))
120 return isArrayOpValid(((UnaExp
*)e
)->e1
);
122 if (isBinArrayOp(e
->op
) ||
123 isBinAssignArrayOp(e
->op
) ||
126 BinExp
*be
= (BinExp
*)e
;
127 return isArrayOpValid(be
->e1
) && isArrayOpValid(be
->e2
);
129 if (e
->op
== TOKconstruct
)
131 BinExp
*be
= (BinExp
*)e
;
132 return be
->e1
->op
== TOKslice
&& isArrayOpValid(be
->e2
);
134 if (e
->op
== TOKcall
)
136 return false; // TODO: Decide if [] is required after arrayop calls.
146 bool isNonAssignmentArrayOp(Expression
*e
)
148 if (e
->op
== TOKslice
)
149 return isNonAssignmentArrayOp(((SliceExp
*)e
)->e1
);
151 Type
*tb
= e
->type
->toBasetype();
152 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
154 return (isUnaArrayOp(e
->op
) || isBinArrayOp(e
->op
));
159 bool checkNonAssignmentArrayOp(Expression
*e
, bool suggestion
)
161 if (isNonAssignmentArrayOp(e
))
165 s
= " (possible missing [])";
166 e
->error("array operation %s without destination memory not allowed%s", e
->toChars(), s
);
172 /***********************************
173 * Construct the array operation expression.
176 Expression
*arrayOp(BinExp
*e
, Scope
*sc
)
178 //printf("BinExp::arrayOp() %s\n", toChars());
180 Type
*tb
= e
->type
->toBasetype();
181 assert(tb
->ty
== Tarray
|| tb
->ty
== Tsarray
);
182 Type
*tbn
= tb
->nextOf()->toBasetype();
183 if (tbn
->ty
== Tvoid
)
185 e
->error("cannot perform array operations on void[] arrays");
186 return new ErrorExp();
188 if (!isArrayOpValid(e
))
190 e
->error("invalid array operation %s (possible missing [])", e
->toChars());
191 return new ErrorExp();
194 Expressions
*arguments
= new Expressions();
196 /* The expression to generate an array operation for is mangled
197 * into a name to use as the array operation function name.
198 * Mangle in the operands and operators in RPN order, and type.
201 buf
.writestring("_array");
202 buildArrayIdent(e
, &buf
, arguments
);
205 /* Append deco of array element type
207 buf
.writestring(e
->type
->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco
);
209 char *name
= buf
.peekString();
210 Identifier
*ident
= Identifier::idPool(name
);
212 FuncDeclaration
**pFd
= (FuncDeclaration
**)dmd_aaGet(&arrayfuncs
, (void *)ident
);
213 FuncDeclaration
*fd
= *pFd
;
216 fd
= buildArrayOp(ident
, e
, sc
);
218 if (fd
&& fd
->errors
)
221 if (tbn
->ty
== Tstruct
|| tbn
->ty
== Tclass
)
222 fmt
= "invalid array operation '%s' because %s doesn't support necessary arithmetic operations";
223 else if (!tbn
->isscalar())
224 fmt
= "invalid array operation '%s' because %s is not a scalar type";
226 fmt
= "invalid array operation '%s' for element type %s";
228 e
->error(fmt
, e
->toChars(), tbn
->toChars());
229 return new ErrorExp();
234 Expression
*ev
= new VarExp(e
->loc
, fd
);
235 Expression
*ec
= new CallExp(e
->loc
, ev
, arguments
);
237 return semantic(ec
, sc
);
240 Expression
*arrayOp(BinAssignExp
*e
, Scope
*sc
)
242 //printf("BinAssignExp::arrayOp() %s\n", toChars());
244 /* Check that the elements of e1 can be assigned to
246 Type
*tn
= e
->e1
->type
->toBasetype()->nextOf();
248 if (tn
&& (!tn
->isMutable() || !tn
->isAssignable()))
250 e
->error("slice %s is not mutable", e
->e1
->toChars());
251 return new ErrorExp();
253 if (e
->e1
->op
== TOKarrayliteral
)
255 return e
->e1
->modifiableLvalue(sc
, e
->e1
);
258 return arrayOp((BinExp
*)e
, sc
);
261 /******************************************
262 * Construct the identifier for the array operation function,
263 * and build the argument list to pass to it.
266 void buildArrayIdent(Expression
*e
, OutBuffer
*buf
, Expressions
*arguments
)
268 class BuildArrayIdentVisitor
: public Visitor
271 Expressions
*arguments
;
273 BuildArrayIdentVisitor(OutBuffer
*buf
, Expressions
*arguments
)
274 : buf(buf
), arguments(arguments
)
278 void visit(Expression
*e
)
280 buf
->writestring("Exp");
284 void visit(CastExp
*e
)
286 Type
*tb
= e
->type
->toBasetype();
287 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
292 visit((Expression
*)e
);
295 void visit(ArrayLiteralExp
*e
)
297 buf
->writestring("Slice");
301 void visit(SliceExp
*e
)
303 buf
->writestring("Slice");
307 void visit(AssignExp
*e
)
309 /* Evaluate assign expressions right to left
313 buf
->writestring("Assign");
316 void visit(BinAssignExp
*e
)
318 /* Evaluate assign expressions right to left
325 case TOKaddass
: s
= "Addass"; break;
326 case TOKminass
: s
= "Minass"; break;
327 case TOKmulass
: s
= "Mulass"; break;
328 case TOKdivass
: s
= "Divass"; break;
329 case TOKmodass
: s
= "Modass"; break;
330 case TOKxorass
: s
= "Xorass"; break;
331 case TOKandass
: s
= "Andass"; break;
332 case TOKorass
: s
= "Orass"; break;
333 case TOKpowass
: s
= "Powass"; break;
339 void visit(NegExp
*e
)
342 buf
->writestring("Neg");
345 void visit(ComExp
*e
)
348 buf
->writestring("Com");
351 void visit(BinExp
*e
)
353 /* Evaluate assign expressions left to right
355 const char *s
= NULL
;
358 case TOKadd
: s
= "Add"; break;
359 case TOKmin
: s
= "Min"; break;
360 case TOKmul
: s
= "Mul"; break;
361 case TOKdiv
: s
= "Div"; break;
362 case TOKmod
: s
= "Mod"; break;
363 case TOKxor
: s
= "Xor"; break;
364 case TOKand
: s
= "And"; break;
365 case TOKor
: s
= "Or"; break;
366 case TOKpow
: s
= "Pow"; break;
371 Type
*tb
= e
->type
->toBasetype();
372 Type
*t1
= e
->e1
->type
->toBasetype();
373 Type
*t2
= e
->e2
->type
->toBasetype();
375 if (t1
->ty
== Tarray
&&
376 ((t2
->ty
== Tarray
&& !t1
->equivalent(tb
)) ||
377 (t2
->ty
!= Tarray
&& !t1
->nextOf()->equivalent(e
->e2
->type
))))
379 // Bugzilla 12780: if A is narrower than B
382 buf
->writestring("Of");
383 buf
->writestring(t1
->nextOf()->mutableOf()->deco
);
386 if (t2
->ty
== Tarray
&&
387 ((t1
->ty
== Tarray
&& !t2
->equivalent(tb
)) ||
388 (t1
->ty
!= Tarray
&& !t2
->nextOf()->equivalent(e
->e1
->type
))))
390 // Bugzilla 12780: if B is narrower than A:
393 buf
->writestring("Of");
394 buf
->writestring(t2
->nextOf()->mutableOf()->deco
);
399 visit((Expression
*)e
);
403 BuildArrayIdentVisitor
v(buf
, arguments
);
407 /******************************************
408 * Construct the inner loop for the array operation function,
409 * and build the parameter list.
412 Expression
*buildArrayLoop(Expression
*e
, Parameters
*fparams
)
414 class BuildArrayLoopVisitor
: public Visitor
420 BuildArrayLoopVisitor(Parameters
*fparams
)
421 : fparams(fparams
), result(NULL
)
425 void visit(Expression
*e
)
427 Identifier
*id
= Identifier::generateId("c", fparams
->dim
);
428 Parameter
*param
= new Parameter(0, e
->type
, id
, NULL
);
429 fparams
->shift(param
);
430 result
= new IdentifierExp(Loc(), id
);
433 void visit(CastExp
*e
)
435 Type
*tb
= e
->type
->toBasetype();
436 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
441 visit((Expression
*)e
);
444 void visit(ArrayLiteralExp
*e
)
446 Identifier
*id
= Identifier::generateId("p", fparams
->dim
);
447 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
);
448 fparams
->shift(param
);
449 Expression
*ie
= new IdentifierExp(Loc(), id
);
450 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
451 result
= new ArrayExp(Loc(), ie
, index
);
454 void visit(SliceExp
*e
)
456 Identifier
*id
= Identifier::generateId("p", fparams
->dim
);
457 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
);
458 fparams
->shift(param
);
459 Expression
*ie
= new IdentifierExp(Loc(), id
);
460 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
461 result
= new ArrayExp(Loc(), ie
, index
);
464 void visit(AssignExp
*e
)
466 /* Evaluate assign expressions right to left
468 Expression
*ex2
= buildArrayLoop(e
->e2
);
469 /* Need the cast because:
471 * where b is a byte fails because (c + p[i]) is an int
472 * which cannot be implicitly cast to byte.
474 ex2
= new CastExp(Loc(), ex2
, e
->e1
->type
->nextOf());
475 Expression
*ex1
= buildArrayLoop(e
->e1
);
476 Parameter
*param
= (*fparams
)[0];
477 param
->storageClass
= 0;
478 result
= new AssignExp(Loc(), ex1
, ex2
);
481 void visit(BinAssignExp
*e
)
483 /* Evaluate assign expressions right to left
485 Expression
*ex2
= buildArrayLoop(e
->e2
);
486 Expression
*ex1
= buildArrayLoop(e
->e1
);
487 Parameter
*param
= (*fparams
)[0];
488 param
->storageClass
= 0;
491 case TOKaddass
: result
= new AddAssignExp(e
->loc
, ex1
, ex2
); return;
492 case TOKminass
: result
= new MinAssignExp(e
->loc
, ex1
, ex2
); return;
493 case TOKmulass
: result
= new MulAssignExp(e
->loc
, ex1
, ex2
); return;
494 case TOKdivass
: result
= new DivAssignExp(e
->loc
, ex1
, ex2
); return;
495 case TOKmodass
: result
= new ModAssignExp(e
->loc
, ex1
, ex2
); return;
496 case TOKxorass
: result
= new XorAssignExp(e
->loc
, ex1
, ex2
); return;
497 case TOKandass
: result
= new AndAssignExp(e
->loc
, ex1
, ex2
); return;
498 case TOKorass
: result
= new OrAssignExp(e
->loc
, ex1
, ex2
); return;
499 case TOKpowass
: result
= new PowAssignExp(e
->loc
, ex1
, ex2
); return;
505 void visit(NegExp
*e
)
507 Expression
*ex1
= buildArrayLoop(e
->e1
);
508 result
= new NegExp(Loc(), ex1
);
511 void visit(ComExp
*e
)
513 Expression
*ex1
= buildArrayLoop(e
->e1
);
514 result
= new ComExp(Loc(), ex1
);
517 void visit(BinExp
*e
)
519 if (isBinArrayOp(e
->op
))
521 /* Evaluate assign expressions left to right
523 BinExp
*be
= (BinExp
*)e
->copy();
524 be
->e1
= buildArrayLoop(be
->e1
);
525 be
->e2
= buildArrayLoop(be
->e2
);
532 visit((Expression
*)e
);
537 Expression
*buildArrayLoop(Expression
*e
)
544 BuildArrayLoopVisitor
v(fparams
);
545 return v
.buildArrayLoop(e
);
548 /***********************************************
549 * Test if expression is a unary array op.
552 bool isUnaArrayOp(TOK op
)
565 /***********************************************
566 * Test if expression is a binary array op.
569 bool isBinArrayOp(TOK op
)
589 /***********************************************
590 * Test if expression is a binary assignment array op.
593 bool isBinAssignArrayOp(TOK op
)
613 /***********************************************
614 * Test if operand is a valid array op operand.
617 bool isArrayOpOperand(Expression
*e
)
619 //printf("Expression::isArrayOpOperand() %s\n", e->toChars());
620 if (e
->op
== TOKslice
)
622 if (e
->op
== TOKarrayliteral
)
624 Type
*t
= e
->type
->toBasetype();
625 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
626 t
= t
->nextOf()->toBasetype();
627 return (t
->ty
!= Tvoid
);
629 Type
*tb
= e
->type
->toBasetype();
630 if (tb
->ty
== Tarray
)
632 return (isUnaArrayOp(e
->op
) ||
633 isBinArrayOp(e
->op
) ||
634 isBinAssignArrayOp(e
->op
) ||