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
11 #include "root/dsystem.h"
12 #include "root/rmem.h"
16 #include "expression.h"
17 #include "statement.h"
19 #include "declaration.h"
26 void buildArrayIdent(Expression
*e
, OutBuffer
*buf
, Expressions
*arguments
);
27 Expression
*buildArrayLoop(Expression
*e
, Parameters
*fparams
);
28 Expression
*semantic(Expression
*e
, Scope
*sc
);
30 /**************************************
31 * Hash table of array op functions already generated or known about.
36 /**************************************
37 * Structure to contain information needed to insert an array op call
40 FuncDeclaration
*buildArrayOp(Identifier
*ident
, BinExp
*exp
, Scope
*sc
)
42 Parameters
*fparams
= new Parameters();
43 Expression
*loopbody
= buildArrayLoop(exp
, fparams
);
45 /* Construct the function body:
46 * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
51 Parameter
*p
= (*fparams
)[0];
52 // foreach (i; 0 .. p.length)
53 Statement
*s1
= new ForeachRangeStatement(Loc(), TOKforeach
,
54 new Parameter(0, NULL
, Id::p
, NULL
),
55 new IntegerExp(Loc(), 0, Type::tsize_t
),
56 new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p
->ident
)),
57 new ExpStatement(Loc(), loopbody
),
59 //printf("%s\n", s1->toChars());
60 Statement
*s2
= new ReturnStatement(Loc(), new IdentifierExp(Loc(), p
->ident
));
61 //printf("s2: %s\n", s2->toChars());
62 Statement
*fbody
= new CompoundStatement(Loc(), s1
, s2
);
64 // Built-in array ops should be @trusted, pure, nothrow and nogc
65 StorageClass stc
= STCtrusted
| STCpure
| STCnothrow
| STCnogc
;
67 /* Construct the function
69 TypeFunction
*ftype
= new TypeFunction(fparams
, exp
->e1
->type
, 0, LINKc
, stc
);
70 //printf("fd: %s %s\n", ident->toChars(), ftype->toChars());
71 FuncDeclaration
*fd
= new FuncDeclaration(Loc(), Loc(), ident
, STCundefined
, ftype
);
73 fd
->protection
= Prot(PROTpublic
);
77 sc
->_module
->importedFrom
->members
->push(fd
);
80 sc
->parent
= sc
->_module
->importedFrom
;
85 unsigned errors
= global
.startGagging();
87 if (global
.endGagging(errors
))
89 fd
->type
= Type::terror
;
98 /**********************************************
99 * Check that there are no uses of arrays without [].
101 bool isArrayOpValid(Expression
*e
)
103 if (e
->op
== TOKslice
)
105 if (e
->op
== TOKarrayliteral
)
107 Type
*t
= e
->type
->toBasetype();
108 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
109 t
= t
->nextOf()->toBasetype();
110 return (t
->ty
!= Tvoid
);
112 Type
*tb
= e
->type
->toBasetype();
113 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
115 if (isUnaArrayOp(e
->op
))
117 return isArrayOpValid(((UnaExp
*)e
)->e1
);
119 if (isBinArrayOp(e
->op
) ||
120 isBinAssignArrayOp(e
->op
) ||
123 BinExp
*be
= (BinExp
*)e
;
124 return isArrayOpValid(be
->e1
) && isArrayOpValid(be
->e2
);
126 if (e
->op
== TOKconstruct
)
128 BinExp
*be
= (BinExp
*)e
;
129 return be
->e1
->op
== TOKslice
&& isArrayOpValid(be
->e2
);
131 if (e
->op
== TOKcall
)
133 return false; // TODO: Decide if [] is required after arrayop calls.
143 bool isNonAssignmentArrayOp(Expression
*e
)
145 if (e
->op
== TOKslice
)
146 return isNonAssignmentArrayOp(((SliceExp
*)e
)->e1
);
148 Type
*tb
= e
->type
->toBasetype();
149 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
151 return (isUnaArrayOp(e
->op
) || isBinArrayOp(e
->op
));
156 bool checkNonAssignmentArrayOp(Expression
*e
, bool suggestion
)
158 if (isNonAssignmentArrayOp(e
))
162 s
= " (possible missing [])";
163 e
->error("array operation %s without destination memory not allowed%s", e
->toChars(), s
);
169 /***********************************
170 * Construct the array operation expression.
173 Expression
*arrayOp(BinExp
*e
, Scope
*sc
)
175 //printf("BinExp::arrayOp() %s\n", toChars());
177 Type
*tb
= e
->type
->toBasetype();
178 assert(tb
->ty
== Tarray
|| tb
->ty
== Tsarray
);
179 Type
*tbn
= tb
->nextOf()->toBasetype();
180 if (tbn
->ty
== Tvoid
)
182 e
->error("cannot perform array operations on void[] arrays");
183 return new ErrorExp();
185 if (!isArrayOpValid(e
))
187 e
->error("invalid array operation %s (possible missing [])", e
->toChars());
188 return new ErrorExp();
191 Expressions
*arguments
= new Expressions();
193 /* The expression to generate an array operation for is mangled
194 * into a name to use as the array operation function name.
195 * Mangle in the operands and operators in RPN order, and type.
198 buf
.writestring("_array");
199 buildArrayIdent(e
, &buf
, arguments
);
202 /* Append deco of array element type
204 buf
.writestring(e
->type
->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco
);
206 char *name
= buf
.peekString();
207 Identifier
*ident
= Identifier::idPool(name
);
209 FuncDeclaration
**pFd
= (FuncDeclaration
**)dmd_aaGet(&arrayfuncs
, (void *)ident
);
210 FuncDeclaration
*fd
= *pFd
;
213 fd
= buildArrayOp(ident
, e
, sc
);
215 if (fd
&& fd
->errors
)
218 if (tbn
->ty
== Tstruct
|| tbn
->ty
== Tclass
)
219 fmt
= "invalid array operation '%s' because %s doesn't support necessary arithmetic operations";
220 else if (!tbn
->isscalar())
221 fmt
= "invalid array operation '%s' because %s is not a scalar type";
223 fmt
= "invalid array operation '%s' for element type %s";
225 e
->error(fmt
, e
->toChars(), tbn
->toChars());
226 return new ErrorExp();
231 Expression
*ev
= new VarExp(e
->loc
, fd
);
232 Expression
*ec
= new CallExp(e
->loc
, ev
, arguments
);
234 return semantic(ec
, sc
);
237 Expression
*arrayOp(BinAssignExp
*e
, Scope
*sc
)
239 //printf("BinAssignExp::arrayOp() %s\n", toChars());
241 /* Check that the elements of e1 can be assigned to
243 Type
*tn
= e
->e1
->type
->toBasetype()->nextOf();
245 if (tn
&& (!tn
->isMutable() || !tn
->isAssignable()))
247 e
->error("slice %s is not mutable", e
->e1
->toChars());
248 return new ErrorExp();
250 if (e
->e1
->op
== TOKarrayliteral
)
252 return e
->e1
->modifiableLvalue(sc
, e
->e1
);
255 return arrayOp((BinExp
*)e
, sc
);
258 /******************************************
259 * Construct the identifier for the array operation function,
260 * and build the argument list to pass to it.
263 void buildArrayIdent(Expression
*e
, OutBuffer
*buf
, Expressions
*arguments
)
265 class BuildArrayIdentVisitor
: public Visitor
268 Expressions
*arguments
;
270 BuildArrayIdentVisitor(OutBuffer
*buf
, Expressions
*arguments
)
271 : buf(buf
), arguments(arguments
)
275 void visit(Expression
*e
)
277 buf
->writestring("Exp");
281 void visit(CastExp
*e
)
283 Type
*tb
= e
->type
->toBasetype();
284 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
289 visit((Expression
*)e
);
292 void visit(ArrayLiteralExp
*e
)
294 buf
->writestring("Slice");
298 void visit(SliceExp
*e
)
300 buf
->writestring("Slice");
304 void visit(AssignExp
*e
)
306 /* Evaluate assign expressions right to left
310 buf
->writestring("Assign");
313 void visit(BinAssignExp
*e
)
315 /* Evaluate assign expressions right to left
322 case TOKaddass
: s
= "Addass"; break;
323 case TOKminass
: s
= "Minass"; break;
324 case TOKmulass
: s
= "Mulass"; break;
325 case TOKdivass
: s
= "Divass"; break;
326 case TOKmodass
: s
= "Modass"; break;
327 case TOKxorass
: s
= "Xorass"; break;
328 case TOKandass
: s
= "Andass"; break;
329 case TOKorass
: s
= "Orass"; break;
330 case TOKpowass
: s
= "Powass"; break;
336 void visit(NegExp
*e
)
339 buf
->writestring("Neg");
342 void visit(ComExp
*e
)
345 buf
->writestring("Com");
348 void visit(BinExp
*e
)
350 /* Evaluate assign expressions left to right
352 const char *s
= NULL
;
355 case TOKadd
: s
= "Add"; break;
356 case TOKmin
: s
= "Min"; break;
357 case TOKmul
: s
= "Mul"; break;
358 case TOKdiv
: s
= "Div"; break;
359 case TOKmod
: s
= "Mod"; break;
360 case TOKxor
: s
= "Xor"; break;
361 case TOKand
: s
= "And"; break;
362 case TOKor
: s
= "Or"; break;
363 case TOKpow
: s
= "Pow"; break;
368 Type
*tb
= e
->type
->toBasetype();
369 Type
*t1
= e
->e1
->type
->toBasetype();
370 Type
*t2
= e
->e2
->type
->toBasetype();
372 if (t1
->ty
== Tarray
&&
373 ((t2
->ty
== Tarray
&& !t1
->equivalent(tb
)) ||
374 (t2
->ty
!= Tarray
&& !t1
->nextOf()->equivalent(e
->e2
->type
))))
376 // Bugzilla 12780: if A is narrower than B
379 buf
->writestring("Of");
380 buf
->writestring(t1
->nextOf()->mutableOf()->deco
);
383 if (t2
->ty
== Tarray
&&
384 ((t1
->ty
== Tarray
&& !t2
->equivalent(tb
)) ||
385 (t1
->ty
!= Tarray
&& !t2
->nextOf()->equivalent(e
->e1
->type
))))
387 // Bugzilla 12780: if B is narrower than A:
390 buf
->writestring("Of");
391 buf
->writestring(t2
->nextOf()->mutableOf()->deco
);
396 visit((Expression
*)e
);
400 BuildArrayIdentVisitor
v(buf
, arguments
);
404 /******************************************
405 * Construct the inner loop for the array operation function,
406 * and build the parameter list.
409 Expression
*buildArrayLoop(Expression
*e
, Parameters
*fparams
)
411 class BuildArrayLoopVisitor
: public Visitor
417 BuildArrayLoopVisitor(Parameters
*fparams
)
418 : fparams(fparams
), result(NULL
)
422 void visit(Expression
*e
)
424 Identifier
*id
= Identifier::generateId("c", fparams
->dim
);
425 Parameter
*param
= new Parameter(0, e
->type
, id
, NULL
);
426 fparams
->shift(param
);
427 result
= new IdentifierExp(Loc(), id
);
430 void visit(CastExp
*e
)
432 Type
*tb
= e
->type
->toBasetype();
433 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
438 visit((Expression
*)e
);
441 void visit(ArrayLiteralExp
*e
)
443 Identifier
*id
= Identifier::generateId("p", fparams
->dim
);
444 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
);
445 fparams
->shift(param
);
446 Expression
*ie
= new IdentifierExp(Loc(), id
);
447 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
448 result
= new ArrayExp(Loc(), ie
, index
);
451 void visit(SliceExp
*e
)
453 Identifier
*id
= Identifier::generateId("p", fparams
->dim
);
454 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
);
455 fparams
->shift(param
);
456 Expression
*ie
= new IdentifierExp(Loc(), id
);
457 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
458 result
= new ArrayExp(Loc(), ie
, index
);
461 void visit(AssignExp
*e
)
463 /* Evaluate assign expressions right to left
465 Expression
*ex2
= buildArrayLoop(e
->e2
);
466 /* Need the cast because:
468 * where b is a byte fails because (c + p[i]) is an int
469 * which cannot be implicitly cast to byte.
471 ex2
= new CastExp(Loc(), ex2
, e
->e1
->type
->nextOf());
472 Expression
*ex1
= buildArrayLoop(e
->e1
);
473 Parameter
*param
= (*fparams
)[0];
474 param
->storageClass
= 0;
475 result
= new AssignExp(Loc(), ex1
, ex2
);
478 void visit(BinAssignExp
*e
)
480 /* Evaluate assign expressions right to left
482 Expression
*ex2
= buildArrayLoop(e
->e2
);
483 Expression
*ex1
= buildArrayLoop(e
->e1
);
484 Parameter
*param
= (*fparams
)[0];
485 param
->storageClass
= 0;
488 case TOKaddass
: result
= new AddAssignExp(e
->loc
, ex1
, ex2
); return;
489 case TOKminass
: result
= new MinAssignExp(e
->loc
, ex1
, ex2
); return;
490 case TOKmulass
: result
= new MulAssignExp(e
->loc
, ex1
, ex2
); return;
491 case TOKdivass
: result
= new DivAssignExp(e
->loc
, ex1
, ex2
); return;
492 case TOKmodass
: result
= new ModAssignExp(e
->loc
, ex1
, ex2
); return;
493 case TOKxorass
: result
= new XorAssignExp(e
->loc
, ex1
, ex2
); return;
494 case TOKandass
: result
= new AndAssignExp(e
->loc
, ex1
, ex2
); return;
495 case TOKorass
: result
= new OrAssignExp(e
->loc
, ex1
, ex2
); return;
496 case TOKpowass
: result
= new PowAssignExp(e
->loc
, ex1
, ex2
); return;
502 void visit(NegExp
*e
)
504 Expression
*ex1
= buildArrayLoop(e
->e1
);
505 result
= new NegExp(Loc(), ex1
);
508 void visit(ComExp
*e
)
510 Expression
*ex1
= buildArrayLoop(e
->e1
);
511 result
= new ComExp(Loc(), ex1
);
514 void visit(BinExp
*e
)
516 if (isBinArrayOp(e
->op
))
518 /* Evaluate assign expressions left to right
520 BinExp
*be
= (BinExp
*)e
->copy();
521 be
->e1
= buildArrayLoop(be
->e1
);
522 be
->e2
= buildArrayLoop(be
->e2
);
529 visit((Expression
*)e
);
534 Expression
*buildArrayLoop(Expression
*e
)
541 BuildArrayLoopVisitor
v(fparams
);
542 return v
.buildArrayLoop(e
);
545 /***********************************************
546 * Test if expression is a unary array op.
549 bool isUnaArrayOp(TOK op
)
562 /***********************************************
563 * Test if expression is a binary array op.
566 bool isBinArrayOp(TOK op
)
586 /***********************************************
587 * Test if expression is a binary assignment array op.
590 bool isBinAssignArrayOp(TOK op
)
610 /***********************************************
611 * Test if operand is a valid array op operand.
614 bool isArrayOpOperand(Expression
*e
)
616 //printf("Expression::isArrayOpOperand() %s\n", e->toChars());
617 if (e
->op
== TOKslice
)
619 if (e
->op
== TOKarrayliteral
)
621 Type
*t
= e
->type
->toBasetype();
622 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
623 t
= t
->nextOf()->toBasetype();
624 return (t
->ty
!= Tvoid
);
626 Type
*tb
= e
->type
->toBasetype();
627 if (tb
->ty
== Tarray
)
629 return (isUnaArrayOp(e
->op
) ||
630 isBinArrayOp(e
->op
) ||
631 isBinAssignArrayOp(e
->op
) ||