]>
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-2021 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
);
29 /**************************************
30 * Hash table of array op functions already generated or known about.
35 /**************************************
36 * Structure to contain information needed to insert an array op call
39 FuncDeclaration
*buildArrayOp(Identifier
*ident
, BinExp
*exp
, Scope
*sc
)
41 Parameters
*fparams
= new Parameters();
42 Expression
*loopbody
= buildArrayLoop(exp
, fparams
);
44 /* Construct the function body:
45 * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
50 Parameter
*p
= (*fparams
)[0];
51 // foreach (i; 0 .. p.length)
52 Statement
*s1
= new ForeachRangeStatement(Loc(), TOKforeach
,
53 new Parameter(0, NULL
, Id::p
, NULL
, NULL
),
54 new IntegerExp(Loc(), 0, Type::tsize_t
),
55 new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p
->ident
)),
56 new ExpStatement(Loc(), loopbody
),
58 //printf("%s\n", s1->toChars());
59 Statement
*s2
= new ReturnStatement(Loc(), new IdentifierExp(Loc(), p
->ident
));
60 //printf("s2: %s\n", s2->toChars());
61 Statement
*fbody
= new CompoundStatement(Loc(), s1
, s2
);
63 // Built-in array ops should be @trusted, pure, nothrow and nogc
64 StorageClass stc
= STCtrusted
| STCpure
| STCnothrow
| STCnogc
;
66 /* Construct the function
68 TypeFunction
*ftype
= new TypeFunction(ParameterList(fparams
), exp
->e1
->type
, LINKc
, stc
);
69 //printf("fd: %s %s\n", ident->toChars(), ftype->toChars());
70 FuncDeclaration
*fd
= new FuncDeclaration(Loc(), Loc(), ident
, STCundefined
, ftype
);
72 fd
->protection
= Prot(Prot::public_
);
76 sc
->_module
->importedFrom
->members
->push(fd
);
79 sc
->parent
= sc
->_module
->importedFrom
;
82 dsymbolSemantic(fd
, sc
);
84 unsigned errors
= global
.startGagging();
86 if (global
.endGagging(errors
))
88 fd
->type
= Type::terror
;
97 /**********************************************
98 * Check that there are no uses of arrays without [].
100 bool isArrayOpValid(Expression
*e
)
102 if (e
->op
== TOKslice
)
104 if (e
->op
== TOKarrayliteral
)
106 Type
*t
= e
->type
->toBasetype();
107 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
108 t
= t
->nextOf()->toBasetype();
109 return (t
->ty
!= Tvoid
);
111 Type
*tb
= e
->type
->toBasetype();
112 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
114 if (isUnaArrayOp(e
->op
))
116 return isArrayOpValid(((UnaExp
*)e
)->e1
);
118 if (isBinArrayOp(e
->op
) ||
119 isBinAssignArrayOp(e
->op
) ||
122 BinExp
*be
= (BinExp
*)e
;
123 return isArrayOpValid(be
->e1
) && isArrayOpValid(be
->e2
);
125 if (e
->op
== TOKconstruct
)
127 BinExp
*be
= (BinExp
*)e
;
128 return be
->e1
->op
== TOKslice
&& isArrayOpValid(be
->e2
);
130 if (e
->op
== TOKcall
)
132 return false; // TODO: Decide if [] is required after arrayop calls.
142 bool isNonAssignmentArrayOp(Expression
*e
)
144 if (e
->op
== TOKslice
)
145 return isNonAssignmentArrayOp(((SliceExp
*)e
)->e1
);
147 Type
*tb
= e
->type
->toBasetype();
148 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
150 return (isUnaArrayOp(e
->op
) || isBinArrayOp(e
->op
));
155 bool checkNonAssignmentArrayOp(Expression
*e
, bool suggestion
)
157 if (isNonAssignmentArrayOp(e
))
161 s
= " (possible missing [])";
162 e
->error("array operation %s without destination memory not allowed%s", e
->toChars(), s
);
168 /***********************************
169 * Construct the array operation expression.
172 Expression
*arrayOp(BinExp
*e
, Scope
*sc
)
174 //printf("BinExp::arrayOp() %s\n", toChars());
176 Type
*tb
= e
->type
->toBasetype();
177 assert(tb
->ty
== Tarray
|| tb
->ty
== Tsarray
);
178 Type
*tbn
= tb
->nextOf()->toBasetype();
179 if (tbn
->ty
== Tvoid
)
181 e
->error("cannot perform array operations on void[] arrays");
182 return new ErrorExp();
184 if (!isArrayOpValid(e
))
186 e
->error("invalid array operation %s (possible missing [])", e
->toChars());
187 return new ErrorExp();
190 Expressions
*arguments
= new Expressions();
192 /* The expression to generate an array operation for is mangled
193 * into a name to use as the array operation function name.
194 * Mangle in the operands and operators in RPN order, and type.
197 buf
.writestring("_array");
198 buildArrayIdent(e
, &buf
, arguments
);
201 /* Append deco of array element type
203 buf
.writestring(e
->type
->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco
);
205 char *name
= buf
.peekChars();
206 Identifier
*ident
= Identifier::idPool(name
);
208 FuncDeclaration
**pFd
= (FuncDeclaration
**)dmd_aaGet(&arrayfuncs
, (void *)ident
);
209 FuncDeclaration
*fd
= *pFd
;
212 fd
= buildArrayOp(ident
, e
, sc
);
214 if (fd
&& fd
->errors
)
217 if (tbn
->ty
== Tstruct
|| tbn
->ty
== Tclass
)
218 fmt
= "invalid array operation '%s' because %s doesn't support necessary arithmetic operations";
219 else if (!tbn
->isscalar())
220 fmt
= "invalid array operation '%s' because %s is not a scalar type";
222 fmt
= "invalid array operation '%s' for element type %s";
224 e
->error(fmt
, e
->toChars(), tbn
->toChars());
225 return new ErrorExp();
230 Expression
*ev
= new VarExp(e
->loc
, fd
);
231 Expression
*ec
= new CallExp(e
->loc
, ev
, arguments
);
233 return expressionSemantic(ec
, sc
);
236 Expression
*arrayOp(BinAssignExp
*e
, Scope
*sc
)
238 //printf("BinAssignExp::arrayOp() %s\n", toChars());
240 /* Check that the elements of e1 can be assigned to
242 Type
*tn
= e
->e1
->type
->toBasetype()->nextOf();
244 if (tn
&& (!tn
->isMutable() || !tn
->isAssignable()))
246 e
->error("slice %s is not mutable", e
->e1
->toChars());
247 return new ErrorExp();
249 if (e
->e1
->op
== TOKarrayliteral
)
251 return e
->e1
->modifiableLvalue(sc
, e
->e1
);
254 return arrayOp((BinExp
*)e
, sc
);
257 /******************************************
258 * Construct the identifier for the array operation function,
259 * and build the argument list to pass to it.
262 void buildArrayIdent(Expression
*e
, OutBuffer
*buf
, Expressions
*arguments
)
264 class BuildArrayIdentVisitor
: public Visitor
267 Expressions
*arguments
;
269 BuildArrayIdentVisitor(OutBuffer
*buf
, Expressions
*arguments
)
270 : buf(buf
), arguments(arguments
)
274 void visit(Expression
*e
)
276 buf
->writestring("Exp");
280 void visit(CastExp
*e
)
282 Type
*tb
= e
->type
->toBasetype();
283 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
288 visit((Expression
*)e
);
291 void visit(ArrayLiteralExp
*e
)
293 buf
->writestring("Slice");
297 void visit(SliceExp
*e
)
299 buf
->writestring("Slice");
303 void visit(AssignExp
*e
)
305 /* Evaluate assign expressions right to left
309 buf
->writestring("Assign");
312 void visit(BinAssignExp
*e
)
314 /* Evaluate assign expressions right to left
321 case TOKaddass
: s
= "Addass"; break;
322 case TOKminass
: s
= "Minass"; break;
323 case TOKmulass
: s
= "Mulass"; break;
324 case TOKdivass
: s
= "Divass"; break;
325 case TOKmodass
: s
= "Modass"; break;
326 case TOKxorass
: s
= "Xorass"; break;
327 case TOKandass
: s
= "Andass"; break;
328 case TOKorass
: s
= "Orass"; break;
329 case TOKpowass
: s
= "Powass"; break;
335 void visit(NegExp
*e
)
338 buf
->writestring("Neg");
341 void visit(ComExp
*e
)
344 buf
->writestring("Com");
347 void visit(BinExp
*e
)
349 /* Evaluate assign expressions left to right
351 const char *s
= NULL
;
354 case TOKadd
: s
= "Add"; break;
355 case TOKmin
: s
= "Min"; break;
356 case TOKmul
: s
= "Mul"; break;
357 case TOKdiv
: s
= "Div"; break;
358 case TOKmod
: s
= "Mod"; break;
359 case TOKxor
: s
= "Xor"; break;
360 case TOKand
: s
= "And"; break;
361 case TOKor
: s
= "Or"; break;
362 case TOKpow
: s
= "Pow"; break;
367 Type
*tb
= e
->type
->toBasetype();
368 Type
*t1
= e
->e1
->type
->toBasetype();
369 Type
*t2
= e
->e2
->type
->toBasetype();
371 if (t1
->ty
== Tarray
&&
372 ((t2
->ty
== Tarray
&& !t1
->equivalent(tb
)) ||
373 (t2
->ty
!= Tarray
&& !t1
->nextOf()->equivalent(e
->e2
->type
))))
375 // Bugzilla 12780: if A is narrower than B
378 buf
->writestring("Of");
379 buf
->writestring(t1
->nextOf()->mutableOf()->deco
);
382 if (t2
->ty
== Tarray
&&
383 ((t1
->ty
== Tarray
&& !t2
->equivalent(tb
)) ||
384 (t1
->ty
!= Tarray
&& !t2
->nextOf()->equivalent(e
->e1
->type
))))
386 // Bugzilla 12780: if B is narrower than A:
389 buf
->writestring("Of");
390 buf
->writestring(t2
->nextOf()->mutableOf()->deco
);
395 visit((Expression
*)e
);
399 BuildArrayIdentVisitor
v(buf
, arguments
);
403 /******************************************
404 * Construct the inner loop for the array operation function,
405 * and build the parameter list.
408 Expression
*buildArrayLoop(Expression
*e
, Parameters
*fparams
)
410 class BuildArrayLoopVisitor
: public Visitor
416 BuildArrayLoopVisitor(Parameters
*fparams
)
417 : fparams(fparams
), result(NULL
)
421 void visit(Expression
*e
)
423 Identifier
*id
= Identifier::generateId("c", fparams
->length
);
424 Parameter
*param
= new Parameter(0, e
->type
, id
, NULL
, NULL
);
425 fparams
->shift(param
);
426 result
= new IdentifierExp(Loc(), id
);
429 void visit(CastExp
*e
)
431 Type
*tb
= e
->type
->toBasetype();
432 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
437 visit((Expression
*)e
);
440 void visit(ArrayLiteralExp
*e
)
442 Identifier
*id
= Identifier::generateId("p", fparams
->length
);
443 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
, NULL
);
444 fparams
->shift(param
);
445 Expression
*ie
= new IdentifierExp(Loc(), id
);
446 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
447 result
= new ArrayExp(Loc(), ie
, index
);
450 void visit(SliceExp
*e
)
452 Identifier
*id
= Identifier::generateId("p", fparams
->length
);
453 Parameter
*param
= new Parameter(STCconst
, e
->type
, id
, NULL
, NULL
);
454 fparams
->shift(param
);
455 Expression
*ie
= new IdentifierExp(Loc(), id
);
456 Expression
*index
= new IdentifierExp(Loc(), Id::p
);
457 result
= new ArrayExp(Loc(), ie
, index
);
460 void visit(AssignExp
*e
)
462 /* Evaluate assign expressions right to left
464 Expression
*ex2
= buildArrayLoop(e
->e2
);
465 /* Need the cast because:
467 * where b is a byte fails because (c + p[i]) is an int
468 * which cannot be implicitly cast to byte.
470 ex2
= new CastExp(Loc(), ex2
, e
->e1
->type
->nextOf());
471 Expression
*ex1
= buildArrayLoop(e
->e1
);
472 Parameter
*param
= (*fparams
)[0];
473 param
->storageClass
= 0;
474 result
= new AssignExp(Loc(), ex1
, ex2
);
477 void visit(BinAssignExp
*e
)
479 /* Evaluate assign expressions right to left
481 Expression
*ex2
= buildArrayLoop(e
->e2
);
482 Expression
*ex1
= buildArrayLoop(e
->e1
);
483 Parameter
*param
= (*fparams
)[0];
484 param
->storageClass
= 0;
487 case TOKaddass
: result
= new AddAssignExp(e
->loc
, ex1
, ex2
); return;
488 case TOKminass
: result
= new MinAssignExp(e
->loc
, ex1
, ex2
); return;
489 case TOKmulass
: result
= new MulAssignExp(e
->loc
, ex1
, ex2
); return;
490 case TOKdivass
: result
= new DivAssignExp(e
->loc
, ex1
, ex2
); return;
491 case TOKmodass
: result
= new ModAssignExp(e
->loc
, ex1
, ex2
); return;
492 case TOKxorass
: result
= new XorAssignExp(e
->loc
, ex1
, ex2
); return;
493 case TOKandass
: result
= new AndAssignExp(e
->loc
, ex1
, ex2
); return;
494 case TOKorass
: result
= new OrAssignExp(e
->loc
, ex1
, ex2
); return;
495 case TOKpowass
: result
= new PowAssignExp(e
->loc
, ex1
, ex2
); return;
501 void visit(NegExp
*e
)
503 Expression
*ex1
= buildArrayLoop(e
->e1
);
504 result
= new NegExp(Loc(), ex1
);
507 void visit(ComExp
*e
)
509 Expression
*ex1
= buildArrayLoop(e
->e1
);
510 result
= new ComExp(Loc(), ex1
);
513 void visit(BinExp
*e
)
515 if (isBinArrayOp(e
->op
))
517 /* Evaluate assign expressions left to right
519 BinExp
*be
= (BinExp
*)e
->copy();
520 be
->e1
= buildArrayLoop(be
->e1
);
521 be
->e2
= buildArrayLoop(be
->e2
);
528 visit((Expression
*)e
);
533 Expression
*buildArrayLoop(Expression
*e
)
540 BuildArrayLoopVisitor
v(fparams
);
541 return v
.buildArrayLoop(e
);
544 /***********************************************
545 * Test if expression is a unary array op.
548 bool isUnaArrayOp(TOK op
)
561 /***********************************************
562 * Test if expression is a binary array op.
565 bool isBinArrayOp(TOK op
)
585 /***********************************************
586 * Test if expression is a binary assignment array op.
589 bool isBinAssignArrayOp(TOK op
)
609 /***********************************************
610 * Test if operand is a valid array op operand.
613 bool isArrayOpOperand(Expression
*e
)
615 //printf("Expression::isArrayOpOperand() %s\n", e->toChars());
616 if (e
->op
== TOKslice
)
618 if (e
->op
== TOKarrayliteral
)
620 Type
*t
= e
->type
->toBasetype();
621 while (t
->ty
== Tarray
|| t
->ty
== Tsarray
)
622 t
= t
->nextOf()->toBasetype();
623 return (t
->ty
!= Tvoid
);
625 Type
*tb
= e
->type
->toBasetype();
626 if (tb
->ty
== Tarray
)
628 return (isUnaArrayOp(e
->op
) ||
629 isBinArrayOp(e
->op
) ||
630 isBinAssignArrayOp(e
->op
) ||