mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-15 04:13:14 +01:00
3443f38 Fix issue 7493 Initialization of void[][N]
0b371da foreach can run semantic again
7216e2a fix Issue 7735 - Functions with variadic void[][]... arguments corrupt passed data
4fb2b2a Merge pull request #850 from 9rnsr/fix7773
9c59931 Merge pull request #851 from donc/ctfe7785pointerToVar
407f7e4 Merge pull request #852 from donc/segfault7639
9370f83 Fix issue 7380 Crash trying to use address of variable in struct constructor at module level
240866b Fix issue 7639 Undefined enum AA key crashes compiler
19b7096 Fix issue 7785 [CTFE] ICE when slicing pointer to variable
d9b11f6 fix Issue 7773 - UCFS syntax on built-in attributes too?
296d812 Merge pull request #846 from donc/ctfe7781segfault
65aca2d Merge pull request #848 from donc/regression7751
5576737 Merge pull request #849 from donc/bug7794
0310838 Merge pull request #828 from 9rnsr/fix7751
4027e4f Fix issue 7794 Sea of errors when calling regex() after compile error
59cc12d Fix issue 7781 [CTFE] Segmentation fault on 'mixin({return;}());'
3430947 fix seg fault in fail91.d
948274e Merge pull request #824 from donc/regression7745
22ac4b1 Merge pull request #826 from 9rnsr/fix6659
1c15841 Merge pull request #823 from redstar/mscclean
5f54752 Merge pull request #827 from 9rnsr/fix7694
399e4a3 Merge pull request #844 from donc/regression7782
516f49b Fix issue 7789 [CTFE] null pointer exception on setting array length
d74b354 Fix issue 7782 Regression: ICE with wrong import syntax
0269194 Fix issue 7751 [ICE] (Regression 2.059head) From auto and forward reference
42ad236 Merge pull request #830 from 9rnsr/fix_ufcs
67bf025 Merge pull request #832 from 9rnsr/fix7608
d13f107 Merge pull request #829 from 9rnsr/fix7754
e25cbe2 Merge pull request #834 from 9rnsr/fix2367
7fac235 merge D2 pull #842
c836773 Merge pull request #836 from 9rnsr/fix7757
a2754c5 Merge pull request #839 from 9rnsr/fix7768
4948836 fix Issue 7694 - Internal error: e2ir.c 1251 when calling member function inside struct via alias param
9f23335 Merge pull request #838 from 9rnsr/fix7621
92eba60 Merge pull request #840 from 9rnsr/fix7769
8fae3c2 fix issue 7742 - 'More initializers than fields' error with correct number of fields
6c2d706 to enum
35e4f08 fix Issue 7769 - relax inout rule doesn't work for template function
96a0105 fix Issue 7768 - More readable template error messages
8012d58 Merge pull request #831 from 9rnsr/fix7743
9c0cbdd fix Issue 7621 - Immutable type equivalence problem
f67f313 Merge pull request #833 from 9rnsr/fix7731
29754dd Merge pull request #837 from braddr/cleanup-backend2
374109a restore original binary() function and re-fix the new version
78c04aa fix Issue 7757 - Inout function with lazy inout parameter doesn't compile
50c34e9 fix Issue 7754 - static this() in template is stripped during header gen
11acdff Fix auto tester breaking.
f0b7157 fix Issue 7755 - regression(2.059head): ICE in glue.c
cfceb77 fix Issue 7751 - [ICE] From auto and forward reference
7a86807 fix Issue 2367 - Overloading error with string literals
6039c40 fix Issue 7731 - Assertion failure: 't' on line 7911 in file 'mtype.c'
aea3a39 fix Issue 7608 - __traits(allMembers) is broken
f46f07a fix Issue 7743 - Parsing problem with nothrow delegate
fa9d29f Revert "Revert "Refactor for UFCS property getter/setter resolution.""
d9698d8 Revert "Revert "fix Issue 7722 - Refuse normal functions to be used as properties""
0fbc772 Revert "Revert "Allow property function has two arguments""
07a3b09 fix Issue 6659 - Destructor in range foreach called after initialization
e499d4d Fix issue 7745 Regression(2.059beta) Methods defined in external object files when a pointer to it is taken
79a74e1 Fixes an unknown pragma warning.
2b12052 Fix issue 176 [module] message "module and package have the same name"
90e89a4 Merge pull request #814 from 9rnsr/fix7713
3ab0e79 Merge pull request #818 from donc/assoc7732
b3360e9 Fix issue 7732 [CTFE] wrong code for a struct called AssociativeArray
05f0b08 Merge pull request #779 from 9rnsr/fix7534
867e567 Revert "Allow property function has two arguments"
9171aeb Revert "fix Issue 7722 - Refuse normal functions to be used as properties"
989ced7 Revert "Refactor for UFCS property getter/setter resolution."
e9b5292 Refactor for UFCS property getter/setter resolution.
761d000 fix Issue 7722 - Refuse normal functions to be used as properties
9f5956b Allow property function has two arguments
1a11862 Revert "Allow property function has two arguments"
32f57e5 Revert "fix Issue 7722 - Refuse normal functions to be used as properties"
6489bb4 Revert "Refactor for UFCS property getter/setter resolution."
214296f Merge pull request #817 from 9rnsr/fix_ufcs
c3c7f2a Merge pull request #816 from donc/voidctfe6438
185d031 Refactor for UFCS property getter/setter resolution.
08bf89d fix Issue 7722 - Refuse normal functions to be used as properties
f0e3433 Allow property function has two arguments
1b67ac9 Direct check by Type::reliesOnTident
a3cd7d9 fix Issue 7713 - lambda inference doesn't work on template function argument
1762112 Fix issue 6438 - [CTFE] wrong error "value used before set" when slicing =void array
ace1eca fix complex constant folding
76f9b22 Consider return type covariance.
f700dbc fix Issue 7534 - Allow attribute-overloading of an overridden method
cba8f5c Merge pull request #763 from 9rnsr/fix7578
392d93f Merge pull request #815 from dawgfoto/fixSegFault
e48aba2 merge part of pull #769
d72a17e revert dd5a543
24d860b error(Loc loc,) doesn't abort program
4c79117 Use correct opcodes for moving cfloat from st->xmm and xmm->st
af875ff Merge pull request #785 from braddr/cleanup-backend2
9d3021a remove debugging printfs
b3df5ee Merge pull request #807 from dawgfoto/fix7698
f005537 Merge pull request #802 from dawgfoto/fixVC
65a145d Merge pull request #803 from donc/ctfeunion6681yebblies
1cf39ca Merge pull request #812 from 9rnsr/fix_ufcs
d846c3c Merge pull request #808 from 9rnsr/fix7702
fd0a492 fix Issue 7670 - UFCS problem with @property and structs
1ad35b2 Fix for UFCS with property syntax, and add exhaustive test
96f15a1 Resolve broken build after merging
4712aab fix regression
4e05482 Merge pull request #805 from donc/regression7681
245a107 dt_ functions aren't x86 specific
b35f43a another missing loc in an error() call
001addb minor cleanups
2fb1e46 make util_assert take a const string
907da39 cleanup whitespace in binary(), add binary() that takes the length of the string to search for
59d0425 Merge pull request #804 from braddr/nearsighted
d725eed Merge pull request #806 from donc/ctfe7633equalmsg
12a5c26 Merge pull request #811 from donc/bug7699
4279d5e revert the revert
c895c3b revert pull #809
865fb20 fix Issue 5733 - Calling opDispatch As Template Results in Compiler Infinite Loop
96e16d3 fix Issue 7702 - opDispatch goes into infinite loop
5e343c0 Remove special case for DotIdExp and opDispatch semantic, it isn't need anymore
1a9d607 Fix issue 7699 - Cannot get frame pointer to in contract when compiling with -inline
d1476eb Merge pull request #809 from 9rnsr/fix_funclit
afc7c60 allow out-of-order semantic analysis of fields
17da3a0 fix Issue 7705 - lambda syntax doesn't allow some valid signatures
e29d06d fix issue 7698
911d053 Fix issue 7633 - Missing CTFE error message
3802dde Fix issue 7681 Regression(2.059head):ICE:opCatAssign(delegate) to undefined identifier
8da4121 near-ectomy
cd6dc83 fix Library::error()s format string to take a const char*
f3f03c6 switch to apply()
faf873a fix Issue 3510 - Cannot forward reference a templated type from within a template mixin
23aa2be fix Issue 3509 - Cannot forward reference a template mixin's members in a compile-time context
e81309b Add missing 'loc' to error message.
b6898e3 Fix issue 6681 - struct constructor call is converted to struct literal that breaks union initialization
b79afba long double => longdouble
e48c319 Merge pull request #742 from yebblies/issue5879
d74485a Merge pull request #787 from eco/ddoc-srcfilename
3038cb9 Merge pull request #795 from dawgfoto/fixComment
89a039a Merge pull request #801 from dawgfoto/fix4507
c17c2d8 fix issue 4507
dd86c72 Merge pull request #796 from dawgfoto/fixVC
a516588 Merge pull request #797 from 9rnsr/fix7682
1b9839a Merge pull request #799 from 9rnsr/fix6982
4596774 Merge pull request #800 from 9rnsr/fix_type_deduction
b68d546 forgot about @system
bfe1083 add attributes to toHash
8f819d6 Stop special case in mutableOf/makeMutable with inout type.
319b1a3 Fix the lacks of type merging in Type::mutableOf() and uhSharedOf()
cfe7450 fix Issue 7671 - Broken inout deduction of shared(inout(T[n])) from immutable(int[3])
aca5c37 Stop too eager call of TypeAArray::getImpl() When implicitConvTo(non aa Tstruct => Taarray)
50b2a97 fix Issue 6982 - immutability isn't respected on associative array assignment
a5daa5e fix Issue 7684 - IFTI and shared overload doesn't work
e43fbac fix Issue 7682 - shared array type and "cast() is not an lvalue" error
8191801 cpp_prettyident only needed for C++
4487f75 fix ldval
525647c tparam is the specialization
f893925 fix issue 7592 d847c1c2dd
108b25d Merge pull request #780 from 9rnsr/fix7641
105a51f Merge pull request #784 from 9rnsr/fix7110
8b5b67f Merge pull request #792 from donc/bug7667
f72f237 fix Issue 3682 - Regression(2.038) is expression fails to match types
436b711 Fix issue 7667. ICE(interpret.c): 'ctfeStack.stackPointer() == 0'
9005276 Merge pull request #679 from yebblies/issue783
350a3ce Merge pull request #582 from 9rnsr/fix3382_ufcs
5f020c3 Merge pull request #788 from braddr/cleanup-backend3
6aa91cf Merge pull request #790 from p0nce/master
351d595 remove tls bracketing
a137d72 Fix bug #6391
6ce219c remove some of the bracketing
aec4c13 fix Issue 7578 - ICE on indexing result of vararg opDispatch
95e3dc1 Fix unintended infinite loop in Phobos build
b66196a fix Issue 3382 - [tdpl] Implement uniform function call syntax
ee2fe6c Fix 977 is with counting end-of-lines towards msot advanced lexer peeking
7790b16 fix Issue 7650 - Bad lambda inference in associative array literal
c03484e fix Issue 7649 - Bad lambda inference in default function argument
f293a10 fix Issue 7499 - [ICE] ('cast.c line 1495) with lambda array
9f0622c Expression::inferType() and remove FuncExp::setType()
cfc67b7 refactor lambda inference process
6d49586 more de-TX86'ing in relation to a bunch of OP codes
2efbf6a TX86-ectomy in evalu8.c
953f6d7 rip TX86 conditionals out of el.c
d5663c7 fix Issue 7595 - Data being overwritten.
449c165 Add predefined Ddoc macro SRCFILENAME
5c5da66 fix uninitialized field
29cde54 Merge pull request #783 from 9rnsr/fix7038
06d65ab fix Issue 7038 - Type mismatch with const struct
b77e2c9 fix Issue 7110 - opSlice() & opIndex functions works unstable as template arguments
a65f02f Merge pull request #781 from braddr/fix
08d6cd5 Merge pull request #782 from braddr/fixiasm
2492332 fix latent bug with Lexer::peek and recently introduced bug in Lexer::scan
ec1888e initialize popndTmp rather than rely on carefulness when usNumops == 0 and emitting a vector instruction, popndTmp is left uninitialized and is later dereferenced.
1d4a742 Merge pull request #766 from 9rnsr/fix7563
e1cd535 refactor
90f8dcf fix Issue 7641 - std.typecons.Proxy incorrectly allows implicit conversion to class
83a93cf Merge pull request #778 from dawgfoto/MoreSpellCorrection
7f0bcde 2nd go at fix issue 5590
567d7df fix Issue 5590 - Regression(2.036) ICE(e2ir.c): when using .values on enum which is associative array
48ea951 fix Issue 4820 - Regression(1.058, 2.044) in DStress caused by changeset 452
e8f9f3b more spell correction
afd9a45 fix Issue 7618 - delegate/function pointer call bypass parameter storage class
dabcdfb Merge pull request #773 from 9rnsr/fix7583
9846bb2 Merge pull request #774 from donc/ctfe7568
8c20445 Merge pull request #775 from donc/_error6785
d41e58e Avoiding shallow copy is more better.
cccef09 Revert "fix Issue 7585 - functions in templates inferred as delegate"
fc8dfc0 6785 Wrong error message from pragma(msg) of failed instantiation
61ec04d 7568 pragma(msg) segfaults with an aggregate including a class.
4d86d39 Merge pull request #767 from 9rnsr/fix7585
207d351 fix Issue 7583 - [CTFE] ICE with tuple and alias this
53bafa2 fix Issue 7411 - Deduce base type from vector types in templates
5ab1bd9 fix Issue 7518 - std.array.empty doesn't work for shared arrays
a1030d3 fix Issue 7554 - Immutable function pointer arguments too
5e96900 Merge pull request #771 from donc/bug7589
2287ebc fix Issue 7547 - -deps output lists object as a top level module
e611781 7589 __traits(compiles) does not work with a template that fails to compile
0113cde fix Issue 7585 - functions in templates inferred as delegate
4b978d5 fix Issue 7563 - Class members with default template arguments have no type
4d68981 fix Issue 7500 - [ICE] (template.c line 5287) with immutable lambda function
1a39c3c missed a line
6dd89ca Merge pull request #765 from 9rnsr/fix7525
8d6dcac fix Issue 7502 - 2.056 regression: Assigning .init takes forever to compile for large structs
042096e fix Issue 7525 - Broken return type inference for delegate returns
c5affa5 fix Issue 7582 - Untyped nested delegate literals don't compile
121677c fix Issue 7580 - Identity assignment of Nullable crashes dmd
adc0502 Small refactoring to resolve alias this.
1f52383 Merge pull request #671 from yebblies/issue4958
2a12345 fix build breakage
8755819 fix build
ba86204 fix vcbuild
464c664 fix linux build
31197c8 tweaked command line moved some inline asm to C-function to not interfere with optimizations build with VS2011
4dcdc9c increase stack size for win64 build
77262aa add missing include to root
56afe3f batch to build through win32.mak
5a0fd30 build through win32.mak
a5b5190 long_double -> longdouble remove C99 printf add Win64 support
9640110 vcbuild
b619171 Merge pull request #761 from donc/ctfe7473structref
7756328 Merge pull request #725 from kennytm/bug7399-import-too-fatal
bbac9e4 Merge pull request #759 from yebblies/issue1149
d1ff23b 7473 [CTFE] Non-ref argument behaves as if it's a ref argument
ab5cb18 Fix OPmsw codegen - integer only is too restrictive.
a00833b Merge pull request #743 from yebblies/issue3354
b006e11 Merge pull request #757 from 9rnsr/fix7562
3bccbb0 fix Issue 7562 - DMD crashes by using TemplateThisParameter
a7dc50e Merge pull request #749 from yebblies/issue1149
a873c5f Merge pull request #758 from 9rnsr/fix5525
5d639ec fix Issue 5525 - Eponymous templates should allow for overloaded eponymous members
f50852c Merge pull request #729 from donc/gag4269
de02523 fix Issue 3927 - array.length++; is an error, but ++array.length compiles
1dc5bfd Merge pull request #680 from yebblies/issue3812
cf887ba move errors to Dsymbol
fc4acf5 Merge pull request #755 from donc/seaOfErrors7557
be2f3a9 7557b soldier on through dottemplate expressions
8cec825 7557 Sea of errors after template failure
37ec6d6 A small fixup to call Type::defaultInitLiteral
7b5e2cb Revert "Revert "Merge pull request #41 from 9rnsr/rvalue-struct-literal""
3d8f09a Merge branch 'master' of github.com:D-Programming-Language/dmd
7dfb4cc Merge pull request #752 from braddr/cleanup-backend2
1b28f51 Merge branch 'master' of github.com:D-Programming-Language/dmd
31ad73c Merge pull request #746 from yebblies/issue5554
25f770d Change lexer to support # as a token, preserving #line's original behavior
dd8d20a Revert "Merge pull request #41 from 9rnsr/rvalue-struct-literal"
ee2fdf9 Merge pull request #41 from 9rnsr/rvalue-struct-literal
f94fdbf Merge pull request #750 from yebblies/issue3630
61f5fcf Improve codegen for OPmsw
05a3fa4 Merge pull request #744 from Safety0ff/avx-fix
0231d6a Merge pull request #748 from 9rnsr/fix7552
9a97979 Merge pull request #751 from donc/ctfe7536
e091e6e 7536 ctfeAdrOnStack triggered
c9edaf4 fix Issue 7552 - Cannot get and combine a part of overloaded functions
1edeba9 Fix Issue 3630 - bad error location in "has no effect in expression" error
7d0fb72 Fix Issue 5554 - [qtd] Covariance detection failure
4f36aca fix Issue 7550 - Missing AVX instruction VPMULDQ
0b82dfe Fix Issue 5879 - Not all frontend errors use stderr
963a41a Merge pull request #695 from yebblies/refactor_expression
3f06690 Fix Issue 3354 - asm fld x, ST(6); accepted
713f69f Merge pull request #677 from yebblies/issue4241
cf22ce3 Merge pull request #711 from yebblies/issue3559
56ca73c Merge pull request #700 from kennytm/bug7452_lazy_safe
c4dc723 Merge pull request #736 from ibuclaw/in_gcc
121c9b9 Merge pull request #737 from yebblies/issue7544
cedcb3c Merge pull request #740 from yebblies/issue7545
fb3e8f2 Merge pull request #741 from dawgfoto/DMCWarning
5d26c1e Merge pull request #735 from 9rnsr/fix7105
734a921 dmc warning
1e1cfbc Fix Issue 7545 - ICE(cast.c) Merge integral types through alias this
6b135be Fix Issue 7544 - ICE(interpret.c) Catching an exception with a null catch block
c5336f9 Update already existing gdc-specific code, harmonise headers.
44b8d59 Merge pull request #703 from kennytm/bug435_template_ctor
6b368e1 Merge pull request #707 from yebblies/issue3822
8439e07 Merge pull request #717 from yebblies/issue6611
2b4502e fix Issue 7105 - relax inout rules
ac4463a wildsubparam isn't need anymore, because it works properly.
f77879a Issue 6611 - better error message for array post increment/decrement
7393395 Merge pull request #716 from yebblies/issue6685
77568f0 Merge pull request #719 from yebblies/issue4536
9accb04 tired of tdata()
5fbd5a2 Merge pull request #732 from dawgfoto/fix5412
41a901a Revert "hide private/package module level symbols"
23d5e14 Merge pull request #733 from dawgfoto/HideModuleMembers
e2f8a23 hide private/package module level symbols
ae75287 detect collisions with renamed imports
75a2442 fix Dsymbol::search_correct
50e122a Merge pull request #723 from kennytm/bug7504_null_array
c5b7601 Revert "fix 7494 - selective imports in function scope"
aa6f4d9 Revert "fix Protection"
5be660e Revert "fix Imports"
040371b Revert "detect collisions with renamed imports"
0159818 Revert "find private symbols during spell correction"
0c95c45 find private symbols during spell correction
ca22fb2 detect collisions with renamed imports
0dca0af fix Imports
37d4fda fix Protection
16a2e7e fix 7494 - selective imports in function scope
c16f5b2 Merge pull request #667 from 9rnsr/fix7406
f776617 explanatory comments belong in the code, not bugzilla
bfa2060 Merge pull request #704 from donc/_error6699
f46705c fix fail222 regression
28d9635 Merge pull request #708 from donc/soldieron7481
2c2a7af Merge pull request #715 from 9rnsr/fix6738
98cfa64 Merge pull request #722 from 9rnsr/fix7353
b040567 revert pull 724
0e84f63 revert part of pull 724
400f702 Merge pull request #724 from yebblies/issue3632
d82cc74 Merge pull request #720 from yebblies/issue3279
2da3bed Merge pull request #718 from yebblies/fixdebugmsg
f6627ec 7527 [CTFE] Segfault when slicing a pointer at compile time
c8f09bf 4269a Regression(2.031): invalid type accepted if evaluated while errors are gagged
d10fba0 implement const/purity/nothrow/@safe inheritance
ad689fb Fix bug 7399: Broken import statement in trySemantic() causes silent compiler error
eb0c643 Add global.speculativeGag
c18220a Refactor isSpeculativeFunction into Dsymbol
f5c56d8 Issue 3632 - modify float is float to do a bitwise compare
af1cab4 Issue 7353 - NRVO not properly working with inferred return type
03ee438 Fix bug 7504: Cannot assign an object of type 'typeof(null)' to an array
dfb941c Remove debug printing in code that generates errors.
62118e3 Issue 4536 - Typetuples (T...) should have an .init member
989da7b Issue 3279 - Confusing error message when comparing types
36e8045 Issue 6685 - Allow using "with" with rvalues
60cbc6f fix issue 6738 revisited
4e20e7d Issue 3822 - Invalid optimization of alloca called with constant size
b37bf8c Fixes bug 435: Constructors should be templatized
ad8157d Issue 3559 - DMD 1.048+ fails to take function pointer from overloaded member functions
838cd06 7481 Compiler should 'soldier on' after template errors
673063e Simplify fix for 6699
1a0b199 6699a __error when instantiating function template
b6d072d 6699b __error in alias expression
df16ffa 6699c __error in synchronized error message
338f804 7462 Error message with _error_ in overridden function
0f60bd3 7463 Duplicated error message with bad template value parameter
f43e93a 6699E: _error inside error msg for bad base class
5109a5a Fixes bug 7452.
04d888f Refactor XxxAssignExp semantic
73973d6 Issue 3812 - Missing line number for implicit cast of variadic function to array
f0bbf18 Issue 3927 - array.length++; is an error, but ++array.length compiles
24576c2 Issue 783 - Cannot use an array w/ const or variable index as new[] size argument.
7e4cd4b Issue 4241 - duplicate union initialization error doesn't give a file location
9987127 Issue 4958 - Floating point enums should check for total loss of precision
60287fd Issue 7406 - tuple foreach doesn't work with mixed tuples
633d88e Issue 5889 - Struct literal/construction should be rvalue
5d5f78a Now function overloading with ref and non-ref parameter is legal for struct type
git-subtree-dir: dmd2
git-subtree-split: 3443f38fc4c17807a0f36005a05d598cfc7301db
12197 lines
331 KiB
C
12197 lines
331 KiB
C
|
|
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2012 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gnu.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#if _MSC_VER
|
|
#include <complex>
|
|
#else
|
|
#include <complex.h>
|
|
#endif
|
|
|
|
#if _WIN32 && __DMC__
|
|
extern "C" char * __cdecl __locale_decpoint;
|
|
#endif
|
|
|
|
#include "rmem.h"
|
|
#include "port.h"
|
|
#include "root.h"
|
|
|
|
#include "mtype.h"
|
|
#include "init.h"
|
|
#include "expression.h"
|
|
#include "template.h"
|
|
#include "utf.h"
|
|
#include "enum.h"
|
|
#include "scope.h"
|
|
#include "statement.h"
|
|
#include "declaration.h"
|
|
#include "aggregate.h"
|
|
#include "import.h"
|
|
#include "id.h"
|
|
#include "dsymbol.h"
|
|
#include "module.h"
|
|
#include "attrib.h"
|
|
#include "hdrgen.h"
|
|
#include "parse.h"
|
|
#include "doc.h"
|
|
|
|
|
|
Expression *createTypeInfoArray(Scope *sc, Expression *args[], unsigned dim);
|
|
Expression *expandVar(int result, VarDeclaration *v);
|
|
|
|
#define LOGSEMANTIC 0
|
|
|
|
/*************************************************************
|
|
* Given var, we need to get the
|
|
* right 'this' pointer if var is in an outer class, but our
|
|
* existing 'this' pointer is in an inner class.
|
|
* Input:
|
|
* e1 existing 'this'
|
|
* ad struct or class we need the correct 'this' for
|
|
* var the specific member of ad we're accessing
|
|
*/
|
|
|
|
Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad,
|
|
Expression *e1, Declaration *var)
|
|
{
|
|
//printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars());
|
|
L1:
|
|
Type *t = e1->type->toBasetype();
|
|
//printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars());
|
|
|
|
/* If e1 is not the 'this' pointer for ad
|
|
*/
|
|
if (ad &&
|
|
!(t->ty == Tpointer && t->nextOf()->ty == Tstruct &&
|
|
((TypeStruct *)t->nextOf())->sym == ad)
|
|
&&
|
|
!(t->ty == Tstruct &&
|
|
((TypeStruct *)t)->sym == ad)
|
|
)
|
|
{
|
|
ClassDeclaration *cd = ad->isClassDeclaration();
|
|
ClassDeclaration *tcd = t->isClassHandle();
|
|
|
|
/* e1 is the right this if ad is a base class of e1
|
|
*/
|
|
if (!cd || !tcd ||
|
|
!(tcd == cd || cd->isBaseOf(tcd, NULL))
|
|
)
|
|
{
|
|
/* Only classes can be inner classes with an 'outer'
|
|
* member pointing to the enclosing class instance
|
|
*/
|
|
if (tcd && tcd->isNested())
|
|
{ /* e1 is the 'this' pointer for an inner class: tcd.
|
|
* Rewrite it as the 'this' pointer for the outer class.
|
|
*/
|
|
|
|
e1 = new DotVarExp(loc, e1, tcd->vthis);
|
|
e1->type = tcd->vthis->type;
|
|
// Do not call checkNestedRef()
|
|
//e1 = e1->semantic(sc);
|
|
|
|
// Skip up over nested functions, and get the enclosing
|
|
// class type.
|
|
int n = 0;
|
|
Dsymbol *s;
|
|
for (s = tcd->toParent();
|
|
s && s->isFuncDeclaration();
|
|
s = s->toParent())
|
|
{ FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f->vthis)
|
|
{
|
|
//printf("rewriting e1 to %s's this\n", f->toChars());
|
|
n++;
|
|
e1 = new VarExp(loc, f->vthis);
|
|
}
|
|
else
|
|
{
|
|
e1->error("need 'this' of type %s to access member %s"
|
|
" from static function %s",
|
|
ad->toChars(), var->toChars(), f->toChars());
|
|
e1 = new ErrorExp();
|
|
return e1;
|
|
}
|
|
}
|
|
if (s && s->isClassDeclaration())
|
|
{ e1->type = s->isClassDeclaration()->type;
|
|
if (n > 1)
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
else
|
|
e1 = e1->semantic(sc);
|
|
goto L1;
|
|
}
|
|
/* Can't find a path from e1 to ad
|
|
*/
|
|
e1->error("this for %s needs to be type %s not type %s",
|
|
var->toChars(), ad->toChars(), t->toChars());
|
|
e1 = new ErrorExp();
|
|
}
|
|
}
|
|
return e1;
|
|
}
|
|
|
|
/*****************************************
|
|
* Determine if 'this' is available.
|
|
* If it is, return the FuncDeclaration that has it.
|
|
*/
|
|
|
|
FuncDeclaration *hasThis(Scope *sc)
|
|
{ FuncDeclaration *fd;
|
|
FuncDeclaration *fdthis;
|
|
|
|
//printf("hasThis()\n");
|
|
fdthis = sc->parent->isFuncDeclaration();
|
|
//printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : "");
|
|
|
|
/* Special case for inside template constraint
|
|
*/
|
|
if (fdthis && (sc->flags & SCOPEstaticif) && fdthis->parent->isTemplateDeclaration())
|
|
{
|
|
//TemplateDeclaration *td = fdthis->parent->isTemplateDeclaration();
|
|
//printf("[%s] td = %s, fdthis->vthis = %p\n", td->loc.toChars(), td->toChars(), fdthis->vthis);
|
|
return fdthis->vthis ? fdthis : NULL;
|
|
}
|
|
|
|
// Go upwards until we find the enclosing member function
|
|
fd = fdthis;
|
|
while (1)
|
|
{
|
|
if (!fd)
|
|
{
|
|
goto Lno;
|
|
}
|
|
if (!fd->isNested())
|
|
break;
|
|
|
|
Dsymbol *parent = fd->parent;
|
|
while (1)
|
|
{
|
|
if (!parent)
|
|
goto Lno;
|
|
TemplateInstance *ti = parent->isTemplateInstance();
|
|
if (ti)
|
|
parent = ti->parent;
|
|
else
|
|
break;
|
|
}
|
|
fd = parent->isFuncDeclaration();
|
|
}
|
|
|
|
if (!fd->isThis())
|
|
{ //printf("test '%s'\n", fd->toChars());
|
|
goto Lno;
|
|
}
|
|
|
|
assert(fd->vthis);
|
|
return fd;
|
|
|
|
Lno:
|
|
return NULL; // don't have 'this' available
|
|
}
|
|
|
|
|
|
/***************************************
|
|
* Pull out any properties.
|
|
*/
|
|
|
|
Expression *resolveProperties(Scope *sc, Expression *e)
|
|
{
|
|
//printf("resolveProperties(%s)\n", e->toChars());
|
|
|
|
TemplateDeclaration *td;
|
|
Objects *targsi;
|
|
Expression *ethis;
|
|
if (e->op == TOKdotti)
|
|
{
|
|
DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e;
|
|
td = dti->getTempdecl(sc);
|
|
dti->ti->semanticTiargs(sc);
|
|
targsi = dti->ti->tiargs;
|
|
ethis = dti->e1;
|
|
goto L1;
|
|
}
|
|
else if (e->op == TOKdottd)
|
|
{
|
|
DotTemplateExp *dte = (DotTemplateExp *)e;
|
|
td = dte->td;
|
|
targsi = NULL;
|
|
ethis = dte->e1;
|
|
goto L1;
|
|
}
|
|
else if (e->op == TOKtemplate)
|
|
{
|
|
td = ((TemplateExp *)e)->td;
|
|
targsi = NULL;
|
|
ethis = NULL;
|
|
L1:
|
|
assert(td);
|
|
unsigned errors = global.startGagging();
|
|
FuncDeclaration *fd = td->deduceFunctionTemplate(sc, e->loc, targsi, ethis, NULL, 1);
|
|
if (global.endGagging(errors))
|
|
fd = NULL; // eat "is not a function template" error
|
|
if (fd && fd->type)
|
|
{ assert(fd->type->ty == Tfunction);
|
|
TypeFunction *tf = (TypeFunction *)fd->type;
|
|
if (!tf->isproperty && global.params.enforcePropertySyntax)
|
|
{ error(e->loc, "not a property %s", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
e = new CallExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
}
|
|
goto return_expr;
|
|
}
|
|
|
|
if (e->type)
|
|
{
|
|
Type *t = e->type->toBasetype();
|
|
|
|
if (t->ty == Tfunction || e->op == TOKoverloadset)
|
|
{
|
|
if (t->ty == Tfunction && !((TypeFunction *)t)->isproperty &&
|
|
global.params.enforcePropertySyntax)
|
|
{
|
|
error(e->loc, "not a property %s", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
e = new CallExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
}
|
|
|
|
/* Look for e being a lazy parameter; rewrite as delegate call
|
|
*/
|
|
else if (e->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)e;
|
|
|
|
if (ve->var->storage_class & STClazy)
|
|
{
|
|
e = new CallExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
}
|
|
}
|
|
|
|
else if (e->op == TOKdotexp)
|
|
{
|
|
e->error("expression has no value");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
}
|
|
|
|
return_expr:
|
|
if (!e->type)
|
|
{
|
|
error(e->loc, "cannot resolve type for %s", e->toChars());
|
|
e->type = new TypeError();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/******************************
|
|
* Check the tail CallExp is really property function call.
|
|
*/
|
|
|
|
void checkPropertyCall(Expression *e, Expression *emsg)
|
|
{
|
|
while (e->op == TOKcomma)
|
|
e = ((CommaExp *)e)->e2;
|
|
|
|
if (e->op == TOKcall)
|
|
{ CallExp *ce = (CallExp *)e;
|
|
TypeFunction *tf;
|
|
if (ce->f)
|
|
tf = (TypeFunction *)ce->f->type;
|
|
else if (ce->e1->type->ty == Tfunction)
|
|
tf = (TypeFunction *)ce->e1->type;
|
|
else if (ce->e1->type->ty == Tdelegate)
|
|
tf = (TypeFunction *)ce->e1->type->nextOf();
|
|
else if (ce->e1->type->ty == Tpointer && ce->e1->type->nextOf()->ty == Tfunction)
|
|
tf = (TypeFunction *)ce->e1->type->nextOf();
|
|
else
|
|
assert(0);
|
|
|
|
if (!tf->isproperty && global.params.enforcePropertySyntax)
|
|
ce->e1->error("not a property %s", emsg->toChars());
|
|
}
|
|
}
|
|
|
|
/******************************
|
|
* Pull out property with UFCS.
|
|
*/
|
|
|
|
Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL)
|
|
{
|
|
Expression *e = NULL;
|
|
Expression *eleft;
|
|
Identifier *ident;
|
|
Objects* tiargs;
|
|
Loc loc = e1->loc;
|
|
|
|
if (e1->op == TOKdot)
|
|
{
|
|
DotIdExp *die = (DotIdExp *)e1;
|
|
eleft = die->e1;
|
|
ident = die->ident;
|
|
tiargs = NULL;
|
|
goto L1;
|
|
}
|
|
else if (e1->op == TOKdotti)
|
|
{
|
|
DotTemplateInstanceExp *dti;
|
|
dti = (DotTemplateInstanceExp *)e1;
|
|
eleft = dti->e1;
|
|
ident = dti->ti->name;
|
|
tiargs = dti->ti->tiargs;
|
|
L1:
|
|
/* .ident
|
|
* .ident!tiargs
|
|
*/
|
|
e = new IdentifierExp(loc, Id::empty);
|
|
if (tiargs)
|
|
e = new DotTemplateInstanceExp(loc, e, ident, tiargs);
|
|
else
|
|
e = new DotIdExp(loc, e, ident);
|
|
|
|
Expressions *arguments = new Expressions();
|
|
/* .f(e1, e2)
|
|
*/
|
|
if (e2)
|
|
{
|
|
arguments->setDim(2);
|
|
(*arguments)[0] = eleft;
|
|
(*arguments)[1] = e2;
|
|
|
|
Expression *ex = e->syntaxCopy();
|
|
e = new CallExp(loc, e, arguments);
|
|
e = e->trySemantic(sc);
|
|
if (e)
|
|
{ checkPropertyCall(e, e1);
|
|
return e->semantic(sc);
|
|
}
|
|
e = ex;
|
|
}
|
|
|
|
/* .f(e1)
|
|
* .f(e1) = e2
|
|
*/
|
|
{
|
|
arguments->setDim(1);
|
|
(*arguments)[0] = eleft;
|
|
e = new CallExp(loc, e, arguments);
|
|
e = e->trySemantic(sc);
|
|
if (!e)
|
|
goto Leprop;
|
|
checkPropertyCall(e, e1);
|
|
if (e2)
|
|
e = new AssignExp(loc, e, e2);
|
|
return e->semantic(sc);
|
|
}
|
|
}
|
|
return e;
|
|
|
|
Leprop:
|
|
e1->error("not a property %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
/******************************
|
|
* Perform semantic() on an array of Expressions.
|
|
*/
|
|
|
|
Expressions *arrayExpressionSemantic(Expressions *exps, Scope *sc)
|
|
{
|
|
if (exps)
|
|
{
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
if (e)
|
|
{ e = e->semantic(sc);
|
|
(*exps)[i] = e;
|
|
}
|
|
}
|
|
}
|
|
return exps;
|
|
}
|
|
|
|
|
|
/******************************
|
|
* Perform canThrow() on an array of Expressions.
|
|
*/
|
|
|
|
#if DMDV2
|
|
int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow)
|
|
{
|
|
if (exps)
|
|
{
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
if (e && e->canThrow(mustNotThrow))
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************
|
|
* Expand tuples.
|
|
*/
|
|
|
|
void expandTuples(Expressions *exps)
|
|
{
|
|
//printf("expandTuples()\n");
|
|
if (exps)
|
|
{
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *arg = (*exps)[i];
|
|
if (!arg)
|
|
continue;
|
|
|
|
// Look for tuple with 0 members
|
|
if (arg->op == TOKtype)
|
|
{ TypeExp *e = (TypeExp *)arg;
|
|
if (e->type->toBasetype()->ty == Ttuple)
|
|
{ TypeTuple *tt = (TypeTuple *)e->type->toBasetype();
|
|
|
|
if (!tt->arguments || tt->arguments->dim == 0)
|
|
{
|
|
exps->remove(i);
|
|
if (i == exps->dim)
|
|
return;
|
|
i--;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inline expand all the tuples
|
|
while (arg->op == TOKtuple)
|
|
{ TupleExp *te = (TupleExp *)arg;
|
|
|
|
exps->remove(i); // remove arg
|
|
exps->insert(i, te->exps); // replace with tuple contents
|
|
if (i == exps->dim)
|
|
return; // empty tuple, no more arguments
|
|
arg = (*exps)[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************
|
|
* Expand alias this tuples.
|
|
*/
|
|
|
|
TupleDeclaration *isAliasThisTuple(Expression *e)
|
|
{
|
|
if (e->type)
|
|
{
|
|
Type *t = e->type->toBasetype();
|
|
AggregateDeclaration *ad;
|
|
if (t->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t)->sym;
|
|
goto L1;
|
|
}
|
|
else if (t->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t)->sym;
|
|
L1:
|
|
Dsymbol *s = ad->aliasthis;
|
|
if (s && s->isVarDeclaration())
|
|
{
|
|
TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration();
|
|
if (td && td->isexp)
|
|
return td;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int expandAliasThisTuples(Expressions *exps, int starti)
|
|
{
|
|
if (!exps || exps->dim == 0)
|
|
return -1;
|
|
|
|
for (size_t u = starti; u < exps->dim; u++)
|
|
{
|
|
Expression *exp = exps->tdata()[u];
|
|
TupleDeclaration *td = isAliasThisTuple(exp);
|
|
if (td)
|
|
{
|
|
exps->remove(u);
|
|
for (size_t i = 0; i<td->objects->dim; ++i)
|
|
{
|
|
Expression *e = isExpression(td->objects->tdata()[i]);
|
|
assert(e);
|
|
assert(e->op == TOKdsymbol);
|
|
DsymbolExp *se = (DsymbolExp *)e;
|
|
Declaration *d = se->s->isDeclaration();
|
|
assert(d);
|
|
e = new DotVarExp(exp->loc, exp, d);
|
|
assert(d->type);
|
|
e->type = d->type;
|
|
exps->insert(u + i, e);
|
|
}
|
|
#if 0
|
|
printf("expansion ->\n");
|
|
for (size_t i = 0; i<exps->dim; ++i)
|
|
{
|
|
Expression *e = exps->tdata()[i];
|
|
printf("\texps[%d] e = %s %s\n", i, Token::tochars[e->op], e->toChars());
|
|
}
|
|
#endif
|
|
return u;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt)
|
|
{
|
|
#if DMDV1
|
|
/* The first element sets the type
|
|
*/
|
|
Type *t0 = NULL;
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
|
|
if (!e->type)
|
|
{ error("%s has no value", e->toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
e = resolveProperties(sc, e);
|
|
|
|
if (!t0)
|
|
t0 = e->type;
|
|
else
|
|
e = e->implicitCastTo(sc, t0);
|
|
(*exps)[i] = e;
|
|
}
|
|
|
|
if (!t0)
|
|
t0 = Type::tvoid;
|
|
if (pt)
|
|
*pt = t0;
|
|
|
|
// Eventually, we want to make this copy-on-write
|
|
return exps;
|
|
#endif
|
|
#if DMDV2
|
|
/* The type is determined by applying ?: to each pair.
|
|
*/
|
|
/* Still have a problem with:
|
|
* ubyte[][] = [ cast(ubyte[])"hello", [1]];
|
|
* which works if the array literal is initialized top down with the ubyte[][]
|
|
* type, but fails with this function doing bottom up typing.
|
|
*/
|
|
//printf("arrayExpressionToCommonType()\n");
|
|
IntegerExp integerexp(0);
|
|
CondExp condexp(0, &integerexp, NULL, NULL);
|
|
|
|
Type *t0 = NULL;
|
|
Expression *e0;
|
|
int j0;
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
|
|
e = resolveProperties(sc, e);
|
|
if (!e->type)
|
|
{ e->error("%s has no value", e->toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
|
|
if (t0)
|
|
{ if (t0 != e->type)
|
|
{
|
|
/* This applies ?: to merge the types. It's backwards;
|
|
* ?: should call this function to merge types.
|
|
*/
|
|
condexp.type = NULL;
|
|
condexp.e1 = e0;
|
|
condexp.e2 = e;
|
|
condexp.loc = e->loc;
|
|
condexp.semantic(sc);
|
|
(*exps)[j0] = condexp.e1;
|
|
e = condexp.e2;
|
|
j0 = i;
|
|
e0 = e;
|
|
t0 = e0->type;
|
|
}
|
|
}
|
|
else
|
|
{ j0 = i;
|
|
e0 = e;
|
|
t0 = e->type;
|
|
}
|
|
(*exps)[i] = e;
|
|
}
|
|
|
|
if (t0)
|
|
{
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
e = e->implicitCastTo(sc, t0);
|
|
(*exps)[i] = e;
|
|
}
|
|
}
|
|
else
|
|
t0 = Type::tvoid; // [] is typed as void[]
|
|
if (pt)
|
|
*pt = t0;
|
|
|
|
// Eventually, we want to make this copy-on-write
|
|
return exps;
|
|
#endif
|
|
}
|
|
|
|
/****************************************
|
|
* Get TemplateDeclaration enclosing FuncDeclaration.
|
|
*/
|
|
|
|
TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s)
|
|
{
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f && f->parent)
|
|
{ TemplateInstance *ti = f->parent->isTemplateInstance();
|
|
|
|
if (ti &&
|
|
!ti->isTemplateMixin() &&
|
|
(ti->name == f->ident ||
|
|
ti->toAlias()->ident == f->ident)
|
|
&&
|
|
ti->tempdecl && ti->tempdecl->onemember)
|
|
{
|
|
return ti->tempdecl;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************
|
|
* Preprocess arguments to function.
|
|
*/
|
|
|
|
void preFunctionParameters(Loc loc, Scope *sc, Expressions *exps)
|
|
{
|
|
if (exps)
|
|
{
|
|
expandTuples(exps);
|
|
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *arg = (*exps)[i];
|
|
|
|
arg = resolveProperties(sc, arg);
|
|
(*exps)[i] = arg;
|
|
|
|
//arg->rvalue();
|
|
#if 0
|
|
if (arg->type->ty == Tfunction)
|
|
{
|
|
arg = new AddrExp(arg->loc, arg);
|
|
arg = arg->semantic(sc);
|
|
(*exps)[i] = arg;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************
|
|
* If we want the value of this expression, but do not want to call
|
|
* the destructor on it.
|
|
*/
|
|
|
|
void valueNoDtor(Expression *e)
|
|
{
|
|
if (e->op == TOKcall)
|
|
{
|
|
/* The struct value returned from the function is transferred
|
|
* so do not call the destructor on it.
|
|
* Recognize:
|
|
* ((S _ctmp = S.init), _ctmp).this(...)
|
|
* and make sure the destructor is not called on _ctmp
|
|
* BUG: if e is a CommaExp, we should go down the right side.
|
|
*/
|
|
CallExp *ce = (CallExp *)e;
|
|
if (ce->e1->op == TOKdotvar)
|
|
{ DotVarExp *dve = (DotVarExp *)ce->e1;
|
|
if (dve->var->isCtorDeclaration())
|
|
{ // It's a constructor call
|
|
if (dve->e1->op == TOKcomma)
|
|
{ CommaExp *comma = (CommaExp *)dve->e1;
|
|
if (comma->e2->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)comma->e2;
|
|
VarDeclaration *ctmp = ve->var->isVarDeclaration();
|
|
if (ctmp)
|
|
ctmp->noscope = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Call copy constructor for struct value argument.
|
|
*/
|
|
#if DMDV2
|
|
Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope)
|
|
{
|
|
Type *tb = e->type->toBasetype();
|
|
assert(tb->ty == Tstruct);
|
|
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
|
|
if (sd->cpctor)
|
|
{
|
|
/* Create a variable tmp, and replace the argument e with:
|
|
* (tmp = e),tmp
|
|
* and let AssignExp() handle the construction.
|
|
* This is not the most efficent, ideally tmp would be constructed
|
|
* directly onto the stack.
|
|
*/
|
|
Identifier *idtmp = Lexer::uniqueId("__cpcttmp");
|
|
VarDeclaration *tmp = new VarDeclaration(loc, tb, idtmp, new ExpInitializer(0, e));
|
|
tmp->storage_class |= STCctfe;
|
|
tmp->noscope = noscope;
|
|
Expression *ae = new DeclarationExp(loc, tmp);
|
|
e = new CommaExp(loc, ae, new VarExp(loc, tmp));
|
|
e = e->semantic(sc);
|
|
}
|
|
return e;
|
|
}
|
|
#endif
|
|
|
|
/****************************************
|
|
* Now that we know the exact type of the function we're calling,
|
|
* the arguments[] need to be adjusted:
|
|
* 1. implicitly convert argument to the corresponding parameter type
|
|
* 2. add default arguments for any missing arguments
|
|
* 3. do default promotions on arguments corresponding to ...
|
|
* 4. add hidden _arguments[] argument
|
|
* 5. call copy constructor for struct value arguments
|
|
* Returns:
|
|
* return type from function
|
|
*/
|
|
|
|
Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
|
|
Expression *ethis, Expressions *arguments, FuncDeclaration *fd)
|
|
{
|
|
//printf("functionParameters()\n");
|
|
assert(arguments);
|
|
assert(fd || tf->next);
|
|
size_t nargs = arguments ? arguments->dim : 0;
|
|
size_t nparams = Parameter::dim(tf->parameters);
|
|
|
|
if (nargs > nparams && tf->varargs == 0)
|
|
{ error(loc, "expected %zu arguments, not %llu for non-variadic function type %s", nparams, (ulonglong)nargs, tf->toChars());
|
|
return Type::terror;
|
|
}
|
|
|
|
// If inferring return type, and semantic3() needs to be run if not already run
|
|
if (!tf->next && fd->inferRetType)
|
|
{
|
|
TemplateInstance *spec = fd->isSpeculative();
|
|
int olderrs = global.errors;
|
|
fd->semantic3(fd->scope);
|
|
// Update the template instantiation with the number
|
|
// of errors which occured.
|
|
if (spec && global.errors != olderrs)
|
|
spec->errors = global.errors - olderrs;
|
|
}
|
|
|
|
unsigned n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
|
|
|
|
unsigned wildmatch = 0;
|
|
if (ethis && tf->isWild())
|
|
{
|
|
Type *t = ethis->type;
|
|
if (t->isWild())
|
|
wildmatch |= MODwild;
|
|
else if (t->isConst())
|
|
wildmatch |= MODconst;
|
|
else if (t->isImmutable())
|
|
wildmatch |= MODimmutable;
|
|
else
|
|
wildmatch |= MODmutable;
|
|
}
|
|
|
|
int done = 0;
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
Expression *arg;
|
|
|
|
if (i < nargs)
|
|
arg = arguments->tdata()[i];
|
|
else
|
|
arg = NULL;
|
|
|
|
if (i < nparams)
|
|
{
|
|
Parameter *p = Parameter::getNth(tf->parameters, i);
|
|
|
|
if (!arg)
|
|
{
|
|
if (!p->defaultArg)
|
|
{
|
|
if (tf->varargs == 2 && i + 1 == nparams)
|
|
goto L2;
|
|
error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
|
|
return Type::terror;
|
|
}
|
|
arg = p->defaultArg;
|
|
arg = arg->inlineCopy(sc);
|
|
#if DMDV2
|
|
arg = arg->resolveLoc(loc, sc); // __FILE__ and __LINE__
|
|
#endif
|
|
arguments->push(arg);
|
|
nargs++;
|
|
}
|
|
else
|
|
{
|
|
Type *pt = p->type;
|
|
if (tf->varargs == 2 && i + 1 == nparams && pt->nextOf())
|
|
pt = pt->nextOf();
|
|
arg = arg->inferType(pt);
|
|
arguments->tdata()[i] = arg;
|
|
}
|
|
|
|
if (tf->varargs == 2 && i + 1 == nparams)
|
|
{
|
|
//printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars());
|
|
MATCH m;
|
|
if ((m = arg->implicitConvTo(p->type)) != MATCHnomatch)
|
|
{
|
|
if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf()) >= m)
|
|
goto L2;
|
|
else if (nargs != nparams)
|
|
{ error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
|
|
return Type::terror;
|
|
}
|
|
goto L1;
|
|
}
|
|
L2:
|
|
Type *tb = p->type->toBasetype();
|
|
Type *tret = p->isLazyArray();
|
|
switch (tb->ty)
|
|
{
|
|
case Tsarray:
|
|
case Tarray:
|
|
{ // Create a static array variable v of type arg->type
|
|
#ifdef IN_GCC
|
|
/* GCC 4.0 does not like zero length arrays used like
|
|
this; pass a null array value instead. Could also
|
|
just make a one-element array. */
|
|
if (nargs - i == 0)
|
|
{
|
|
arg = new NullExp(loc);
|
|
break;
|
|
}
|
|
#endif
|
|
Identifier *id = Lexer::uniqueId("__arrayArg");
|
|
Type *t = new TypeSArray(((TypeArray *)tb)->next, new IntegerExp(nargs - i));
|
|
t = t->semantic(loc, sc);
|
|
bool isSafe = fd ? fd->isSafe() : tf->trust == TRUSTsafe;
|
|
VarDeclaration *v = new VarDeclaration(loc, t, id,
|
|
isSafe ? NULL : new VoidInitializer(loc));
|
|
v->storage_class |= STCctfe;
|
|
v->semantic(sc);
|
|
v->parent = sc->parent;
|
|
//sc->insert(v);
|
|
|
|
Expression *c = new DeclarationExp(0, v);
|
|
c->type = v->type;
|
|
|
|
for (size_t u = i; u < nargs; u++)
|
|
{ Expression *a = arguments->tdata()[u];
|
|
if (tret && !((TypeArray *)tb)->next->equals(a->type))
|
|
a = a->toDelegate(sc, tret);
|
|
|
|
Expression *e = new VarExp(loc, v);
|
|
e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams));
|
|
ConstructExp *ae = new ConstructExp(loc, e, a);
|
|
if (c)
|
|
c = new CommaExp(loc, c, ae);
|
|
else
|
|
c = ae;
|
|
}
|
|
arg = new VarExp(loc, v);
|
|
if (c)
|
|
arg = new CommaExp(loc, c, arg);
|
|
break;
|
|
}
|
|
case Tclass:
|
|
{ /* Set arg to be:
|
|
* new Tclass(arg0, arg1, ..., argn)
|
|
*/
|
|
Expressions *args = new Expressions();
|
|
args->setDim(nargs - i);
|
|
for (size_t u = i; u < nargs; u++)
|
|
args->tdata()[u - i] = arguments->tdata()[u];
|
|
arg = new NewExp(loc, NULL, NULL, p->type, args);
|
|
break;
|
|
}
|
|
default:
|
|
if (!arg)
|
|
{ error(loc, "not enough arguments");
|
|
return Type::terror;
|
|
}
|
|
break;
|
|
}
|
|
arg = arg->semantic(sc);
|
|
//printf("\targ = '%s'\n", arg->toChars());
|
|
arguments->setDim(i + 1);
|
|
arguments->tdata()[i] = arg;
|
|
nargs = i + 1;
|
|
done = 1;
|
|
}
|
|
|
|
L1:
|
|
if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
|
|
{
|
|
unsigned mod = arg->type->wildConvTo(p->type);
|
|
if (mod)
|
|
{
|
|
wildmatch |= mod;
|
|
}
|
|
}
|
|
}
|
|
if (done)
|
|
break;
|
|
}
|
|
if (wildmatch)
|
|
{ /* Calculate wild matching modifier
|
|
*/
|
|
if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
|
|
wildmatch = MODconst;
|
|
else if (wildmatch & MODimmutable)
|
|
wildmatch = MODimmutable;
|
|
else if (wildmatch & MODwild)
|
|
wildmatch = MODwild;
|
|
else
|
|
{ assert(wildmatch & MODmutable);
|
|
wildmatch = MODmutable;
|
|
}
|
|
}
|
|
|
|
assert(nargs >= nparams);
|
|
for (size_t i = 0; i < nargs; i++)
|
|
{
|
|
Expression *arg = arguments->tdata()[i];
|
|
assert(arg);
|
|
|
|
if (i < nparams)
|
|
{
|
|
Parameter *p = Parameter::getNth(tf->parameters, i);
|
|
|
|
if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
|
|
{
|
|
if (p->type->hasWild())
|
|
{
|
|
arg = arg->implicitCastTo(sc, p->type->substWildTo(wildmatch));
|
|
arg = arg->optimize(WANTvalue);
|
|
}
|
|
else if (p->type != arg->type)
|
|
{
|
|
//printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
|
|
if (arg->op == TOKtype)
|
|
{ arg->error("cannot pass type %s as function argument", arg->toChars());
|
|
arg = new ErrorExp();
|
|
goto L3;
|
|
}
|
|
else
|
|
arg = arg->implicitCastTo(sc, p->type);
|
|
arg = arg->optimize(WANTvalue);
|
|
}
|
|
}
|
|
if (p->storageClass & STCref)
|
|
{
|
|
arg = arg->toLvalue(sc, arg);
|
|
}
|
|
else if (p->storageClass & STCout)
|
|
{
|
|
arg = arg->modifiableLvalue(sc, arg);
|
|
}
|
|
|
|
Type *tb = arg->type->toBasetype();
|
|
#if !SARRAYVALUE
|
|
// Convert static arrays to pointers
|
|
if (tb->ty == Tsarray)
|
|
{
|
|
arg = arg->checkToPointer();
|
|
}
|
|
#endif
|
|
#if DMDV2
|
|
if (tb->ty == Tstruct && !(p->storageClass & (STCref | STCout)))
|
|
{
|
|
if (arg->op == TOKcall)
|
|
{
|
|
/* The struct value returned from the function is transferred
|
|
* to the function, so the callee should not call the destructor
|
|
* on it.
|
|
*/
|
|
valueNoDtor(arg);
|
|
}
|
|
else
|
|
{ /* Not transferring it, so call the copy constructor
|
|
*/
|
|
arg = callCpCtor(loc, sc, arg, 1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//printf("arg: %s\n", arg->toChars());
|
|
//printf("type: %s\n", arg->type->toChars());
|
|
|
|
// Convert lazy argument to a delegate
|
|
if (p->storageClass & STClazy)
|
|
{
|
|
arg = arg->toDelegate(sc, p->type);
|
|
}
|
|
#if DMDV2
|
|
/* Look for arguments that cannot 'escape' from the called
|
|
* function.
|
|
*/
|
|
if (!tf->parameterEscapes(p))
|
|
{
|
|
Expression *a = arg;
|
|
if (a->op == TOKcast)
|
|
a = ((CastExp *)a)->e1;
|
|
|
|
/* Function literals can only appear once, so if this
|
|
* appearance was scoped, there cannot be any others.
|
|
*/
|
|
if (a->op == TOKfunction)
|
|
{ FuncExp *fe = (FuncExp *)a;
|
|
fe->fd->tookAddressOf = 0;
|
|
}
|
|
|
|
/* For passing a delegate to a scoped parameter,
|
|
* this doesn't count as taking the address of it.
|
|
* We only worry about 'escaping' references to the function.
|
|
*/
|
|
else if (a->op == TOKdelegate)
|
|
{ DelegateExp *de = (DelegateExp *)a;
|
|
if (de->e1->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)de->e1;
|
|
FuncDeclaration *f = ve->var->isFuncDeclaration();
|
|
if (f)
|
|
{ f->tookAddressOf--;
|
|
//printf("tookAddressOf = %d\n", f->tookAddressOf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
|
|
// If not D linkage, do promotions
|
|
if (tf->linkage != LINKd)
|
|
{
|
|
// Promote bytes, words, etc., to ints
|
|
arg = arg->integralPromotions(sc);
|
|
|
|
// Promote floats to doubles
|
|
switch (arg->type->ty)
|
|
{
|
|
case Tfloat32:
|
|
arg = arg->castTo(sc, Type::tfloat64);
|
|
break;
|
|
|
|
case Timaginary32:
|
|
arg = arg->castTo(sc, Type::timaginary64);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Do not allow types that need destructors
|
|
if (arg->type->needsDestruction())
|
|
{ arg->error("cannot pass types that need destruction as variadic arguments");
|
|
arg = new ErrorExp();
|
|
}
|
|
|
|
// Convert static arrays to dynamic arrays
|
|
// BUG: I don't think this is right for D2
|
|
Type *tb = arg->type->toBasetype();
|
|
if (tb->ty == Tsarray)
|
|
{ TypeSArray *ts = (TypeSArray *)tb;
|
|
Type *ta = ts->next->arrayOf();
|
|
if (ts->size(arg->loc) == 0)
|
|
arg = new NullExp(arg->loc, ta);
|
|
else
|
|
arg = arg->castTo(sc, ta);
|
|
}
|
|
#if DMDV2
|
|
if (tb->ty == Tstruct)
|
|
{
|
|
arg = callCpCtor(loc, sc, arg, 1);
|
|
}
|
|
#endif
|
|
|
|
// Give error for overloaded function addresses
|
|
if (arg->op == TOKsymoff)
|
|
{ SymOffExp *se = (SymOffExp *)arg;
|
|
if (
|
|
#if DMDV2
|
|
se->hasOverloads &&
|
|
#endif
|
|
!se->var->isFuncDeclaration()->isUnique())
|
|
{ arg->error("function %s is overloaded", arg->toChars());
|
|
arg = new ErrorExp();
|
|
}
|
|
}
|
|
arg->rvalue();
|
|
}
|
|
arg = arg->optimize(WANTvalue);
|
|
L3:
|
|
arguments->tdata()[i] = arg;
|
|
}
|
|
|
|
// If D linkage and variadic, add _arguments[] as first argument
|
|
if (tf->linkage == LINKd && tf->varargs == 1)
|
|
{
|
|
assert(arguments->dim >= nparams);
|
|
Expression *e = createTypeInfoArray(sc, (Expression **)&arguments->tdata()[nparams],
|
|
arguments->dim - nparams);
|
|
arguments->insert(0, e);
|
|
}
|
|
|
|
Type *tret = tf->next;
|
|
if (wildmatch)
|
|
{ /* Adjust function return type based on wildmatch
|
|
*/
|
|
//printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars());
|
|
tret = tret->substWildTo(wildmatch);
|
|
}
|
|
return tret;
|
|
}
|
|
|
|
/**************************************************
|
|
* Write expression out to buf, but wrap it
|
|
* in ( ) if its precedence is less than pr.
|
|
*/
|
|
|
|
void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, enum PREC pr)
|
|
{
|
|
#ifdef DEBUG
|
|
if (precedence[e->op] == PREC_zero)
|
|
printf("precedence not defined for token '%s'\n",Token::tochars[e->op]);
|
|
#endif
|
|
assert(precedence[e->op] != PREC_zero);
|
|
assert(pr != PREC_zero);
|
|
|
|
//if (precedence[e->op] == 0) e->dump(0);
|
|
if (precedence[e->op] < pr ||
|
|
/* Despite precedence, we don't allow a<b<c expressions.
|
|
* They must be parenthesized.
|
|
*/
|
|
(pr == PREC_rel && precedence[e->op] == pr))
|
|
{
|
|
buf->writeByte('(');
|
|
e->toCBuffer(buf, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
else
|
|
e->toCBuffer(buf, hgs);
|
|
}
|
|
|
|
/**************************************************
|
|
* Write out argument list to buf.
|
|
*/
|
|
|
|
void argsToCBuffer(OutBuffer *buf, Expressions *expressions, HdrGenState *hgs)
|
|
{
|
|
if (expressions)
|
|
{
|
|
for (size_t i = 0; i < expressions->dim; i++)
|
|
{ Expression *e = (*expressions)[i];
|
|
|
|
if (i)
|
|
buf->writeByte(',');
|
|
if (e)
|
|
expToCBuffer(buf, hgs, e, PREC_assign);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************
|
|
* Write out argument types to buf.
|
|
*/
|
|
|
|
void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs)
|
|
{
|
|
if (arguments)
|
|
{ OutBuffer argbuf;
|
|
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{ Expression *e = (*arguments)[i];
|
|
|
|
if (i)
|
|
buf->writeByte(',');
|
|
argbuf.reset();
|
|
e->type->toCBuffer2(&argbuf, hgs, 0);
|
|
buf->write(&argbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************** Expression **************************/
|
|
|
|
Expression::Expression(Loc loc, enum TOK op, int size)
|
|
: loc(loc)
|
|
{
|
|
//printf("Expression::Expression(op = %d) this = %p\n", op, this);
|
|
this->loc = loc;
|
|
this->op = op;
|
|
this->size = size;
|
|
this->parens = 0;
|
|
type = NULL;
|
|
}
|
|
|
|
Expression *Expression::syntaxCopy()
|
|
{
|
|
//printf("Expression::syntaxCopy()\n");
|
|
//dump(0);
|
|
return copy();
|
|
}
|
|
|
|
/*********************************
|
|
* Does *not* do a deep copy.
|
|
*/
|
|
|
|
Expression *Expression::copy()
|
|
{
|
|
Expression *e;
|
|
if (!size)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stdmsg, "No expression copy for: %s\n", toChars());
|
|
printf("op = %d\n", op);
|
|
dump(0);
|
|
#endif
|
|
assert(0);
|
|
}
|
|
e = (Expression *)mem.malloc(size);
|
|
//printf("Expression::copy(op = %d) e = %p\n", op, e);
|
|
return (Expression *)memcpy(e, this, size);
|
|
}
|
|
|
|
/**************************
|
|
* Semantically analyze Expression.
|
|
* Determine types, fold constants, etc.
|
|
*/
|
|
|
|
Expression *Expression::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("Expression::semantic() %s\n", toChars());
|
|
#endif
|
|
if (type)
|
|
type = type->semantic(loc, sc);
|
|
else
|
|
type = Type::tvoid;
|
|
return this;
|
|
}
|
|
|
|
/**********************************
|
|
* Try to run semantic routines.
|
|
* If they fail, return NULL.
|
|
*/
|
|
|
|
Expression *Expression::trySemantic(Scope *sc)
|
|
{
|
|
//printf("+trySemantic(%s)\n", toChars());
|
|
unsigned errors = global.startGagging();
|
|
Expression *e = semantic(sc);
|
|
if (global.endGagging(errors))
|
|
{
|
|
e = NULL;
|
|
}
|
|
//printf("-trySemantic(%s)\n", toChars());
|
|
return e;
|
|
}
|
|
|
|
void Expression::print()
|
|
{
|
|
fprintf(stdmsg, "%s\n", toChars());
|
|
fflush(stdmsg);
|
|
}
|
|
|
|
char *Expression::toChars()
|
|
{ OutBuffer *buf;
|
|
HdrGenState hgs;
|
|
|
|
memset(&hgs, 0, sizeof(hgs));
|
|
buf = new OutBuffer();
|
|
toCBuffer(buf, &hgs);
|
|
return buf->toChars();
|
|
}
|
|
|
|
void Expression::error(const char *format, ...)
|
|
{
|
|
if (type != Type::terror)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
::verror(loc, format, ap);
|
|
va_end( ap );
|
|
}
|
|
}
|
|
|
|
void Expression::warning(const char *format, ...)
|
|
{
|
|
if (type != Type::terror)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
::vwarning(loc, format, ap);
|
|
va_end( ap );
|
|
}
|
|
}
|
|
|
|
int Expression::rvalue()
|
|
{
|
|
if (type && type->toBasetype()->ty == Tvoid)
|
|
{ error("expression %s is void and has no value", toChars());
|
|
#if 0
|
|
dump(0);
|
|
halt();
|
|
#endif
|
|
if (!global.gag)
|
|
type = Type::terror;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
Expression *Expression::combine(Expression *e1, Expression *e2)
|
|
{
|
|
if (e1)
|
|
{
|
|
if (e2)
|
|
{
|
|
e1 = new CommaExp(e1->loc, e1, e2);
|
|
e1->type = e2->type;
|
|
}
|
|
}
|
|
else
|
|
e1 = e2;
|
|
return e1;
|
|
}
|
|
|
|
dinteger_t Expression::toInteger()
|
|
{
|
|
//printf("Expression %s\n", Token::toChars(op));
|
|
error("Integer constant expression expected instead of %s", toChars());
|
|
return 0;
|
|
}
|
|
|
|
uinteger_t Expression::toUInteger()
|
|
{
|
|
//printf("Expression %s\n", Token::toChars(op));
|
|
return (uinteger_t)toInteger();
|
|
}
|
|
|
|
real_t Expression::toReal()
|
|
{
|
|
error("Floating point constant expression expected instead of %s", toChars());
|
|
return ldouble(0);
|
|
}
|
|
|
|
real_t Expression::toImaginary()
|
|
{
|
|
error("Floating point constant expression expected instead of %s", toChars());
|
|
return ldouble(0);
|
|
}
|
|
|
|
complex_t Expression::toComplex()
|
|
{
|
|
error("Floating point constant expression expected instead of %s", toChars());
|
|
#ifdef IN_GCC
|
|
return complex_t(real_t(0)); // %% nicer
|
|
#else
|
|
return 0.0;
|
|
#endif
|
|
}
|
|
|
|
StringExp *Expression::toString()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void Expression::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(Token::toChars(op));
|
|
}
|
|
|
|
void Expression::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
error("expression %s is not a valid template value argument", toChars());
|
|
}
|
|
|
|
/***************************************
|
|
* Return !=0 if expression is an lvalue.
|
|
*/
|
|
#if DMDV2
|
|
int Expression::isLvalue()
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*******************************
|
|
* Give error if we're not an lvalue.
|
|
* If we can, convert expression to be an lvalue.
|
|
*/
|
|
|
|
Expression *Expression::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
if (!e)
|
|
e = this;
|
|
else if (!loc.filename)
|
|
loc = e->loc;
|
|
error("%s is not an lvalue", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Expression *Expression::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars());
|
|
|
|
// See if this expression is a modifiable lvalue (i.e. not const)
|
|
#if DMDV2
|
|
if (type && (!type->isMutable() || !type->isAssignable()))
|
|
{ error("%s is not mutable", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
#endif
|
|
return toLvalue(sc, e);
|
|
}
|
|
|
|
|
|
/************************************
|
|
* Detect cases where pointers to the stack can 'escape' the
|
|
* lifetime of the stack frame.
|
|
*/
|
|
|
|
void Expression::checkEscape()
|
|
{
|
|
}
|
|
|
|
void Expression::checkEscapeRef()
|
|
{
|
|
}
|
|
|
|
void Expression::checkScalar()
|
|
{
|
|
if (!type->isscalar() && type->toBasetype() != Type::terror)
|
|
error("'%s' is not a scalar, it is a %s", toChars(), type->toChars());
|
|
rvalue();
|
|
}
|
|
|
|
void Expression::checkNoBool()
|
|
{
|
|
if (type->toBasetype()->ty == Tbool)
|
|
error("operation not allowed on bool '%s'", toChars());
|
|
}
|
|
|
|
Expression *Expression::checkIntegral()
|
|
{
|
|
if (!type->isintegral())
|
|
{ if (type->toBasetype() != Type::terror)
|
|
error("'%s' is not of integral type, it is a %s", toChars(), type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (!rvalue())
|
|
return new ErrorExp();
|
|
return this;
|
|
}
|
|
|
|
Expression *Expression::checkArithmetic()
|
|
{
|
|
if (!type->isintegral() && !type->isfloating())
|
|
{ if (type->toBasetype() != Type::terror)
|
|
error("'%s' is not of arithmetic type, it is a %s", toChars(), type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (!rvalue())
|
|
return new ErrorExp();
|
|
return this;
|
|
}
|
|
|
|
void Expression::checkDeprecated(Scope *sc, Dsymbol *s)
|
|
{
|
|
s->checkDeprecated(loc, sc);
|
|
}
|
|
|
|
#if DMDV2
|
|
/*********************************************
|
|
* Calling function f.
|
|
* Check the purity, i.e. if we're in a pure function
|
|
* we can only call other pure functions.
|
|
*/
|
|
void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
|
|
{
|
|
#if 1
|
|
if (sc->func)
|
|
{
|
|
/* Given:
|
|
* void f()
|
|
* { pure void g()
|
|
* {
|
|
* void h()
|
|
* {
|
|
* void i() { }
|
|
* }
|
|
* }
|
|
* }
|
|
* g() can call h() but not f()
|
|
* i() can call h() and g() but not f()
|
|
*/
|
|
FuncDeclaration *outerfunc = sc->func;
|
|
// Find the closest pure parent of the calling function
|
|
while (outerfunc->toParent2() &&
|
|
!outerfunc->isPureBypassingInference() &&
|
|
outerfunc->toParent2()->isFuncDeclaration())
|
|
{
|
|
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
|
|
}
|
|
// Find the closest pure parent of the called function
|
|
FuncDeclaration *calledparent = f;
|
|
while (calledparent->toParent2() && !calledparent->isPureBypassingInference()
|
|
&& calledparent->toParent2()->isFuncDeclaration() )
|
|
{
|
|
calledparent = calledparent->toParent2()->isFuncDeclaration();
|
|
}
|
|
// If the caller has a pure parent, then either the called func must be pure,
|
|
// OR, they must have the same pure parent.
|
|
if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now
|
|
!sc->intypeof &&
|
|
!(sc->flags & SCOPEdebug) &&
|
|
!(f->isPure() || (calledparent == outerfunc)))
|
|
{
|
|
if (outerfunc->setImpure())
|
|
error("pure function '%s' cannot call impure function '%s'",
|
|
outerfunc->toChars(), f->toChars());
|
|
}
|
|
}
|
|
#else
|
|
if (sc->func && sc->func->isPure() && !sc->intypeof && !f->isPure())
|
|
error("pure function '%s' cannot call impure function '%s'",
|
|
sc->func->toChars(), f->toChars());
|
|
#endif
|
|
}
|
|
|
|
/*******************************************
|
|
* Accessing variable v.
|
|
* Check for purity and safety violations.
|
|
* If ethis is not NULL, then ethis is the 'this' pointer as in ethis.v
|
|
*/
|
|
|
|
void Expression::checkPurity(Scope *sc, VarDeclaration *v, Expression *ethis)
|
|
{
|
|
/* Look for purity and safety violations when accessing variable v
|
|
* from current function.
|
|
*/
|
|
if (sc->func &&
|
|
!sc->intypeof && // allow violations inside typeof(expression)
|
|
!(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals
|
|
v->ident != Id::ctfe && // magic variable never violates pure and safe
|
|
!v->isImmutable() && // always safe and pure to access immutables...
|
|
!(v->isConst() && v->isDataseg() && !v->type->hasPointers()) && // const global value types are immutable
|
|
!(v->storage_class & STCmanifest) // ...or manifest constants
|
|
)
|
|
{
|
|
if (v->isDataseg())
|
|
{
|
|
/* Accessing global mutable state.
|
|
* Therefore, this function and all its immediately enclosing
|
|
* functions must be pure.
|
|
*/
|
|
bool msg = FALSE;
|
|
for (Dsymbol *s = sc->func; s; s = s->toParent2())
|
|
{
|
|
FuncDeclaration *ff = s->isFuncDeclaration();
|
|
if (!ff)
|
|
break;
|
|
if (ff->setImpure() && !msg)
|
|
{ error("pure function '%s' cannot access mutable static data '%s'",
|
|
sc->func->toPrettyChars(), v->toChars());
|
|
msg = TRUE; // only need the innermost message
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Given:
|
|
* void f()
|
|
* { int fx;
|
|
* pure void g()
|
|
* { int gx;
|
|
* void h()
|
|
* { int hx;
|
|
* void i() { }
|
|
* }
|
|
* }
|
|
* }
|
|
* i() can modify hx and gx but not fx
|
|
*/
|
|
|
|
Dsymbol *vparent = v->toParent2();
|
|
for (Dsymbol *s = sc->func; s; s = s->toParent2())
|
|
{
|
|
if (s == vparent)
|
|
break;
|
|
FuncDeclaration *ff = s->isFuncDeclaration();
|
|
if (!ff)
|
|
break;
|
|
if (ff->setImpure())
|
|
{ error("pure nested function '%s' cannot access mutable data '%s'",
|
|
ff->toChars(), v->toChars());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do not allow safe functions to access __gshared data
|
|
*/
|
|
if (v->storage_class & STCgshared)
|
|
{
|
|
if (sc->func->setUnsafe())
|
|
error("safe function '%s' cannot access __gshared data '%s'",
|
|
sc->func->toChars(), v->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Expression::checkSafety(Scope *sc, FuncDeclaration *f)
|
|
{
|
|
if (sc->func && !sc->intypeof &&
|
|
!f->isSafe() && !f->isTrusted())
|
|
{
|
|
if (sc->func->setUnsafe())
|
|
error("safe function '%s' cannot call system function '%s'",
|
|
sc->func->toChars(), f->toChars());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*****************************
|
|
* Check that expression can be tested for true or false.
|
|
*/
|
|
|
|
Expression *Expression::checkToBoolean(Scope *sc)
|
|
{
|
|
// Default is 'yes' - do nothing
|
|
|
|
#ifdef DEBUG
|
|
if (!type)
|
|
dump(0);
|
|
assert(type);
|
|
#endif
|
|
|
|
// Structs can be converted to bool using opCast(bool)()
|
|
Type *tb = type->toBasetype();
|
|
if (tb->ty == Tstruct)
|
|
{ AggregateDeclaration *ad = ((TypeStruct *)tb)->sym;
|
|
/* Don't really need to check for opCast first, but by doing so we
|
|
* get better error messages if it isn't there.
|
|
*/
|
|
Dsymbol *fd = search_function(ad, Id::cast);
|
|
if (fd)
|
|
{
|
|
Expression *e = new CastExp(loc, this, Type::tbool);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
// Forward to aliasthis.
|
|
if (ad->aliasthis)
|
|
{
|
|
Expression *e = resolveAliasThis(sc, this);
|
|
e = e->checkToBoolean(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (!type->checkBoolean())
|
|
{ if (type->toBasetype() != Type::terror)
|
|
error("expression %s of type %s does not have a boolean value", toChars(), type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/****************************
|
|
*/
|
|
|
|
Expression *Expression::checkToPointer()
|
|
{
|
|
//printf("Expression::checkToPointer()\n");
|
|
Expression *e = this;
|
|
|
|
#if !SARRAYVALUE
|
|
// If C static array, convert to pointer
|
|
Type *tb = type->toBasetype();
|
|
if (tb->ty == Tsarray)
|
|
{ TypeSArray *ts = (TypeSArray *)tb;
|
|
if (ts->size(loc) == 0)
|
|
e = new NullExp(loc);
|
|
else
|
|
e = new AddrExp(loc, this);
|
|
e->type = ts->next->pointerTo();
|
|
}
|
|
#endif
|
|
return e;
|
|
}
|
|
|
|
/******************************
|
|
* Take address of expression.
|
|
*/
|
|
|
|
Expression *Expression::addressOf(Scope *sc)
|
|
{
|
|
Expression *e;
|
|
Type *t = type;
|
|
|
|
//printf("Expression::addressOf()\n");
|
|
e = toLvalue(sc, NULL);
|
|
e = new AddrExp(loc, e);
|
|
e->type = t->pointerTo();
|
|
return e;
|
|
}
|
|
|
|
/******************************
|
|
* If this is a reference, dereference it.
|
|
*/
|
|
|
|
Expression *Expression::deref()
|
|
{
|
|
//printf("Expression::deref()\n");
|
|
// type could be null if forward referencing an 'auto' variable
|
|
if (type && type->ty == Treference)
|
|
{
|
|
Expression *e = new PtrExp(loc, this);
|
|
e->type = ((TypeReference *)type)->next;
|
|
return e;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/********************************
|
|
* Does this expression statically evaluate to a boolean TRUE or FALSE?
|
|
*/
|
|
|
|
int Expression::isBool(int result)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/********************************
|
|
* Does this expression result in either a 1 or a 0?
|
|
*/
|
|
|
|
int Expression::isBit()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************
|
|
* Resolve __LINE__ and __FILE__ to loc.
|
|
*/
|
|
|
|
Expression *Expression::resolveLoc(Loc loc, Scope *sc)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Expressions *Expression::arraySyntaxCopy(Expressions *exps)
|
|
{ Expressions *a = NULL;
|
|
|
|
if (exps)
|
|
{
|
|
a = new Expressions();
|
|
a->setDim(exps->dim);
|
|
for (size_t i = 0; i < a->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
|
|
if (e)
|
|
e = e->syntaxCopy();
|
|
a->tdata()[i] = e;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
/***************************************************
|
|
* Recognize expressions of the form:
|
|
* ((T v = init), v)
|
|
* where v is a temp.
|
|
* This is used in optimizing out unnecessary temporary generation.
|
|
* Returns initializer expression of v if so, NULL if not.
|
|
*/
|
|
|
|
Expression *Expression::isTemp()
|
|
{
|
|
//printf("isTemp() %s\n", toChars());
|
|
if (op == TOKcomma)
|
|
{ CommaExp *ec = (CommaExp *)this;
|
|
if (ec->e1->op == TOKdeclaration &&
|
|
ec->e2->op == TOKvar)
|
|
{ DeclarationExp *de = (DeclarationExp *)ec->e1;
|
|
VarExp *ve = (VarExp *)ec->e2;
|
|
if (ve->var == de->declaration && ve->var->storage_class & STCctfe)
|
|
{ VarDeclaration *v = ve->var->isVarDeclaration();
|
|
if (v && v->init)
|
|
{
|
|
ExpInitializer *ei = v->init->isExpInitializer();
|
|
if (ei)
|
|
{ Expression *e = ei->exp;
|
|
if (e->op == TOKconstruct)
|
|
{ ConstructExp *ce = (ConstructExp *)e;
|
|
if (ce->e1->op == TOKvar && ((VarExp *)ce->e1)->var == ve->var)
|
|
e = ce->e2;
|
|
}
|
|
return e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************
|
|
* Destructors are attached to VarDeclarations.
|
|
* Hence, if expression returns a temp that needs a destructor,
|
|
* make sure and create a VarDeclaration for that temp.
|
|
*/
|
|
|
|
Expression *Expression::addDtorHook(Scope *sc)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/******************************** IntegerExp **************************/
|
|
|
|
IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type)
|
|
: Expression(loc, TOKint64, sizeof(IntegerExp))
|
|
{
|
|
//printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : "");
|
|
if (type && !type->isscalar())
|
|
{
|
|
//printf("%s, loc = %d\n", toChars(), loc.linnum);
|
|
if (type->ty != Terror)
|
|
error("integral constant must be scalar type, not %s", type->toChars());
|
|
type = Type::terror;
|
|
}
|
|
this->type = type;
|
|
this->value = value;
|
|
}
|
|
|
|
IntegerExp::IntegerExp(dinteger_t value)
|
|
: Expression(0, TOKint64, sizeof(IntegerExp))
|
|
{
|
|
this->type = Type::tint32;
|
|
this->value = value;
|
|
}
|
|
|
|
int IntegerExp::equals(Object *o)
|
|
{ IntegerExp *ne;
|
|
|
|
if (this == o ||
|
|
(((Expression *)o)->op == TOKint64 &&
|
|
((ne = (IntegerExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) &&
|
|
value == ne->value))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
char *IntegerExp::toChars()
|
|
{
|
|
#if 1
|
|
return Expression::toChars();
|
|
#else
|
|
static char buffer[sizeof(value) * 3 + 1];
|
|
|
|
sprintf(buffer, "%lld", value);
|
|
return buffer;
|
|
#endif
|
|
}
|
|
|
|
dinteger_t IntegerExp::toInteger()
|
|
{ Type *t;
|
|
|
|
t = type;
|
|
while (t)
|
|
{
|
|
switch (t->ty)
|
|
{
|
|
case Tbool: value = (value != 0); break;
|
|
case Tint8: value = (d_int8) value; break;
|
|
case Tchar:
|
|
case Tuns8: value = (d_uns8) value; break;
|
|
case Tint16: value = (d_int16) value; break;
|
|
case Twchar:
|
|
case Tuns16: value = (d_uns16) value; break;
|
|
case Tint32: value = (d_int32) value; break;
|
|
case Tdchar:
|
|
case Tuns32: value = (d_uns32) value; break;
|
|
case Tint64: value = (d_int64) value; break;
|
|
case Tuns64: value = (d_uns64) value; break;
|
|
case Tpointer:
|
|
if (PTRSIZE == 4)
|
|
value = (d_uns32) value;
|
|
else if (PTRSIZE == 8)
|
|
value = (d_uns64) value;
|
|
else
|
|
assert(0);
|
|
break;
|
|
|
|
case Tenum:
|
|
{
|
|
TypeEnum *te = (TypeEnum *)t;
|
|
t = te->sym->memtype;
|
|
continue;
|
|
}
|
|
|
|
case Ttypedef:
|
|
{
|
|
TypeTypedef *tt = (TypeTypedef *)t;
|
|
t = tt->sym->basetype;
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
/* This can happen if errors, such as
|
|
* the type is painted on like in fromConstInitializer().
|
|
*/
|
|
if (!global.errors)
|
|
{
|
|
printf("e = %p, ty = %d\n", this, type->ty);
|
|
type->print();
|
|
assert(0);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
real_t IntegerExp::toReal()
|
|
{
|
|
Type *t;
|
|
|
|
toInteger();
|
|
t = type->toBasetype();
|
|
if (t->ty == Tuns64)
|
|
return ldouble((d_uns64)value);
|
|
else
|
|
return ldouble((d_int64)value);
|
|
}
|
|
|
|
real_t IntegerExp::toImaginary()
|
|
{
|
|
return ldouble(0);
|
|
}
|
|
|
|
complex_t IntegerExp::toComplex()
|
|
{
|
|
return toReal();
|
|
}
|
|
|
|
int IntegerExp::isBool(int result)
|
|
{
|
|
int r = toInteger() != 0;
|
|
return result ? r : !r;
|
|
}
|
|
|
|
Expression *IntegerExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
{
|
|
// Determine what the type of this number is
|
|
dinteger_t number = value;
|
|
|
|
if (number & 0x8000000000000000LL)
|
|
type = Type::tuns64;
|
|
else if (number & 0xFFFFFFFF80000000LL)
|
|
type = Type::tint64;
|
|
else
|
|
type = Type::tint32;
|
|
}
|
|
else
|
|
{ if (!type->deco)
|
|
type = type->semantic(loc, sc);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Expression *IntegerExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
if (!e)
|
|
e = this;
|
|
else if (!loc.filename)
|
|
loc = e->loc;
|
|
e->error("constant %s is not an lvalue", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
dinteger_t v = toInteger();
|
|
|
|
if (type)
|
|
{ Type *t = type;
|
|
|
|
L1:
|
|
switch (t->ty)
|
|
{
|
|
case Tenum:
|
|
{ TypeEnum *te = (TypeEnum *)t;
|
|
buf->printf("cast(%s)", te->sym->toChars());
|
|
t = te->sym->memtype;
|
|
goto L1;
|
|
}
|
|
|
|
case Ttypedef:
|
|
{ TypeTypedef *tt = (TypeTypedef *)t;
|
|
buf->printf("cast(%s)", tt->sym->toChars());
|
|
t = tt->sym->basetype;
|
|
goto L1;
|
|
}
|
|
|
|
case Twchar: // BUG: need to cast(wchar)
|
|
case Tdchar: // BUG: need to cast(dchar)
|
|
if ((uinteger_t)v > 0xFF)
|
|
{
|
|
buf->printf("'\\U%08x'", v);
|
|
break;
|
|
}
|
|
case Tchar:
|
|
{
|
|
unsigned o = buf->offset;
|
|
if (v == '\'')
|
|
buf->writestring("'\\''");
|
|
else if (isprint(v) && v != '\\')
|
|
buf->printf("'%c'", (int)v);
|
|
else
|
|
buf->printf("'\\x%02x'", (int)v);
|
|
if (hgs->ddoc)
|
|
escapeDdocString(buf, o);
|
|
break;
|
|
}
|
|
|
|
case Tint8:
|
|
buf->writestring("cast(byte)");
|
|
goto L2;
|
|
|
|
case Tint16:
|
|
buf->writestring("cast(short)");
|
|
goto L2;
|
|
|
|
case Tint32:
|
|
L2:
|
|
buf->printf("%d", (int)v);
|
|
break;
|
|
|
|
case Tuns8:
|
|
buf->writestring("cast(ubyte)");
|
|
goto L3;
|
|
|
|
case Tuns16:
|
|
buf->writestring("cast(ushort)");
|
|
goto L3;
|
|
|
|
case Tuns32:
|
|
L3:
|
|
buf->printf("%du", (unsigned)v);
|
|
break;
|
|
|
|
case Tint64:
|
|
buf->printf("%lldL", v);
|
|
break;
|
|
|
|
case Tuns64:
|
|
L4:
|
|
buf->printf("%lluLU", v);
|
|
break;
|
|
|
|
case Tbool:
|
|
buf->writestring((char *)(v ? "true" : "false"));
|
|
break;
|
|
|
|
case Tpointer:
|
|
buf->writestring("cast(");
|
|
buf->writestring(t->toChars());
|
|
buf->writeByte(')');
|
|
if (PTRSIZE == 4)
|
|
goto L3;
|
|
else if (PTRSIZE == 8)
|
|
goto L4;
|
|
else
|
|
assert(0);
|
|
|
|
default:
|
|
/* This can happen if errors, such as
|
|
* the type is painted on like in fromConstInitializer().
|
|
*/
|
|
if (!global.errors)
|
|
{
|
|
#ifdef DEBUG
|
|
t->print();
|
|
#endif
|
|
assert(0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (v & 0x8000000000000000LL)
|
|
buf->printf("0x%llx", v);
|
|
else
|
|
buf->printf("%lld", v);
|
|
}
|
|
|
|
void IntegerExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
if ((sinteger_t)value < 0)
|
|
buf->printf("N%lld", -value);
|
|
else
|
|
{
|
|
/* This is an awful hack to maintain backwards compatibility.
|
|
* There really always should be an 'i' before a number, but
|
|
* there wasn't in earlier implementations, so to maintain
|
|
* backwards compatibility it is only done if necessary to disambiguate.
|
|
* See bugzilla 3029
|
|
*/
|
|
if (buf->offset > 0 && isdigit(buf->data[buf->offset - 1]))
|
|
buf->writeByte('i');
|
|
|
|
buf->printf("%lld", value);
|
|
}
|
|
}
|
|
|
|
/******************************** ErrorExp **************************/
|
|
|
|
/* Use this expression for error recovery.
|
|
* It should behave as a 'sink' to prevent further cascaded error messages.
|
|
*/
|
|
|
|
ErrorExp::ErrorExp()
|
|
: IntegerExp(0, 0, Type::terror)
|
|
{
|
|
op = TOKerror;
|
|
}
|
|
|
|
Expression *ErrorExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
void ErrorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("__error");
|
|
}
|
|
|
|
/******************************** RealExp **************************/
|
|
|
|
RealExp::RealExp(Loc loc, real_t value, Type *type)
|
|
: Expression(loc, TOKfloat64, sizeof(RealExp))
|
|
{
|
|
//printf("RealExp::RealExp(%Lg)\n", value);
|
|
this->value = value;
|
|
this->type = type;
|
|
}
|
|
|
|
char *RealExp::toChars()
|
|
{
|
|
char buffer[sizeof(value) * 3 + 8 + 1 + 1];
|
|
|
|
#ifdef IN_GCC
|
|
value.format(buffer, sizeof(buffer));
|
|
#else
|
|
ld_sprint(buffer, 'g', value);
|
|
#endif
|
|
if (type->isimaginary())
|
|
strcat(buffer, "i");
|
|
|
|
assert(strlen(buffer) < sizeof(buffer));
|
|
return mem.strdup(buffer);
|
|
}
|
|
|
|
dinteger_t RealExp::toInteger()
|
|
{
|
|
#ifdef IN_GCC
|
|
return (sinteger_t) toReal().toInt();
|
|
#else
|
|
return (sinteger_t) toReal();
|
|
#endif
|
|
}
|
|
|
|
uinteger_t RealExp::toUInteger()
|
|
{
|
|
#ifdef IN_GCC
|
|
return (uinteger_t) toReal().toInt();
|
|
#else
|
|
return (uinteger_t) toReal();
|
|
#endif
|
|
}
|
|
|
|
real_t RealExp::toReal()
|
|
{
|
|
return type->isreal() ? value : ldouble(0);
|
|
}
|
|
|
|
real_t RealExp::toImaginary()
|
|
{
|
|
return type->isreal() ? ldouble(0) : value;
|
|
}
|
|
|
|
complex_t RealExp::toComplex()
|
|
{
|
|
#ifdef __DMC__
|
|
return toReal() + toImaginary() * I;
|
|
#else
|
|
return complex_t(toReal(), toImaginary());
|
|
#endif
|
|
}
|
|
|
|
/********************************
|
|
* Test to see if two reals are the same.
|
|
* Regard NaN's as equivalent.
|
|
* Regard +0 and -0 as different.
|
|
*/
|
|
|
|
int RealEquals(real_t x1, real_t x2)
|
|
{
|
|
return (Port::isNan(x1) && Port::isNan(x2)) ||
|
|
/* In some cases, the REALPAD bytes get garbage in them,
|
|
* so be sure and ignore them.
|
|
*/
|
|
memcmp(&x1, &x2, REALSIZE - REALPAD) == 0;
|
|
}
|
|
|
|
int RealExp::equals(Object *o)
|
|
{ RealExp *ne;
|
|
|
|
if (this == o ||
|
|
(((Expression *)o)->op == TOKfloat64 &&
|
|
((ne = (RealExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) &&
|
|
RealEquals(value, ne->value)
|
|
)
|
|
)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Expression *RealExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
type = Type::tfloat64;
|
|
else
|
|
type = type->semantic(loc, sc);
|
|
return this;
|
|
}
|
|
|
|
int RealExp::isBool(int result)
|
|
{
|
|
#ifdef IN_GCC
|
|
return result ? (! value.isZero()) : (value.isZero());
|
|
#else
|
|
return result ? (value != 0)
|
|
: (value == 0);
|
|
#endif
|
|
}
|
|
|
|
void floatToBuffer(OutBuffer *buf, Type *type, real_t value)
|
|
{
|
|
/* In order to get an exact representation, try converting it
|
|
* to decimal then back again. If it matches, use it.
|
|
* If it doesn't, fall back to hex, which is
|
|
* always exact.
|
|
*/
|
|
char buffer[25];
|
|
ld_sprint(buffer, 'g', value);
|
|
assert(strlen(buffer) < sizeof(buffer));
|
|
#if _WIN32 && __DMC__
|
|
char *save = __locale_decpoint;
|
|
__locale_decpoint = ".";
|
|
real_t r = strtold(buffer, NULL);
|
|
__locale_decpoint = save;
|
|
#else
|
|
real_t r = strtold(buffer, NULL);
|
|
#endif
|
|
if (r != value) // if exact duplication
|
|
ld_sprint(buffer, 'a', value);
|
|
buf->writestring(buffer);
|
|
|
|
if (type)
|
|
{
|
|
Type *t = type->toBasetype();
|
|
switch (t->ty)
|
|
{
|
|
case Tfloat32:
|
|
case Timaginary32:
|
|
case Tcomplex32:
|
|
buf->writeByte('F');
|
|
break;
|
|
|
|
case Tfloat80:
|
|
case Timaginary80:
|
|
case Tcomplex80:
|
|
buf->writeByte('L');
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (t->isimaginary())
|
|
buf->writeByte('i');
|
|
}
|
|
}
|
|
|
|
void RealExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
floatToBuffer(buf, type, value);
|
|
}
|
|
|
|
void realToMangleBuffer(OutBuffer *buf, real_t value)
|
|
{
|
|
/* Rely on %A to get portable mangling.
|
|
* Must munge result to get only identifier characters.
|
|
*
|
|
* Possible values from %A => mangled result
|
|
* NAN => NAN
|
|
* -INF => NINF
|
|
* INF => INF
|
|
* -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
|
|
* 0X1.9P+2 => 19P2
|
|
*/
|
|
|
|
if (Port::isNan(value))
|
|
buf->writestring("NAN"); // no -NAN bugs
|
|
else
|
|
{
|
|
char buffer[32];
|
|
int n = ld_sprint(buffer, 'A', value);
|
|
assert(n > 0 && n < sizeof(buffer));
|
|
for (int i = 0; i < n; i++)
|
|
{ char c = buffer[i];
|
|
|
|
switch (c)
|
|
{
|
|
case '-':
|
|
buf->writeByte('N');
|
|
break;
|
|
|
|
case '+':
|
|
case 'X':
|
|
case '.':
|
|
break;
|
|
|
|
case '0':
|
|
if (i < 2)
|
|
break; // skip leading 0X
|
|
default:
|
|
buf->writeByte(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RealExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
buf->writeByte('e');
|
|
realToMangleBuffer(buf, value);
|
|
}
|
|
|
|
|
|
/******************************** ComplexExp **************************/
|
|
|
|
ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type)
|
|
: Expression(loc, TOKcomplex80, sizeof(ComplexExp))
|
|
{
|
|
this->value = value;
|
|
this->type = type;
|
|
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
|
|
}
|
|
|
|
char *ComplexExp::toChars()
|
|
{
|
|
char buffer[sizeof(value) * 3 + 8 + 1];
|
|
|
|
char buf1[sizeof(value) * 3 + 8 + 1];
|
|
char buf2[sizeof(value) * 3 + 8 + 1];
|
|
#ifdef IN_GCC
|
|
creall(value).format(buf1, sizeof(buf1));
|
|
cimagl(value).format(buf2, sizeof(buf2));
|
|
#else
|
|
ld_sprint(buffer, 'g', creall(value));
|
|
ld_sprint(buffer, 'g', cimagl(value));
|
|
#endif
|
|
sprintf(buffer, "(%s+%si)", buf1, buf2);
|
|
assert(strlen(buffer) < sizeof(buffer));
|
|
return mem.strdup(buffer);
|
|
}
|
|
|
|
dinteger_t ComplexExp::toInteger()
|
|
{
|
|
#ifdef IN_GCC
|
|
return (sinteger_t) toReal().toInt();
|
|
#else
|
|
return (sinteger_t) toReal();
|
|
#endif
|
|
}
|
|
|
|
uinteger_t ComplexExp::toUInteger()
|
|
{
|
|
#ifdef IN_GCC
|
|
return (uinteger_t) toReal().toInt();
|
|
#else
|
|
return (uinteger_t) toReal();
|
|
#endif
|
|
}
|
|
|
|
real_t ComplexExp::toReal()
|
|
{
|
|
return creall(value);
|
|
}
|
|
|
|
real_t ComplexExp::toImaginary()
|
|
{
|
|
return cimagl(value);
|
|
}
|
|
|
|
complex_t ComplexExp::toComplex()
|
|
{
|
|
return value;
|
|
}
|
|
|
|
int ComplexExp::equals(Object *o)
|
|
{ ComplexExp *ne;
|
|
|
|
if (this == o ||
|
|
(((Expression *)o)->op == TOKcomplex80 &&
|
|
((ne = (ComplexExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) &&
|
|
RealEquals(creall(value), creall(ne->value)) &&
|
|
RealEquals(cimagl(value), cimagl(ne->value))
|
|
)
|
|
)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Expression *ComplexExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
type = Type::tcomplex80;
|
|
else
|
|
type = type->semantic(loc, sc);
|
|
return this;
|
|
}
|
|
|
|
int ComplexExp::isBool(int result)
|
|
{
|
|
if (result)
|
|
return (bool)(value);
|
|
else
|
|
return !value;
|
|
}
|
|
|
|
void ComplexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
/* Print as:
|
|
* (re+imi)
|
|
*/
|
|
#ifdef IN_GCC
|
|
char buf1[sizeof(value) * 3 + 8 + 1];
|
|
char buf2[sizeof(value) * 3 + 8 + 1];
|
|
creall(value).format(buf1, sizeof(buf1));
|
|
cimagl(value).format(buf2, sizeof(buf2));
|
|
buf->printf("(%s+%si)", buf1, buf2);
|
|
#else
|
|
buf->writeByte('(');
|
|
floatToBuffer(buf, type, creall(value));
|
|
buf->writeByte('+');
|
|
floatToBuffer(buf, type, cimagl(value));
|
|
buf->writestring("i)");
|
|
#endif
|
|
}
|
|
|
|
void ComplexExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
buf->writeByte('c');
|
|
real_t r = toReal();
|
|
realToMangleBuffer(buf, r);
|
|
buf->writeByte('c'); // separate the two
|
|
r = toImaginary();
|
|
realToMangleBuffer(buf, r);
|
|
}
|
|
|
|
/******************************** IdentifierExp **************************/
|
|
|
|
IdentifierExp::IdentifierExp(Loc loc, Identifier *ident)
|
|
: Expression(loc, TOKidentifier, sizeof(IdentifierExp))
|
|
{
|
|
this->ident = ident;
|
|
}
|
|
|
|
Expression *IdentifierExp::semantic(Scope *sc)
|
|
{
|
|
Dsymbol *s;
|
|
Dsymbol *scopesym;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("IdentifierExp::semantic('%s')\n", ident->toChars());
|
|
#endif
|
|
s = sc->search(loc, ident, &scopesym);
|
|
if (s)
|
|
{ Expression *e;
|
|
WithScopeSymbol *withsym;
|
|
|
|
/* See if the symbol was a member of an enclosing 'with'
|
|
*/
|
|
withsym = scopesym->isWithScopeSymbol();
|
|
if (withsym)
|
|
{
|
|
#if DMDV2
|
|
/* Disallow shadowing
|
|
*/
|
|
// First find the scope of the with
|
|
Scope *scwith = sc;
|
|
while (scwith->scopesym != scopesym)
|
|
{ scwith = scwith->enclosing;
|
|
assert(scwith);
|
|
}
|
|
// Look at enclosing scopes for symbols with the same name,
|
|
// in the same function
|
|
for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing)
|
|
{ Dsymbol *s2;
|
|
|
|
if (scx->scopesym && scx->scopesym->symtab &&
|
|
(s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
|
|
s != s2)
|
|
{
|
|
error("with symbol %s is shadowing local symbol %s", s->toPrettyChars(), s2->toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
#endif
|
|
s = s->toAlias();
|
|
|
|
// Same as wthis.ident
|
|
if (s->needThis() || s->isTemplateDeclaration())
|
|
{
|
|
e = new VarExp(loc, withsym->withstate->wthis);
|
|
e = new DotIdExp(loc, e, ident);
|
|
}
|
|
else
|
|
{ Type *t = withsym->withstate->wthis->type;
|
|
if (t->ty == Tpointer)
|
|
t = ((TypePointer *)t)->next;
|
|
e = typeDotIdExp(loc, t, ident);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If f is really a function template,
|
|
* then replace f with the function template declaration.
|
|
*/
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f)
|
|
{ TemplateDeclaration *tempdecl = getFuncTemplateDecl(f);
|
|
if (tempdecl)
|
|
{
|
|
if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's
|
|
tempdecl = tempdecl->overroot; // then get the start
|
|
e = new TemplateExp(loc, tempdecl);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
// Haven't done overload resolution yet, so pass 1
|
|
e = new DsymbolExp(loc, s, 1);
|
|
}
|
|
return e->semantic(sc);
|
|
}
|
|
#if DMDV2
|
|
if (hasThis(sc))
|
|
{
|
|
AggregateDeclaration *ad = sc->getStructClassScope();
|
|
if (ad && ad->aliasthis)
|
|
{
|
|
Expression *e;
|
|
e = new IdentifierExp(loc, Id::This);
|
|
e = new DotIdExp(loc, e, ad->aliasthis->ident);
|
|
e = new DotIdExp(loc, e, ident);
|
|
e = e->trySemantic(sc);
|
|
if (e)
|
|
return e;
|
|
}
|
|
}
|
|
if (ident == Id::ctfe)
|
|
{ // Create the magic __ctfe bool variable
|
|
VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL);
|
|
Expression *e = new VarExp(loc, vd);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#endif
|
|
const char *n = importHint(ident->toChars());
|
|
if (n)
|
|
error("'%s' is not defined, perhaps you need to import %s; ?", ident->toChars(), n);
|
|
else
|
|
{
|
|
s = sc->search_correct(ident);
|
|
if (s)
|
|
error("undefined identifier %s, did you mean %s %s?", ident->toChars(), s->kind(), s->toChars());
|
|
else
|
|
error("undefined identifier %s", ident->toChars());
|
|
}
|
|
return new ErrorExp();
|
|
}
|
|
|
|
char *IdentifierExp::toChars()
|
|
{
|
|
return ident->toChars();
|
|
}
|
|
|
|
void IdentifierExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (hgs->hdrgen)
|
|
buf->writestring(ident->toHChars2());
|
|
else
|
|
buf->writestring(ident->toChars());
|
|
}
|
|
|
|
#if DMDV2
|
|
int IdentifierExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *IdentifierExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
#if 0
|
|
tym = tybasic(e1->ET->Tty);
|
|
if (!(tyscalar(tym) ||
|
|
tym == TYstruct ||
|
|
tym == TYarray && e->Eoper == TOKaddr))
|
|
synerr(EM_lvalue); // lvalue expected
|
|
#endif
|
|
return this;
|
|
}
|
|
|
|
/******************************** DollarExp **************************/
|
|
|
|
DollarExp::DollarExp(Loc loc)
|
|
: IdentifierExp(loc, Id::dollar)
|
|
{
|
|
}
|
|
|
|
/******************************** DsymbolExp **************************/
|
|
|
|
DsymbolExp::DsymbolExp(Loc loc, Dsymbol *s, int hasOverloads)
|
|
: Expression(loc, TOKdsymbol, sizeof(DsymbolExp))
|
|
{
|
|
this->s = s;
|
|
this->hasOverloads = hasOverloads;
|
|
}
|
|
|
|
AggregateDeclaration *isAggregate(Type *t);
|
|
|
|
Expression *DsymbolExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DsymbolExp::semantic('%s')\n", s->toChars());
|
|
#endif
|
|
|
|
Lagain:
|
|
EnumMember *em;
|
|
Expression *e;
|
|
VarDeclaration *v;
|
|
FuncDeclaration *f;
|
|
FuncLiteralDeclaration *fld;
|
|
OverloadSet *o;
|
|
ClassDeclaration *cd;
|
|
ClassDeclaration *thiscd = NULL;
|
|
Import *imp;
|
|
Package *pkg;
|
|
Type *t;
|
|
|
|
//printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
|
|
//printf("s = '%s', s->kind = '%s'\n", s->toChars(), s->kind());
|
|
if (type)
|
|
return this;
|
|
if (!s->isFuncDeclaration()) // functions are checked after overloading
|
|
checkDeprecated(sc, s);
|
|
Dsymbol *olds = s;
|
|
s = s->toAlias();
|
|
//printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis());
|
|
if (s != olds && !s->isFuncDeclaration())
|
|
checkDeprecated(sc, s);
|
|
|
|
if (sc->func)
|
|
thiscd = sc->func->parent->isClassDeclaration();
|
|
|
|
// BUG: This should happen after overload resolution for functions, not before
|
|
if (s->needThis())
|
|
{
|
|
if (hasThis(sc)
|
|
#if DMDV2
|
|
&& !s->isFuncDeclaration()
|
|
#endif
|
|
)
|
|
{
|
|
// Supply an implicit 'this', as in
|
|
// this.ident
|
|
|
|
DotVarExp *de;
|
|
|
|
de = new DotVarExp(loc, new ThisExp(loc), s->isDeclaration());
|
|
return de->semantic(sc);
|
|
}
|
|
}
|
|
|
|
em = s->isEnumMember();
|
|
if (em)
|
|
{
|
|
e = em->value;
|
|
e->loc = loc;
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
v = s->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
//printf("Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
|
|
if (!type)
|
|
{ if ((!v->type || !v->type->deco) && v->scope)
|
|
v->semantic(v->scope);
|
|
type = v->type;
|
|
if (!v->type)
|
|
{ error("forward reference of %s %s", v->kind(), v->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
|
|
if ((v->storage_class & STCmanifest) && v->init)
|
|
{
|
|
e = v->init->toExpression();
|
|
if (!e)
|
|
{ error("cannot make expression out of initializer for %s", v->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
e = e->copy();
|
|
e->loc = loc; // for better error message
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
e = new VarExp(loc, v);
|
|
e->type = type;
|
|
e = e->semantic(sc);
|
|
return e->deref();
|
|
}
|
|
fld = s->isFuncLiteralDeclaration();
|
|
if (fld)
|
|
{ //printf("'%s' is a function literal\n", fld->toChars());
|
|
e = new FuncExp(loc, fld);
|
|
return e->semantic(sc);
|
|
}
|
|
f = s->isFuncDeclaration();
|
|
if (f)
|
|
{ //printf("'%s' is a function\n", f->toChars());
|
|
|
|
if (!f->originalType && f->scope) // semantic not yet run
|
|
{
|
|
unsigned oldgag = global.gag;
|
|
if (global.isSpeculativeGagging() && !f->isSpeculative())
|
|
global.gag = 0;
|
|
f->semantic(f->scope);
|
|
global.gag = oldgag;
|
|
}
|
|
|
|
// if inferring return type, sematic3 needs to be run
|
|
if (f->inferRetType && f->scope && f->type && !f->type->nextOf())
|
|
{
|
|
TemplateInstance *spec = f->isSpeculative();
|
|
int olderrs = global.errors;
|
|
f->semantic3(f->scope);
|
|
// Update the template instantiation with the number
|
|
// of errors which occured.
|
|
if (spec && global.errors != olderrs)
|
|
spec->errors = global.errors - olderrs;
|
|
}
|
|
|
|
if (f->isUnitTestDeclaration())
|
|
{
|
|
error("cannot call unittest function %s", toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (!f->type->deco)
|
|
{
|
|
error("forward reference to %s", toChars());
|
|
return new ErrorExp();
|
|
}
|
|
return new VarExp(loc, f, hasOverloads);
|
|
}
|
|
o = s->isOverloadSet();
|
|
if (o)
|
|
{ //printf("'%s' is an overload set\n", o->toChars());
|
|
return new OverExp(o);
|
|
}
|
|
cd = s->isClassDeclaration();
|
|
if (cd && thiscd && cd->isBaseOf(thiscd, NULL) && sc->func->needThis())
|
|
{
|
|
// We need to add an implicit 'this' if cd is this class or a base class.
|
|
DotTypeExp *dte;
|
|
|
|
dte = new DotTypeExp(loc, new ThisExp(loc), s);
|
|
return dte->semantic(sc);
|
|
}
|
|
imp = s->isImport();
|
|
if (imp)
|
|
{
|
|
if (!imp->pkg)
|
|
{ error("forward reference of import %s", imp->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
ScopeExp *ie = new ScopeExp(loc, imp->pkg);
|
|
return ie->semantic(sc);
|
|
}
|
|
pkg = s->isPackage();
|
|
if (pkg)
|
|
{
|
|
ScopeExp *ie;
|
|
|
|
ie = new ScopeExp(loc, pkg);
|
|
return ie->semantic(sc);
|
|
}
|
|
Module *mod = s->isModule();
|
|
if (mod)
|
|
{
|
|
ScopeExp *ie;
|
|
|
|
ie = new ScopeExp(loc, mod);
|
|
return ie->semantic(sc);
|
|
}
|
|
|
|
t = s->getType();
|
|
if (t)
|
|
{
|
|
TypeExp *te = new TypeExp(loc, t);
|
|
return te->semantic(sc);
|
|
}
|
|
|
|
TupleDeclaration *tup = s->isTupleDeclaration();
|
|
if (tup)
|
|
{
|
|
e = new TupleExp(loc, tup);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
TemplateInstance *ti = s->isTemplateInstance();
|
|
if (ti)
|
|
{ if (!ti->semanticRun)
|
|
ti->semantic(sc);
|
|
s = ti->inst->toAlias();
|
|
if (!s->isTemplateInstance())
|
|
goto Lagain;
|
|
e = new ScopeExp(loc, ti);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
TemplateDeclaration *td = s->isTemplateDeclaration();
|
|
if (td)
|
|
{
|
|
Dsymbol *p = td->toParent2();
|
|
FuncDeclaration *fdthis = hasThis(sc);
|
|
AggregateDeclaration *ad = p ? p->isAggregateDeclaration() : NULL;
|
|
if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad)
|
|
{
|
|
e = new DotTemplateExp(loc, new ThisExp(loc), td);
|
|
}
|
|
else
|
|
e = new TemplateExp(loc, td);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
error("%s '%s' is not a variable", s->kind(), s->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
char *DsymbolExp::toChars()
|
|
{
|
|
return s->toChars();
|
|
}
|
|
|
|
void DsymbolExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(s->toChars());
|
|
}
|
|
|
|
#if DMDV2
|
|
int DsymbolExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *DsymbolExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
#if 0
|
|
tym = tybasic(e1->ET->Tty);
|
|
if (!(tyscalar(tym) ||
|
|
tym == TYstruct ||
|
|
tym == TYarray && e->Eoper == TOKaddr))
|
|
synerr(EM_lvalue); // lvalue expected
|
|
#endif
|
|
return this;
|
|
}
|
|
|
|
/******************************** ThisExp **************************/
|
|
|
|
ThisExp::ThisExp(Loc loc)
|
|
: Expression(loc, TOKthis, sizeof(ThisExp))
|
|
{
|
|
//printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
|
|
var = NULL;
|
|
}
|
|
|
|
Expression *ThisExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("ThisExp::semantic()\n");
|
|
#endif
|
|
if (type)
|
|
{ //assert(global.errors || var);
|
|
return this;
|
|
}
|
|
|
|
FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
|
|
|
|
/* Special case for typeof(this) and typeof(super) since both
|
|
* should work even if they are not inside a non-static member function
|
|
*/
|
|
if (!fd && sc->intypeof)
|
|
{
|
|
// Find enclosing struct or class
|
|
for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent)
|
|
{
|
|
if (!s)
|
|
{
|
|
error("%s is not in a class or struct scope", toChars());
|
|
goto Lerr;
|
|
}
|
|
ClassDeclaration *cd = s->isClassDeclaration();
|
|
if (cd)
|
|
{
|
|
type = cd->type;
|
|
return this;
|
|
}
|
|
StructDeclaration *sd = s->isStructDeclaration();
|
|
if (sd)
|
|
{
|
|
#if STRUCTTHISREF
|
|
type = sd->type;
|
|
#else
|
|
type = sd->type->pointerTo();
|
|
#endif
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
if (!fd)
|
|
goto Lerr;
|
|
|
|
assert(fd->vthis);
|
|
var = fd->vthis;
|
|
assert(var->parent);
|
|
type = var->type;
|
|
var->isVarDeclaration()->checkNestedReference(sc, loc);
|
|
if (!sc->intypeof)
|
|
sc->callSuper |= CSXthis;
|
|
return this;
|
|
|
|
Lerr:
|
|
error("'this' is only defined in non-static member functions, not %s", sc->parent->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
int ThisExp::isBool(int result)
|
|
{
|
|
return result ? TRUE : FALSE;
|
|
}
|
|
|
|
void ThisExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("this");
|
|
}
|
|
|
|
#if DMDV2
|
|
int ThisExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *ThisExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/******************************** SuperExp **************************/
|
|
|
|
SuperExp::SuperExp(Loc loc)
|
|
: ThisExp(loc)
|
|
{
|
|
op = TOKsuper;
|
|
}
|
|
|
|
Expression *SuperExp::semantic(Scope *sc)
|
|
{
|
|
ClassDeclaration *cd;
|
|
Dsymbol *s;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("SuperExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
FuncDeclaration *fd = hasThis(sc);
|
|
|
|
/* Special case for typeof(this) and typeof(super) since both
|
|
* should work even if they are not inside a non-static member function
|
|
*/
|
|
if (!fd && sc->intypeof)
|
|
{
|
|
// Find enclosing class
|
|
for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent)
|
|
{
|
|
if (!s)
|
|
{
|
|
error("%s is not in a class scope", toChars());
|
|
goto Lerr;
|
|
}
|
|
ClassDeclaration *cd = s->isClassDeclaration();
|
|
if (cd)
|
|
{
|
|
cd = cd->baseClass;
|
|
if (!cd)
|
|
{ error("class %s has no 'super'", s->toChars());
|
|
goto Lerr;
|
|
}
|
|
type = cd->type;
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
if (!fd)
|
|
goto Lerr;
|
|
|
|
assert(fd->vthis);
|
|
var = fd->vthis;
|
|
assert(var->parent);
|
|
|
|
s = fd->toParent();
|
|
while (s && s->isTemplateInstance())
|
|
s = s->toParent();
|
|
assert(s);
|
|
cd = s->isClassDeclaration();
|
|
//printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars());
|
|
if (!cd)
|
|
goto Lerr;
|
|
if (!cd->baseClass)
|
|
{
|
|
error("no base class for %s", cd->toChars());
|
|
type = fd->vthis->type;
|
|
}
|
|
else
|
|
{
|
|
type = cd->baseClass->type;
|
|
type = type->castMod(var->type->mod);
|
|
}
|
|
|
|
var->isVarDeclaration()->checkNestedReference(sc, loc);
|
|
|
|
if (!sc->intypeof)
|
|
sc->callSuper |= CSXsuper;
|
|
return this;
|
|
|
|
|
|
Lerr:
|
|
error("'super' is only allowed in non-static class member functions");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
void SuperExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("super");
|
|
}
|
|
|
|
|
|
/******************************** NullExp **************************/
|
|
|
|
NullExp::NullExp(Loc loc, Type *type)
|
|
: Expression(loc, TOKnull, sizeof(NullExp))
|
|
{
|
|
committed = 0;
|
|
this->type = type;
|
|
}
|
|
|
|
int NullExp::equals(Object *o)
|
|
{
|
|
if (o && o->dyncast() == DYNCAST_EXPRESSION)
|
|
{ Expression *e = (Expression *)o;
|
|
|
|
if (e->op == TOKnull)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
Expression *NullExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("NullExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
// NULL is the same as (void *)0
|
|
if (!type)
|
|
type = Type::tnull;
|
|
return this;
|
|
}
|
|
|
|
int NullExp::isBool(int result)
|
|
{
|
|
return result ? FALSE : TRUE;
|
|
}
|
|
|
|
StringExp *NullExp::toString()
|
|
{
|
|
if (implicitConvTo(Type::tstring))
|
|
{
|
|
StringExp *se = new StringExp(loc, (char*)mem.calloc(1, 1), 0);
|
|
se->type = Type::tstring;
|
|
return se;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void NullExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("null");
|
|
}
|
|
|
|
void NullExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
buf->writeByte('n');
|
|
}
|
|
|
|
/******************************** StringExp **************************/
|
|
|
|
StringExp::StringExp(Loc loc, char *string)
|
|
: Expression(loc, TOKstring, sizeof(StringExp))
|
|
{
|
|
this->string = string;
|
|
this->len = strlen(string);
|
|
this->sz = 1;
|
|
this->committed = 0;
|
|
this->postfix = 0;
|
|
this->ownedByCtfe = false;
|
|
}
|
|
|
|
StringExp::StringExp(Loc loc, void *string, size_t len)
|
|
: Expression(loc, TOKstring, sizeof(StringExp))
|
|
{
|
|
this->string = string;
|
|
this->len = len;
|
|
this->sz = 1;
|
|
this->committed = 0;
|
|
this->postfix = 0;
|
|
this->ownedByCtfe = false;
|
|
}
|
|
|
|
StringExp::StringExp(Loc loc, void *string, size_t len, unsigned char postfix)
|
|
: Expression(loc, TOKstring, sizeof(StringExp))
|
|
{
|
|
this->string = string;
|
|
this->len = len;
|
|
this->sz = 1;
|
|
this->committed = 0;
|
|
this->postfix = postfix;
|
|
this->ownedByCtfe = false;
|
|
}
|
|
|
|
#if 0
|
|
Expression *StringExp::syntaxCopy()
|
|
{
|
|
printf("StringExp::syntaxCopy() %s\n", toChars());
|
|
return copy();
|
|
}
|
|
#endif
|
|
|
|
int StringExp::equals(Object *o)
|
|
{
|
|
//printf("StringExp::equals('%s') %s\n", o->toChars(), toChars());
|
|
if (o && o->dyncast() == DYNCAST_EXPRESSION)
|
|
{ Expression *e = (Expression *)o;
|
|
|
|
if (e->op == TOKstring)
|
|
{
|
|
return compare(o) == 0;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
char *StringExp::toChars()
|
|
{
|
|
OutBuffer buf;
|
|
HdrGenState hgs;
|
|
char *p;
|
|
|
|
memset(&hgs, 0, sizeof(hgs));
|
|
toCBuffer(&buf, &hgs);
|
|
buf.writeByte(0);
|
|
p = (char *)buf.data;
|
|
buf.data = NULL;
|
|
return p;
|
|
}
|
|
|
|
Expression *StringExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("StringExp::semantic() %s\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{ OutBuffer buffer;
|
|
size_t newlen = 0;
|
|
const char *p;
|
|
size_t u;
|
|
unsigned c;
|
|
|
|
switch (postfix)
|
|
{
|
|
case 'd':
|
|
for (u = 0; u < len;)
|
|
{
|
|
p = utf_decodeChar((unsigned char *)string, len, &u, &c);
|
|
if (p)
|
|
{ error("%s", p);
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{ buffer.write4(c);
|
|
newlen++;
|
|
}
|
|
}
|
|
buffer.write4(0);
|
|
string = buffer.extractData();
|
|
len = newlen;
|
|
sz = 4;
|
|
//type = new TypeSArray(Type::tdchar, new IntegerExp(loc, len, Type::tindex));
|
|
type = new TypeDArray(Type::tdchar->invariantOf());
|
|
committed = 1;
|
|
break;
|
|
|
|
case 'w':
|
|
for (u = 0; u < len;)
|
|
{
|
|
p = utf_decodeChar((unsigned char *)string, len, &u, &c);
|
|
if (p)
|
|
{ error("%s", p);
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{ buffer.writeUTF16(c);
|
|
newlen++;
|
|
if (c >= 0x10000)
|
|
newlen++;
|
|
}
|
|
}
|
|
buffer.writeUTF16(0);
|
|
string = buffer.extractData();
|
|
len = newlen;
|
|
sz = 2;
|
|
//type = new TypeSArray(Type::twchar, new IntegerExp(loc, len, Type::tindex));
|
|
type = new TypeDArray(Type::twchar->invariantOf());
|
|
committed = 1;
|
|
break;
|
|
|
|
case 'c':
|
|
committed = 1;
|
|
default:
|
|
//type = new TypeSArray(Type::tchar, new IntegerExp(loc, len, Type::tindex));
|
|
type = new TypeDArray(Type::tchar->invariantOf());
|
|
break;
|
|
}
|
|
type = type->semantic(loc, sc);
|
|
//type = type->invariantOf();
|
|
//printf("type = %s\n", type->toChars());
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**********************************
|
|
* Return length of string.
|
|
*/
|
|
|
|
size_t StringExp::length()
|
|
{
|
|
size_t result = 0;
|
|
dchar_t c;
|
|
const char *p;
|
|
|
|
switch (sz)
|
|
{
|
|
case 1:
|
|
for (size_t u = 0; u < len;)
|
|
{
|
|
p = utf_decodeChar((unsigned char *)string, len, &u, &c);
|
|
if (p)
|
|
{ error("%s", p);
|
|
return 0;
|
|
}
|
|
else
|
|
result++;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
for (size_t u = 0; u < len;)
|
|
{
|
|
p = utf_decodeWchar((unsigned short *)string, len, &u, &c);
|
|
if (p)
|
|
{ error("%s", p);
|
|
return 0;
|
|
}
|
|
else
|
|
result++;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
result = len;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
StringExp *StringExp::toString()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/****************************************
|
|
* Convert string to char[].
|
|
*/
|
|
|
|
StringExp *StringExp::toUTF8(Scope *sc)
|
|
{
|
|
if (sz != 1)
|
|
{ // Convert to UTF-8 string
|
|
committed = 0;
|
|
Expression *e = castTo(sc, Type::tchar->arrayOf());
|
|
e = e->optimize(WANTvalue);
|
|
assert(e->op == TOKstring);
|
|
StringExp *se = (StringExp *)e;
|
|
assert(se->sz == 1);
|
|
return se;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
int StringExp::compare(Object *obj)
|
|
{
|
|
//printf("StringExp::compare()\n");
|
|
// Used to sort case statement expressions so we can do an efficient lookup
|
|
StringExp *se2 = (StringExp *)(obj);
|
|
|
|
// This is a kludge so isExpression() in template.c will return 5
|
|
// for StringExp's.
|
|
if (!se2)
|
|
return 5;
|
|
|
|
assert(se2->op == TOKstring);
|
|
|
|
int len1 = len;
|
|
int len2 = se2->len;
|
|
|
|
//printf("sz = %d, len1 = %d, len2 = %d\n", sz, len1, len2);
|
|
if (len1 == len2)
|
|
{
|
|
switch (sz)
|
|
{
|
|
case 1:
|
|
return memcmp((char *)string, (char *)se2->string, len1);
|
|
|
|
case 2:
|
|
{ unsigned u;
|
|
d_wchar *s1 = (d_wchar *)string;
|
|
d_wchar *s2 = (d_wchar *)se2->string;
|
|
|
|
for (u = 0; u < len; u++)
|
|
{
|
|
if (s1[u] != s2[u])
|
|
return s1[u] - s2[u];
|
|
}
|
|
}
|
|
|
|
case 4:
|
|
{ unsigned u;
|
|
d_dchar *s1 = (d_dchar *)string;
|
|
d_dchar *s2 = (d_dchar *)se2->string;
|
|
|
|
for (u = 0; u < len; u++)
|
|
{
|
|
if (s1[u] != s2[u])
|
|
return s1[u] - s2[u];
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
return len1 - len2;
|
|
}
|
|
|
|
int StringExp::isBool(int result)
|
|
{
|
|
return result ? TRUE : FALSE;
|
|
}
|
|
|
|
#if DMDV2
|
|
int StringExp::isLvalue()
|
|
{
|
|
/* string literal is rvalue in default, but
|
|
* conversion to reference of static array is only allowed.
|
|
*/
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
Expression *StringExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("StringExp::toLvalue(%s)\n", toChars());
|
|
return this;
|
|
}
|
|
|
|
Expression *StringExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
error("Cannot modify '%s'", toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
unsigned StringExp::charAt(size_t i)
|
|
{ unsigned value;
|
|
|
|
switch (sz)
|
|
{
|
|
case 1:
|
|
value = ((unsigned char *)string)[i];
|
|
break;
|
|
|
|
case 2:
|
|
value = ((unsigned short *)string)[i];
|
|
break;
|
|
|
|
case 4:
|
|
value = ((unsigned int *)string)[i];
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writeByte('"');
|
|
unsigned o = buf->offset;
|
|
for (size_t i = 0; i < len; i++)
|
|
{ unsigned c = charAt(i);
|
|
|
|
switch (c)
|
|
{
|
|
case '"':
|
|
case '\\':
|
|
if (!hgs->console)
|
|
buf->writeByte('\\');
|
|
default:
|
|
if (c <= 0xFF)
|
|
{ if (c <= 0x7F && (isprint(c) || hgs->console))
|
|
buf->writeByte(c);
|
|
else
|
|
buf->printf("\\x%02x", c);
|
|
}
|
|
else if (c <= 0xFFFF)
|
|
buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
|
|
else
|
|
buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x",
|
|
c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
|
|
break;
|
|
}
|
|
}
|
|
if (hgs->ddoc)
|
|
escapeDdocString(buf, o);
|
|
buf->writeByte('"');
|
|
if (postfix)
|
|
buf->writeByte(postfix);
|
|
}
|
|
|
|
void StringExp::toMangleBuffer(OutBuffer *buf)
|
|
{ char m;
|
|
OutBuffer tmp;
|
|
const char *p;
|
|
unsigned c;
|
|
size_t u;
|
|
unsigned char *q;
|
|
unsigned qlen;
|
|
|
|
/* Write string in UTF-8 format
|
|
*/
|
|
switch (sz)
|
|
{ case 1:
|
|
m = 'a';
|
|
q = (unsigned char *)string;
|
|
qlen = len;
|
|
break;
|
|
case 2:
|
|
m = 'w';
|
|
for (u = 0; u < len; )
|
|
{
|
|
p = utf_decodeWchar((unsigned short *)string, len, &u, &c);
|
|
if (p)
|
|
error("%s", p);
|
|
else
|
|
tmp.writeUTF8(c);
|
|
}
|
|
q = tmp.data;
|
|
qlen = tmp.offset;
|
|
break;
|
|
case 4:
|
|
m = 'd';
|
|
for (u = 0; u < len; u++)
|
|
{
|
|
c = ((unsigned *)string)[u];
|
|
if (!utf_isValidDchar(c))
|
|
error("invalid UCS-32 char \\U%08x", c);
|
|
else
|
|
tmp.writeUTF8(c);
|
|
}
|
|
q = tmp.data;
|
|
qlen = tmp.offset;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
buf->reserve(1 + 11 + 2 * qlen);
|
|
buf->writeByte(m);
|
|
buf->printf("%d_", qlen); // nbytes <= 11
|
|
|
|
for (unsigned char *p = buf->data + buf->offset, *pend = p + 2 * qlen;
|
|
p < pend; p += 2, ++q)
|
|
{
|
|
unsigned char hi = *q >> 4 & 0xF;
|
|
p[0] = (hi < 10 ? hi + '0' : hi - 10 + 'a');
|
|
unsigned char lo = *q & 0xF;
|
|
p[1] = (lo < 10 ? lo + '0' : lo - 10 + 'a');
|
|
}
|
|
buf->offset += 2 * qlen;
|
|
}
|
|
|
|
/************************ ArrayLiteralExp ************************************/
|
|
|
|
// [ e1, e2, e3, ... ]
|
|
|
|
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expressions *elements)
|
|
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
|
|
{
|
|
this->elements = elements;
|
|
this->ownedByCtfe = false;
|
|
}
|
|
|
|
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expression *e)
|
|
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
|
|
{
|
|
elements = new Expressions;
|
|
elements->push(e);
|
|
}
|
|
|
|
Expression *ArrayLiteralExp::syntaxCopy()
|
|
{
|
|
return new ArrayLiteralExp(loc, arraySyntaxCopy(elements));
|
|
}
|
|
|
|
Expression *ArrayLiteralExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("ArrayLiteralExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
/* Perhaps an empty array literal [ ] should be rewritten as null?
|
|
*/
|
|
|
|
arrayExpressionSemantic(elements, sc); // run semantic() on each element
|
|
expandTuples(elements);
|
|
|
|
Type *t0;
|
|
elements = arrayExpressionToCommonType(sc, elements, &t0);
|
|
|
|
type = t0->arrayOf();
|
|
//type = new TypeSArray(t0, new IntegerExp(elements->dim));
|
|
type = type->semantic(loc, sc);
|
|
|
|
/* Disallow array literals of type void being used.
|
|
*/
|
|
if (elements->dim > 0 && t0->ty == Tvoid)
|
|
{ error("%s of type %s has no value", toChars(), type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
int ArrayLiteralExp::isBool(int result)
|
|
{
|
|
size_t dim = elements ? elements->dim : 0;
|
|
return result ? (dim != 0) : (dim == 0);
|
|
}
|
|
|
|
StringExp *ArrayLiteralExp::toString()
|
|
{
|
|
TY telem = type->nextOf()->toBasetype()->ty;
|
|
|
|
if (telem == Tchar || telem == Twchar || telem == Tdchar ||
|
|
(telem == Tvoid && (!elements || elements->dim == 0)))
|
|
{
|
|
OutBuffer buf;
|
|
if (elements)
|
|
for (int i = 0; i < elements->dim; ++i)
|
|
{
|
|
Expression *ch = elements->tdata()[i];
|
|
if (ch->op != TOKint64)
|
|
return NULL;
|
|
buf.writedchar(ch->toInteger());
|
|
}
|
|
buf.writebyte(0);
|
|
|
|
char prefix = 'c';
|
|
if (telem == Twchar) prefix = 'w';
|
|
else if (telem == Tdchar) prefix = 'd';
|
|
|
|
StringExp *se = new StringExp(loc, buf.extractData(), buf.size - 1, prefix);
|
|
se->type = type;
|
|
return se;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void ArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writeByte('[');
|
|
argsToCBuffer(buf, elements, hgs);
|
|
buf->writeByte(']');
|
|
}
|
|
|
|
void ArrayLiteralExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
size_t dim = elements ? elements->dim : 0;
|
|
buf->printf("A%u", dim);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Expression *e = elements->tdata()[i];
|
|
e->toMangleBuffer(buf);
|
|
}
|
|
}
|
|
|
|
/************************ AssocArrayLiteralExp ************************************/
|
|
|
|
// [ key0 : value0, key1 : value1, ... ]
|
|
|
|
AssocArrayLiteralExp::AssocArrayLiteralExp(Loc loc,
|
|
Expressions *keys, Expressions *values)
|
|
: Expression(loc, TOKassocarrayliteral, sizeof(AssocArrayLiteralExp))
|
|
{
|
|
assert(keys->dim == values->dim);
|
|
this->keys = keys;
|
|
this->values = values;
|
|
this->ownedByCtfe = false;
|
|
}
|
|
|
|
Expression *AssocArrayLiteralExp::syntaxCopy()
|
|
{
|
|
return new AssocArrayLiteralExp(loc,
|
|
arraySyntaxCopy(keys), arraySyntaxCopy(values));
|
|
}
|
|
|
|
Expression *AssocArrayLiteralExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("AssocArrayLiteralExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
// Run semantic() on each element
|
|
arrayExpressionSemantic(keys, sc);
|
|
arrayExpressionSemantic(values, sc);
|
|
expandTuples(keys);
|
|
expandTuples(values);
|
|
if (keys->dim != values->dim)
|
|
{
|
|
error("number of keys is %u, must match number of values %u", keys->dim, values->dim);
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Type *tkey = NULL;
|
|
Type *tvalue = NULL;
|
|
keys = arrayExpressionToCommonType(sc, keys, &tkey);
|
|
values = arrayExpressionToCommonType(sc, values, &tvalue);
|
|
|
|
if (tkey == Type::terror || tvalue == Type::terror)
|
|
return new ErrorExp;
|
|
|
|
type = new TypeAArray(tvalue, tkey);
|
|
type = type->semantic(loc, sc);
|
|
return this;
|
|
}
|
|
|
|
|
|
int AssocArrayLiteralExp::isBool(int result)
|
|
{
|
|
size_t dim = keys->dim;
|
|
return result ? (dim != 0) : (dim == 0);
|
|
}
|
|
|
|
void AssocArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writeByte('[');
|
|
for (size_t i = 0; i < keys->dim; i++)
|
|
{ Expression *key = keys->tdata()[i];
|
|
Expression *value = values->tdata()[i];
|
|
|
|
if (i)
|
|
buf->writeByte(',');
|
|
expToCBuffer(buf, hgs, key, PREC_assign);
|
|
buf->writeByte(':');
|
|
expToCBuffer(buf, hgs, value, PREC_assign);
|
|
}
|
|
buf->writeByte(']');
|
|
}
|
|
|
|
void AssocArrayLiteralExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
size_t dim = keys->dim;
|
|
buf->printf("A%u", dim);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Expression *key = keys->tdata()[i];
|
|
Expression *value = values->tdata()[i];
|
|
|
|
key->toMangleBuffer(buf);
|
|
value->toMangleBuffer(buf);
|
|
}
|
|
}
|
|
|
|
/************************ StructLiteralExp ************************************/
|
|
|
|
// sd( e1, e2, e3, ... )
|
|
|
|
StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype)
|
|
: Expression(loc, TOKstructliteral, sizeof(StructLiteralExp))
|
|
{
|
|
this->sd = sd;
|
|
this->elements = elements;
|
|
this->stype = stype;
|
|
this->sinit = NULL;
|
|
this->sym = NULL;
|
|
this->soffset = 0;
|
|
this->fillHoles = 1;
|
|
this->ownedByCtfe = false;
|
|
//printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
|
|
}
|
|
|
|
Expression *StructLiteralExp::syntaxCopy()
|
|
{
|
|
return new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), stype);
|
|
}
|
|
|
|
Expression *StructLiteralExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("StructLiteralExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
sd->size(loc);
|
|
if (sd->sizeok != SIZEOKdone)
|
|
return new ErrorExp();
|
|
size_t nfields = sd->fields.dim - sd->isnested;
|
|
|
|
elements = arrayExpressionSemantic(elements, sc); // run semantic() on each element
|
|
expandTuples(elements);
|
|
size_t offset = 0;
|
|
for (size_t i = 0; i < elements->dim; i++)
|
|
{ e = elements->tdata()[i];
|
|
if (!e)
|
|
continue;
|
|
|
|
e = resolveProperties(sc, e);
|
|
if (i >= nfields)
|
|
{
|
|
#if 0
|
|
for (size_t i = 0; i < sd->fields.dim; i++)
|
|
printf("[%d] = %s\n", i, sd->fields[i]->toChars());
|
|
#endif
|
|
error("more initializers than fields (%d) of %s", nfields, sd->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
Dsymbol *s = sd->fields.tdata()[i];
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(v);
|
|
if (v->offset < offset)
|
|
{ error("overlapping initialization for %s", v->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
offset = v->offset + v->type->size();
|
|
|
|
Type *telem = v->type;
|
|
if (stype)
|
|
telem = telem->addMod(stype->mod);
|
|
while (!e->implicitConvTo(telem) && telem->toBasetype()->ty == Tsarray)
|
|
{ /* Static array initialization, as in:
|
|
* T[3][5] = e;
|
|
*/
|
|
telem = telem->toBasetype()->nextOf();
|
|
}
|
|
|
|
e = e->implicitCastTo(sc, telem);
|
|
|
|
elements->tdata()[i] = e;
|
|
}
|
|
|
|
/* Fill out remainder of elements[] with default initializers for fields[]
|
|
*/
|
|
for (size_t i = elements->dim; i < nfields; i++)
|
|
{ Dsymbol *s = sd->fields.tdata()[i];
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(v);
|
|
assert(!v->isThisDeclaration());
|
|
|
|
if (v->offset < offset)
|
|
{ e = NULL;
|
|
sd->hasUnions = 1;
|
|
}
|
|
else
|
|
{
|
|
if (v->init)
|
|
{ if (v->init->isVoidInitializer())
|
|
e = NULL;
|
|
else
|
|
{ e = v->init->toExpression();
|
|
if (!e)
|
|
{ error("cannot make expression out of initializer for %s", v->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else if (v->scope)
|
|
{ // Do deferred semantic analysis
|
|
Initializer *i2 = v->init->syntaxCopy();
|
|
i2 = i2->semantic(v->scope, v->type, WANTinterpret);
|
|
e = i2->toExpression();
|
|
// remove v->scope (see bug 3426)
|
|
// but not if gagged, for we might be called again.
|
|
if (!global.gag)
|
|
v->scope = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
e = v->type->defaultInitLiteral(loc);
|
|
offset = v->offset + v->type->size();
|
|
}
|
|
elements->push(e);
|
|
}
|
|
|
|
type = stype ? stype : sd->type;
|
|
|
|
/* If struct requires a destructor, rewrite as:
|
|
* (S tmp = S()),tmp
|
|
* so that the destructor can be hung on tmp.
|
|
*/
|
|
if (sd->dtor && sc->func)
|
|
{
|
|
Identifier *idtmp = Lexer::uniqueId("__sl");
|
|
VarDeclaration *tmp = new VarDeclaration(loc, type, idtmp, new ExpInitializer(0, this));
|
|
tmp->storage_class |= STCctfe;
|
|
Expression *ae = new DeclarationExp(loc, tmp);
|
|
Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp));
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**************************************
|
|
* Gets expression at offset of type.
|
|
* Returns NULL if not found.
|
|
*/
|
|
|
|
Expression *StructLiteralExp::getField(Type *type, unsigned offset)
|
|
{
|
|
//printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
|
|
// /*toChars()*/"", type->toChars(), offset);
|
|
Expression *e = NULL;
|
|
int i = getFieldIndex(type, offset);
|
|
|
|
if (i != -1)
|
|
{
|
|
//printf("\ti = %d\n", i);
|
|
assert(i < elements->dim);
|
|
e = elements->tdata()[i];
|
|
if (e)
|
|
{
|
|
//printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars());
|
|
|
|
/* If type is a static array, and e is an initializer for that array,
|
|
* then the field initializer should be an array literal of e.
|
|
*/
|
|
if (e->type->castMod(0) != type->castMod(0) && type->ty == Tsarray)
|
|
{ TypeSArray *tsa = (TypeSArray *)type;
|
|
uinteger_t length = tsa->dim->toInteger();
|
|
Expressions *z = new Expressions;
|
|
z->setDim(length);
|
|
for (int q = 0; q < length; ++q)
|
|
z->tdata()[q] = e->copy();
|
|
e = new ArrayLiteralExp(loc, z);
|
|
e->type = type;
|
|
}
|
|
else
|
|
{
|
|
e = e->copy();
|
|
e->type = type;
|
|
}
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/************************************
|
|
* Get index of field.
|
|
* Returns -1 if not found.
|
|
*/
|
|
|
|
int StructLiteralExp::getFieldIndex(Type *type, unsigned offset)
|
|
{
|
|
/* Find which field offset is by looking at the field offsets
|
|
*/
|
|
if (elements->dim)
|
|
{
|
|
for (size_t i = 0; i < sd->fields.dim; i++)
|
|
{
|
|
Dsymbol *s = sd->fields.tdata()[i];
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(v);
|
|
|
|
if (offset == v->offset &&
|
|
type->size() == v->type->size())
|
|
{ Expression *e = elements->tdata()[i];
|
|
if (e)
|
|
{
|
|
return i;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void StructLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(sd->toChars());
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, elements, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
void StructLiteralExp::toMangleBuffer(OutBuffer *buf)
|
|
{
|
|
size_t dim = elements ? elements->dim : 0;
|
|
buf->printf("S%u", dim);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Expression *e = elements->tdata()[i];
|
|
if (e)
|
|
e->toMangleBuffer(buf);
|
|
else
|
|
buf->writeByte('v'); // 'v' for void
|
|
}
|
|
}
|
|
|
|
/************************ TypeDotIdExp ************************************/
|
|
|
|
/* Things like:
|
|
* int.size
|
|
* foo.size
|
|
* (foo).size
|
|
* cast(foo).size
|
|
*/
|
|
|
|
Expression *typeDotIdExp(Loc loc, Type *type, Identifier *ident)
|
|
{
|
|
return new DotIdExp(loc, new TypeExp(loc, type), ident);
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
// Mainly just a placeholder
|
|
|
|
TypeExp::TypeExp(Loc loc, Type *type)
|
|
: Expression(loc, TOKtype, sizeof(TypeExp))
|
|
{
|
|
//printf("TypeExp::TypeExp(%s)\n", type->toChars());
|
|
this->type = type;
|
|
}
|
|
|
|
Expression *TypeExp::syntaxCopy()
|
|
{
|
|
//printf("TypeExp::syntaxCopy()\n");
|
|
return new TypeExp(loc, type->syntaxCopy());
|
|
}
|
|
|
|
Expression *TypeExp::semantic(Scope *sc)
|
|
{
|
|
//printf("TypeExp::semantic(%s)\n", type->toChars());
|
|
type = type->semantic(loc, sc);
|
|
return this;
|
|
}
|
|
|
|
int TypeExp::rvalue()
|
|
{
|
|
error("type %s has no value", toChars());
|
|
return 0;
|
|
}
|
|
|
|
void TypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
type->toCBuffer(buf, NULL, hgs);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
// Mainly just a placeholder
|
|
|
|
ScopeExp::ScopeExp(Loc loc, ScopeDsymbol *pkg)
|
|
: Expression(loc, TOKimport, sizeof(ScopeExp))
|
|
{
|
|
//printf("ScopeExp::ScopeExp(pkg = '%s')\n", pkg->toChars());
|
|
//static int count; if (++count == 38) *(char*)0=0;
|
|
this->sds = pkg;
|
|
}
|
|
|
|
Expression *ScopeExp::syntaxCopy()
|
|
{
|
|
ScopeExp *se = new ScopeExp(loc, (ScopeDsymbol *)sds->syntaxCopy(NULL));
|
|
return se;
|
|
}
|
|
|
|
Expression *ScopeExp::semantic(Scope *sc)
|
|
{
|
|
TemplateInstance *ti;
|
|
ScopeDsymbol *sds2;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("+ScopeExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
Lagain:
|
|
ti = sds->isTemplateInstance();
|
|
if (ti && !ti->errors)
|
|
{
|
|
unsigned olderrs = global.errors;
|
|
if (!ti->semanticRun)
|
|
ti->semantic(sc);
|
|
if (ti->inst)
|
|
{
|
|
Dsymbol *s = ti->inst->toAlias();
|
|
sds2 = s->isScopeDsymbol();
|
|
if (!sds2)
|
|
{ Expression *e;
|
|
|
|
//printf("s = %s, '%s'\n", s->kind(), s->toChars());
|
|
if (ti->withsym)
|
|
{
|
|
// Same as wthis.s
|
|
e = new VarExp(loc, ti->withsym->withstate->wthis);
|
|
e = new DotVarExp(loc, e, s->isDeclaration());
|
|
}
|
|
else
|
|
e = new DsymbolExp(loc, s);
|
|
e = e->semantic(sc);
|
|
//printf("-1ScopeExp::semantic()\n");
|
|
return e;
|
|
}
|
|
if (sds2 != sds)
|
|
{
|
|
sds = sds2;
|
|
goto Lagain;
|
|
}
|
|
//printf("sds = %s, '%s'\n", sds->kind(), sds->toChars());
|
|
}
|
|
if (olderrs != global.errors)
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
//printf("sds = %s, '%s'\n", sds->kind(), sds->toChars());
|
|
//printf("\tparent = '%s'\n", sds->parent->toChars());
|
|
sds->semantic(sc);
|
|
|
|
AggregateDeclaration *ad = sds->isAggregateDeclaration();
|
|
if (ad)
|
|
return (new TypeExp(loc, ad->type))->semantic(sc);
|
|
}
|
|
type = Type::tvoid;
|
|
//printf("-2ScopeExp::semantic() %s\n", toChars());
|
|
return this;
|
|
}
|
|
|
|
void ScopeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (sds->isTemplateInstance())
|
|
{
|
|
sds->toCBuffer(buf, hgs);
|
|
}
|
|
else if (hgs != NULL && hgs->ddoc)
|
|
{ // fixes bug 6491
|
|
Module *module = sds->isModule();
|
|
if (module)
|
|
buf->writestring(module->md->toChars());
|
|
else
|
|
buf->writestring(sds->toChars());
|
|
}
|
|
else
|
|
{
|
|
buf->writestring(sds->kind());
|
|
buf->writestring(" ");
|
|
buf->writestring(sds->toChars());
|
|
}
|
|
}
|
|
|
|
/********************** TemplateExp **************************************/
|
|
|
|
// Mainly just a placeholder
|
|
|
|
TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td)
|
|
: Expression(loc, TOKtemplate, sizeof(TemplateExp))
|
|
{
|
|
//printf("TemplateExp(): %s\n", td->toChars());
|
|
this->td = td;
|
|
}
|
|
|
|
void TemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(td->toChars());
|
|
}
|
|
|
|
int TemplateExp::rvalue()
|
|
{
|
|
error("template %s has no value", toChars());
|
|
return 0;
|
|
}
|
|
|
|
/********************** NewExp **************************************/
|
|
|
|
/* thisexp.new(newargs) newtype(arguments) */
|
|
|
|
NewExp::NewExp(Loc loc, Expression *thisexp, Expressions *newargs,
|
|
Type *newtype, Expressions *arguments)
|
|
: Expression(loc, TOKnew, sizeof(NewExp))
|
|
{
|
|
this->thisexp = thisexp;
|
|
this->newargs = newargs;
|
|
this->newtype = newtype;
|
|
this->arguments = arguments;
|
|
member = NULL;
|
|
allocator = NULL;
|
|
onstack = 0;
|
|
}
|
|
|
|
Expression *NewExp::syntaxCopy()
|
|
{
|
|
return new NewExp(loc,
|
|
thisexp ? thisexp->syntaxCopy() : NULL,
|
|
arraySyntaxCopy(newargs),
|
|
newtype->syntaxCopy(), arraySyntaxCopy(arguments));
|
|
}
|
|
|
|
|
|
Expression *NewExp::semantic(Scope *sc)
|
|
{
|
|
Type *tb;
|
|
ClassDeclaration *cdthis = NULL;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("NewExp::semantic() %s\n", toChars());
|
|
if (thisexp)
|
|
printf("\tthisexp = %s\n", thisexp->toChars());
|
|
printf("\tnewtype: %s\n", newtype->toChars());
|
|
#endif
|
|
if (type) // if semantic() already run
|
|
return this;
|
|
|
|
Lagain:
|
|
if (thisexp)
|
|
{ thisexp = thisexp->semantic(sc);
|
|
cdthis = thisexp->type->isClassHandle();
|
|
if (cdthis)
|
|
{
|
|
sc = sc->push(cdthis);
|
|
type = newtype->semantic(loc, sc);
|
|
sc = sc->pop();
|
|
}
|
|
else
|
|
{
|
|
error("'this' for nested class must be a class type, not %s", thisexp->type->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
else
|
|
type = newtype->semantic(loc, sc);
|
|
newtype = type; // in case type gets cast to something else
|
|
tb = type->toBasetype();
|
|
//printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco);
|
|
|
|
arrayExpressionSemantic(newargs, sc);
|
|
preFunctionParameters(loc, sc, newargs);
|
|
arrayExpressionSemantic(arguments, sc);
|
|
preFunctionParameters(loc, sc, arguments);
|
|
|
|
if (thisexp && tb->ty != Tclass)
|
|
{ error("e.new is only for allocating nested classes, not %s", tb->toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
if (tb->ty == Tclass)
|
|
{
|
|
TypeClass *tc = (TypeClass *)(tb);
|
|
ClassDeclaration *cd = tc->sym->isClassDeclaration();
|
|
if (cd->isInterfaceDeclaration())
|
|
{ error("cannot create instance of interface %s", cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
else if (cd->isAbstract())
|
|
{ error("cannot create instance of abstract class %s", cd->toChars());
|
|
for (size_t i = 0; i < cd->vtbl.dim; i++)
|
|
{ FuncDeclaration *fd = cd->vtbl.tdata()[i]->isFuncDeclaration();
|
|
if (fd && fd->isAbstract())
|
|
error("function %s is abstract", fd->toChars());
|
|
}
|
|
goto Lerr;
|
|
}
|
|
|
|
if (cd->noDefaultCtor && (!arguments || !arguments->dim))
|
|
{ error("default construction is disabled for type %s", cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
checkDeprecated(sc, cd);
|
|
if (cd->isNested())
|
|
{ /* We need a 'this' pointer for the nested class.
|
|
* Ensure we have the right one.
|
|
*/
|
|
Dsymbol *s = cd->toParent2();
|
|
ClassDeclaration *cdn = s->isClassDeclaration();
|
|
FuncDeclaration *fdn = s->isFuncDeclaration();
|
|
|
|
//printf("cd isNested, cdn = %s\n", cdn ? cdn->toChars() : "null");
|
|
if (cdn)
|
|
{
|
|
if (!cdthis)
|
|
{
|
|
// Supply an implicit 'this' and try again
|
|
thisexp = new ThisExp(loc);
|
|
for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
|
|
{ if (!sp)
|
|
{
|
|
error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
ClassDeclaration *cdp = sp->isClassDeclaration();
|
|
if (!cdp)
|
|
continue;
|
|
if (cdp == cdn || cdn->isBaseOf(cdp, NULL))
|
|
break;
|
|
// Add a '.outer' and try again
|
|
thisexp = new DotIdExp(loc, thisexp, Id::outer);
|
|
}
|
|
if (!global.errors)
|
|
goto Lagain;
|
|
}
|
|
if (cdthis)
|
|
{
|
|
//printf("cdthis = %s\n", cdthis->toChars());
|
|
if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL))
|
|
{ error("'this' for nested class must be of type %s, not %s", cdn->toChars(), thisexp->type->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
for (Dsymbol *sf = sc->func; 1; sf= sf->toParent2()->isFuncDeclaration())
|
|
{
|
|
if (!sf)
|
|
{
|
|
error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
printf("sf = %s\n", sf->toChars());
|
|
AggregateDeclaration *ad = sf->isThis();
|
|
if (ad && (ad == cdn || cdn->isBaseOf(ad->isClassDeclaration(), NULL)))
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#if 1
|
|
else if (thisexp)
|
|
{ error("e.new is only for allocating nested classes");
|
|
goto Lerr;
|
|
}
|
|
else if (fdn)
|
|
{
|
|
// make sure the parent context fdn of cd is reachable from sc
|
|
for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
|
|
{
|
|
if (fdn == sp)
|
|
break;
|
|
FuncDeclaration *fsp = sp ? sp->isFuncDeclaration() : NULL;
|
|
if (!sp || (fsp && fsp->isStatic()))
|
|
{
|
|
error("outer function context of %s is needed to 'new' nested class %s", fdn->toPrettyChars(), cd->toPrettyChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
else if (fdn)
|
|
{ /* The nested class cd is nested inside a function,
|
|
* we'll let getEthis() look for errors.
|
|
*/
|
|
//printf("nested class %s is nested inside function %s, we're in %s\n", cd->toChars(), fdn->toChars(), sc->func->toChars());
|
|
if (thisexp)
|
|
{ // Because thisexp cannot be a function frame pointer
|
|
error("e.new is only for allocating nested classes");
|
|
goto Lerr;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
assert(0);
|
|
}
|
|
else if (thisexp)
|
|
{ error("e.new is only for allocating nested classes");
|
|
goto Lerr;
|
|
}
|
|
|
|
FuncDeclaration *f = NULL;
|
|
if (cd->ctor)
|
|
f = resolveFuncCall(sc, loc, cd->ctor, NULL, NULL, arguments, 0);
|
|
if (f)
|
|
{
|
|
checkDeprecated(sc, f);
|
|
member = f->isCtorDeclaration();
|
|
assert(member);
|
|
|
|
cd->accessCheck(loc, sc, member);
|
|
|
|
TypeFunction *tf = (TypeFunction *)f->type;
|
|
|
|
if (!arguments)
|
|
arguments = new Expressions();
|
|
unsigned olderrors = global.errors;
|
|
functionParameters(loc, sc, tf, NULL, arguments, f);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
type = type->addMod(tf->nextOf()->mod);
|
|
}
|
|
else
|
|
{
|
|
if (arguments && arguments->dim)
|
|
{ error("no constructor for %s", cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
|
|
if (cd->aggNew)
|
|
{
|
|
// Prepend the size argument to newargs[]
|
|
Expression *e = new IntegerExp(loc, cd->size(loc), Type::tsize_t);
|
|
if (!newargs)
|
|
newargs = new Expressions();
|
|
newargs->shift(e);
|
|
|
|
f = cd->aggNew->overloadResolve(loc, NULL, newargs);
|
|
allocator = f->isNewDeclaration();
|
|
assert(allocator);
|
|
|
|
TypeFunction *tf = (TypeFunction *)f->type;
|
|
unsigned olderrors = global.errors;
|
|
functionParameters(loc, sc, tf, NULL, newargs, f);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
|
|
}
|
|
else
|
|
{
|
|
if (newargs && newargs->dim)
|
|
{ error("no allocator for %s", cd->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
}
|
|
else if (tb->ty == Tstruct)
|
|
{
|
|
TypeStruct *ts = (TypeStruct *)tb;
|
|
StructDeclaration *sd = ts->sym;
|
|
TypeFunction *tf;
|
|
|
|
if (sd->noDefaultCtor && (!arguments || !arguments->dim))
|
|
{ error("default construction is disabled for type %s", sd->toChars());
|
|
goto Lerr;
|
|
}
|
|
FuncDeclaration *f = NULL;
|
|
if (sd->ctor)
|
|
f = resolveFuncCall(sc, loc, sd->ctor, NULL, NULL, arguments, 0);
|
|
if (f)
|
|
{
|
|
checkDeprecated(sc, f);
|
|
member = f->isCtorDeclaration();
|
|
assert(member);
|
|
|
|
sd->accessCheck(loc, sc, member);
|
|
|
|
tf = (TypeFunction *)f->type;
|
|
type = tf->next;
|
|
|
|
if (!arguments)
|
|
arguments = new Expressions();
|
|
unsigned olderrors = global.errors;
|
|
functionParameters(loc, sc, tf, NULL, arguments, f);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
|
|
}
|
|
else
|
|
{
|
|
if (arguments && arguments->dim)
|
|
{ error("no constructor for %s", sd->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
|
|
|
|
if (sd->aggNew)
|
|
{
|
|
// Prepend the uint size argument to newargs[]
|
|
Expression *e = new IntegerExp(loc, sd->size(loc), Type::tuns32);
|
|
if (!newargs)
|
|
newargs = new Expressions();
|
|
newargs->shift(e);
|
|
|
|
f = sd->aggNew->overloadResolve(loc, NULL, newargs);
|
|
allocator = f->isNewDeclaration();
|
|
assert(allocator);
|
|
|
|
tf = (TypeFunction *)f->type;
|
|
unsigned olderrors = global.errors;
|
|
functionParameters(loc, sc, tf, NULL, newargs, f);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
|
|
#if 0
|
|
e = new VarExp(loc, f);
|
|
e = new CallExp(loc, e, newargs);
|
|
e = e->semantic(sc);
|
|
e->type = type->pointerTo();
|
|
return e;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (newargs && newargs->dim)
|
|
{ error("no allocator for %s", sd->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
|
|
type = type->pointerTo();
|
|
}
|
|
else if (tb->ty == Tarray && (arguments && arguments->dim))
|
|
{
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{
|
|
if (tb->ty != Tarray)
|
|
{ error("too many arguments for array");
|
|
goto Lerr;
|
|
}
|
|
|
|
Expression *arg = arguments->tdata()[i];
|
|
arg = resolveProperties(sc, arg);
|
|
arg = arg->implicitCastTo(sc, Type::tsize_t);
|
|
arg = arg->optimize(WANTvalue);
|
|
if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0)
|
|
{ error("negative array index %s", arg->toChars());
|
|
goto Lerr;
|
|
}
|
|
arguments->tdata()[i] = arg;
|
|
tb = ((TypeDArray *)tb)->next->toBasetype();
|
|
}
|
|
}
|
|
else if (tb->isscalar())
|
|
{
|
|
if (arguments && arguments->dim)
|
|
{ error("no constructor for %s", type->toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
type = type->pointerTo();
|
|
}
|
|
else
|
|
{
|
|
error("new can only create structs, dynamic arrays or class objects, not %s's", type->toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
//printf("NewExp: '%s'\n", toChars());
|
|
//printf("NewExp:type '%s'\n", type->toChars());
|
|
|
|
return this;
|
|
|
|
Lerr:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
|
|
void NewExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (thisexp)
|
|
{ expToCBuffer(buf, hgs, thisexp, PREC_primary);
|
|
buf->writeByte('.');
|
|
}
|
|
buf->writestring("new ");
|
|
if (newargs && newargs->dim)
|
|
{
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, newargs, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
newtype->toCBuffer(buf, NULL, hgs);
|
|
if (arguments && arguments->dim)
|
|
{
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, arguments, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
}
|
|
|
|
/********************** NewAnonClassExp **************************************/
|
|
|
|
NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp,
|
|
Expressions *newargs, ClassDeclaration *cd, Expressions *arguments)
|
|
: Expression(loc, TOKnewanonclass, sizeof(NewAnonClassExp))
|
|
{
|
|
this->thisexp = thisexp;
|
|
this->newargs = newargs;
|
|
this->cd = cd;
|
|
this->arguments = arguments;
|
|
}
|
|
|
|
Expression *NewAnonClassExp::syntaxCopy()
|
|
{
|
|
return new NewAnonClassExp(loc,
|
|
thisexp ? thisexp->syntaxCopy() : NULL,
|
|
arraySyntaxCopy(newargs),
|
|
(ClassDeclaration *)cd->syntaxCopy(NULL),
|
|
arraySyntaxCopy(arguments));
|
|
}
|
|
|
|
|
|
Expression *NewAnonClassExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("NewAnonClassExp::semantic() %s\n", toChars());
|
|
//printf("thisexp = %p\n", thisexp);
|
|
//printf("type: %s\n", type->toChars());
|
|
#endif
|
|
|
|
Expression *d = new DeclarationExp(loc, cd);
|
|
d = d->semantic(sc);
|
|
|
|
Expression *n = new NewExp(loc, thisexp, newargs, cd->type, arguments);
|
|
|
|
Expression *c = new CommaExp(loc, d, n);
|
|
return c->semantic(sc);
|
|
}
|
|
|
|
|
|
void NewAnonClassExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (thisexp)
|
|
{ expToCBuffer(buf, hgs, thisexp, PREC_primary);
|
|
buf->writeByte('.');
|
|
}
|
|
buf->writestring("new");
|
|
if (newargs && newargs->dim)
|
|
{
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, newargs, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
buf->writestring(" class ");
|
|
if (arguments && arguments->dim)
|
|
{
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, arguments, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
//buf->writestring(" { }");
|
|
if (cd)
|
|
{
|
|
cd->toCBuffer(buf, hgs);
|
|
}
|
|
}
|
|
|
|
/********************** SymbolExp **************************************/
|
|
|
|
#if DMDV2
|
|
SymbolExp::SymbolExp(Loc loc, enum TOK op, int size, Declaration *var, int hasOverloads)
|
|
: Expression(loc, op, size)
|
|
{
|
|
assert(var);
|
|
this->var = var;
|
|
this->hasOverloads = hasOverloads;
|
|
}
|
|
#endif
|
|
|
|
/********************** SymOffExp **************************************/
|
|
|
|
SymOffExp::SymOffExp(Loc loc, Declaration *var, unsigned offset, int hasOverloads)
|
|
: SymbolExp(loc, TOKsymoff, sizeof(SymOffExp), var, hasOverloads)
|
|
{
|
|
this->offset = offset;
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v && v->needThis())
|
|
error("need 'this' for address of %s", v->toChars());
|
|
}
|
|
|
|
Expression *SymOffExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("SymOffExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
//var->semantic(sc);
|
|
if (!type)
|
|
type = var->type->pointerTo();
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v)
|
|
v->checkNestedReference(sc, loc);
|
|
FuncDeclaration *f = var->isFuncDeclaration();
|
|
if (f)
|
|
f->checkNestedReference(sc, loc);
|
|
return this;
|
|
}
|
|
|
|
int SymOffExp::isBool(int result)
|
|
{
|
|
return result ? TRUE : FALSE;
|
|
}
|
|
|
|
void SymOffExp::checkEscape()
|
|
{
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
if (!v->isDataseg() && !(v->storage_class & (STCref | STCout)))
|
|
{ /* BUG: This should be allowed:
|
|
* void foo()
|
|
* { int a;
|
|
* int* bar() { return &a; }
|
|
* }
|
|
*/
|
|
error("escaping reference to local %s", v->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymOffExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (offset)
|
|
buf->printf("(& %s+%u)", var->toChars(), offset);
|
|
else
|
|
buf->printf("& %s", var->toChars());
|
|
}
|
|
|
|
/******************************** VarExp **************************/
|
|
|
|
VarExp::VarExp(Loc loc, Declaration *var, int hasOverloads)
|
|
: SymbolExp(loc, TOKvar, sizeof(VarExp), var, hasOverloads)
|
|
{
|
|
//printf("VarExp(this = %p, '%s', loc = %s)\n", this, var->toChars(), loc.toChars());
|
|
//if (strcmp(var->ident->toChars(), "func") == 0) halt();
|
|
this->type = var->type;
|
|
}
|
|
|
|
int VarExp::equals(Object *o)
|
|
{ VarExp *ne;
|
|
|
|
if (this == o ||
|
|
(((Expression *)o)->op == TOKvar &&
|
|
((ne = (VarExp *)o), type->toHeadMutable()->equals(ne->type->toHeadMutable())) &&
|
|
var == ne->var))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
Expression *VarExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("VarExp::semantic(%s)\n", toChars());
|
|
#endif
|
|
// if (var->sem == SemanticStart && var->scope) // if forward referenced
|
|
// var->semantic(sc);
|
|
if (!type)
|
|
{ type = var->type;
|
|
#if 0
|
|
if (var->storage_class & STClazy)
|
|
{
|
|
TypeFunction *tf = new TypeFunction(NULL, type, 0, LINKd);
|
|
type = new TypeDelegate(tf);
|
|
type = type->semantic(loc, sc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (type && !type->deco)
|
|
type = type->semantic(loc, sc);
|
|
|
|
/* Fix for 1161 doesn't work because it causes protection
|
|
* problems when instantiating imported templates passing private
|
|
* variables as alias template parameters.
|
|
*/
|
|
//accessCheck(loc, sc, NULL, var);
|
|
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
v->checkNestedReference(sc, loc);
|
|
#if DMDV2
|
|
checkPurity(sc, v, NULL);
|
|
#endif
|
|
}
|
|
FuncDeclaration *f = var->isFuncDeclaration();
|
|
if (f)
|
|
f->checkNestedReference(sc, loc);
|
|
#if 0
|
|
else if ((fd = var->isFuncLiteralDeclaration()) != NULL)
|
|
{ Expression *e;
|
|
e = new FuncExp(loc, fd);
|
|
e->type = type;
|
|
return e;
|
|
}
|
|
#endif
|
|
|
|
return this;
|
|
}
|
|
|
|
char *VarExp::toChars()
|
|
{
|
|
return var->toChars();
|
|
}
|
|
|
|
void VarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(var->toChars());
|
|
}
|
|
|
|
void VarExp::checkEscape()
|
|
{
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v)
|
|
{ Type *tb = v->type->toBasetype();
|
|
// if reference type
|
|
if (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tclass || tb->ty == Tdelegate)
|
|
{
|
|
if (v->isScope() && (!v->noscope || tb->ty == Tclass))
|
|
error("escaping reference to scope local %s", v->toChars());
|
|
else if (v->storage_class & STCvariadic)
|
|
error("escaping reference to variadic parameter %s", v->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
void VarExp::checkEscapeRef()
|
|
{
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
if (!v->isDataseg() && !(v->storage_class & (STCref | STCout)))
|
|
error("escaping reference to local variable %s", v->toChars());
|
|
}
|
|
}
|
|
|
|
#if DMDV2
|
|
int VarExp::isLvalue()
|
|
{
|
|
if (var->storage_class & STClazy)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *VarExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
#if 0
|
|
tym = tybasic(e1->ET->Tty);
|
|
if (!(tyscalar(tym) ||
|
|
tym == TYstruct ||
|
|
tym == TYarray && e->Eoper == TOKaddr))
|
|
synerr(EM_lvalue); // lvalue expected
|
|
#endif
|
|
if (var->storage_class & STClazy)
|
|
{ error("lazy variables cannot be lvalues");
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("VarExp::modifiableLvalue('%s')\n", var->toChars());
|
|
//if (type && type->toBasetype()->ty == Tsarray)
|
|
//error("cannot change reference to static array '%s'", var->toChars());
|
|
|
|
var->checkModify(loc, sc, type);
|
|
|
|
// See if this expression is a modifiable lvalue (i.e. not const)
|
|
return toLvalue(sc, e);
|
|
}
|
|
|
|
|
|
/******************************** OverExp **************************/
|
|
|
|
#if DMDV2
|
|
OverExp::OverExp(OverloadSet *s)
|
|
: Expression(loc, TOKoverloadset, sizeof(OverExp))
|
|
{
|
|
//printf("OverExp(this = %p, '%s')\n", this, var->toChars());
|
|
vars = s;
|
|
type = Type::tvoid;
|
|
}
|
|
|
|
int OverExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
Expression *OverExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
return this;
|
|
}
|
|
#endif
|
|
|
|
|
|
/******************************** TupleExp **************************/
|
|
|
|
TupleExp::TupleExp(Loc loc, Expressions *exps)
|
|
: Expression(loc, TOKtuple, sizeof(TupleExp))
|
|
{
|
|
//printf("TupleExp(this = %p)\n", this);
|
|
this->exps = exps;
|
|
this->type = NULL;
|
|
}
|
|
|
|
|
|
TupleExp::TupleExp(Loc loc, TupleDeclaration *tup)
|
|
: Expression(loc, TOKtuple, sizeof(TupleExp))
|
|
{
|
|
exps = new Expressions();
|
|
type = NULL;
|
|
|
|
exps->reserve(tup->objects->dim);
|
|
for (size_t i = 0; i < tup->objects->dim; i++)
|
|
{ Object *o = tup->objects->tdata()[i];
|
|
if (o->dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
Expression *e = (Expression *)o;
|
|
if (e->op == TOKdsymbol)
|
|
e = e->syntaxCopy();
|
|
exps->push(e);
|
|
}
|
|
else if (o->dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
Dsymbol *s = (Dsymbol *)o;
|
|
Expression *e = new DsymbolExp(loc, s);
|
|
exps->push(e);
|
|
}
|
|
else if (o->dyncast() == DYNCAST_TYPE)
|
|
{
|
|
Type *t = (Type *)o;
|
|
Expression *e = new TypeExp(loc, t);
|
|
exps->push(e);
|
|
}
|
|
else
|
|
{
|
|
error("%s is not an expression", o->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
int TupleExp::equals(Object *o)
|
|
{
|
|
if (this == o)
|
|
return 1;
|
|
if (((Expression *)o)->op == TOKtuple)
|
|
{
|
|
TupleExp *te = (TupleExp *)o;
|
|
if (exps->dim != te->exps->dim)
|
|
return 0;
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e1 = (*exps)[i];
|
|
Expression *e2 = (*te->exps)[i];
|
|
|
|
if (!e1->equals(e2))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Expression *TupleExp::syntaxCopy()
|
|
{
|
|
return new TupleExp(loc, arraySyntaxCopy(exps));
|
|
}
|
|
|
|
Expression *TupleExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("+TupleExp::semantic(%s)\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
// Run semantic() on each argument
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
|
|
e = e->semantic(sc);
|
|
if (!e->type)
|
|
{ error("%s has no value", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
(*exps)[i] = e;
|
|
}
|
|
|
|
expandTuples(exps);
|
|
type = new TypeTuple(exps);
|
|
type = type->semantic(loc, sc);
|
|
//printf("-TupleExp::semantic(%s)\n", toChars());
|
|
return this;
|
|
}
|
|
|
|
void TupleExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("tuple(");
|
|
argsToCBuffer(buf, exps, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
|
|
void TupleExp::checkEscape()
|
|
{
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*exps)[i];
|
|
e->checkEscape();
|
|
}
|
|
}
|
|
|
|
/******************************** FuncExp *********************************/
|
|
|
|
FuncExp::FuncExp(Loc loc, FuncLiteralDeclaration *fd, TemplateDeclaration *td)
|
|
: Expression(loc, TOKfunction, sizeof(FuncExp))
|
|
{
|
|
this->fd = fd;
|
|
this->td = td;
|
|
tok = fd->tok; // save original kind of function/delegate/(infer)
|
|
treq = NULL;
|
|
}
|
|
|
|
Expression *FuncExp::syntaxCopy()
|
|
{
|
|
TemplateDeclaration *td2 = td ? (TemplateDeclaration *)td->syntaxCopy(NULL) : NULL;
|
|
return new FuncExp(loc, (FuncLiteralDeclaration *)fd->syntaxCopy(NULL), td2);
|
|
}
|
|
|
|
Expression *FuncExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("FuncExp::semantic(%s)\n", toChars());
|
|
if (treq) printf(" treq = %s\n", treq->toChars());
|
|
#endif
|
|
if (!type || type == Type::tvoid)
|
|
{
|
|
if (treq)
|
|
treq = treq->semantic(loc, sc);
|
|
|
|
// Set target of return type inference
|
|
if (treq && !fd->type->nextOf())
|
|
{ TypeFunction *tfv = NULL;
|
|
if (treq->ty == Tdelegate ||
|
|
(treq->ty == Tpointer && treq->nextOf()->ty == Tfunction))
|
|
tfv = (TypeFunction *)treq->nextOf();
|
|
if (tfv)
|
|
{ TypeFunction *tfl = (TypeFunction *)fd->type;
|
|
tfl->next = tfv->nextOf();
|
|
}
|
|
}
|
|
|
|
//printf("td = %p, treq = %p\n", td, treq);
|
|
if (td)
|
|
{
|
|
assert(td->parameters && td->parameters->dim);
|
|
td->semantic(sc);
|
|
type = Type::tvoid; // temporary type
|
|
|
|
if (!treq) // defer type determination
|
|
return this;
|
|
|
|
return inferType(treq);
|
|
}
|
|
|
|
unsigned olderrors = global.errors;
|
|
fd->semantic(sc);
|
|
//fd->parent = sc->parent;
|
|
if (olderrors != global.errors)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
fd->semantic2(sc);
|
|
if ( (olderrors == global.errors) ||
|
|
// need to infer return type
|
|
(fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()))
|
|
{
|
|
fd->semantic3(sc);
|
|
|
|
if ( (olderrors == global.errors) && global.params.useInline)
|
|
fd->inlineScan();
|
|
}
|
|
}
|
|
|
|
// need to infer return type
|
|
if ((olderrors != global.errors) && fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())
|
|
((TypeFunction *)fd->type)->next = Type::terror;
|
|
|
|
// Type is a "delegate to" or "pointer to" the function literal
|
|
if ((fd->isNested() && fd->tok == TOKdelegate) ||
|
|
(tok == TOKreserved && treq && treq->ty == Tdelegate))
|
|
{
|
|
type = new TypeDelegate(fd->type);
|
|
type = type->semantic(loc, sc);
|
|
}
|
|
else
|
|
{
|
|
type = fd->type->pointerTo();
|
|
}
|
|
fd->tookAddressOf++;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// used from CallExp::semantic()
|
|
Expression *FuncExp::semantic(Scope *sc, Expressions *arguments)
|
|
{
|
|
if ((!type || type == Type::tvoid) && td && arguments && arguments->dim)
|
|
{
|
|
for (size_t k = 0; k < arguments->dim; k++)
|
|
{ Expression *checkarg = arguments->tdata()[k];
|
|
if (checkarg->op == TOKerror)
|
|
return checkarg;
|
|
}
|
|
|
|
assert(td->parameters && td->parameters->dim);
|
|
td->semantic(sc);
|
|
|
|
TypeFunction *tfl = (TypeFunction *)fd->type;
|
|
size_t dim = Parameter::dim(tfl->parameters);
|
|
|
|
if ((!tfl->varargs && arguments->dim == dim) ||
|
|
( tfl->varargs && arguments->dim >= dim))
|
|
{
|
|
Objects *tiargs = new Objects();
|
|
tiargs->reserve(td->parameters->dim);
|
|
|
|
for (size_t i = 0; i < td->parameters->dim; i++)
|
|
{
|
|
TemplateParameter *tp = (*td->parameters)[i];
|
|
for (size_t u = 0; u < dim; u++)
|
|
{ Parameter *p = Parameter::getNth(tfl->parameters, u);
|
|
if (p->type->ty == Tident &&
|
|
((TypeIdentifier *)p->type)->ident == tp->ident)
|
|
{ Expression *e = (*arguments)[u];
|
|
tiargs->push(e->type);
|
|
u = dim; // break inner loop
|
|
}
|
|
}
|
|
}
|
|
|
|
TemplateInstance *ti = new TemplateInstance(loc, td, tiargs);
|
|
return (new ScopeExp(loc, ti))->semantic(sc);
|
|
}
|
|
error("cannot infer function literal type");
|
|
return new ErrorExp();
|
|
}
|
|
return semantic(sc);
|
|
}
|
|
|
|
char *FuncExp::toChars()
|
|
{
|
|
return fd->toChars();
|
|
}
|
|
|
|
void FuncExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
fd->toCBuffer(buf, hgs);
|
|
//buf->writestring(fd->toChars());
|
|
}
|
|
|
|
|
|
/******************************** DeclarationExp **************************/
|
|
|
|
DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration)
|
|
: Expression(loc, TOKdeclaration, sizeof(DeclarationExp))
|
|
{
|
|
this->declaration = declaration;
|
|
}
|
|
|
|
Expression *DeclarationExp::syntaxCopy()
|
|
{
|
|
return new DeclarationExp(loc, declaration->syntaxCopy(NULL));
|
|
}
|
|
|
|
Expression *DeclarationExp::semantic(Scope *sc)
|
|
{
|
|
if (type)
|
|
return this;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("DeclarationExp::semantic() %s\n", toChars());
|
|
#endif
|
|
|
|
unsigned olderrors = global.errors;
|
|
|
|
/* This is here to support extern(linkage) declaration,
|
|
* where the extern(linkage) winds up being an AttribDeclaration
|
|
* wrapper.
|
|
*/
|
|
Dsymbol *s = declaration;
|
|
|
|
AttribDeclaration *ad = declaration->isAttribDeclaration();
|
|
if (ad)
|
|
{
|
|
if (ad->decl && ad->decl->dim == 1)
|
|
s = ad->decl->tdata()[0];
|
|
}
|
|
|
|
if (s->isVarDeclaration())
|
|
{ // Do semantic() on initializer first, so:
|
|
// int a = a;
|
|
// will be illegal.
|
|
declaration->semantic(sc);
|
|
s->parent = sc->parent;
|
|
}
|
|
|
|
//printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc);
|
|
// Insert into both local scope and function scope.
|
|
// Must be unique in both.
|
|
if (s->ident)
|
|
{
|
|
if (!sc->insert(s))
|
|
{ error("declaration %s is already defined", s->toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
else if (sc->func)
|
|
{ VarDeclaration *v = s->isVarDeclaration();
|
|
if ( (s->isFuncDeclaration() || s->isTypedefDeclaration() ||
|
|
s->isAggregateDeclaration() || s->isEnumDeclaration() ||
|
|
s->isInterfaceDeclaration()) &&
|
|
!sc->func->localsymtab->insert(s))
|
|
{
|
|
error("declaration %s is already defined in another scope in %s",
|
|
s->toPrettyChars(), sc->func->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else if (!global.params.useDeprecated)
|
|
{ // Disallow shadowing
|
|
|
|
for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing)
|
|
{ Dsymbol *s2;
|
|
|
|
if (scx->scopesym && scx->scopesym->symtab &&
|
|
(s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
|
|
s != s2)
|
|
{
|
|
error("shadowing declaration %s is deprecated", s->toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!s->isVarDeclaration())
|
|
{
|
|
Scope *sc2 = sc;
|
|
if (sc2->stc & (STCpure | STCnothrow))
|
|
sc2 = sc->push();
|
|
sc2->stc &= ~(STCpure | STCnothrow);
|
|
declaration->semantic(sc2);
|
|
if (sc2 != sc)
|
|
sc2->pop();
|
|
s->parent = sc->parent;
|
|
}
|
|
if (global.errors == olderrors)
|
|
{
|
|
declaration->semantic2(sc);
|
|
if (global.errors == olderrors)
|
|
{
|
|
declaration->semantic3(sc);
|
|
|
|
if ((global.errors == olderrors) && global.params.useInline)
|
|
declaration->inlineScan();
|
|
}
|
|
}
|
|
|
|
type = Type::tvoid;
|
|
return this;
|
|
}
|
|
|
|
|
|
void DeclarationExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
declaration->toCBuffer(buf, hgs);
|
|
}
|
|
|
|
|
|
/************************ TypeidExp ************************************/
|
|
|
|
/*
|
|
* typeid(int)
|
|
*/
|
|
|
|
TypeidExp::TypeidExp(Loc loc, Object *o)
|
|
: Expression(loc, TOKtypeid, sizeof(TypeidExp))
|
|
{
|
|
this->obj = o;
|
|
}
|
|
|
|
|
|
Expression *TypeidExp::syntaxCopy()
|
|
{
|
|
return new TypeidExp(loc, objectSyntaxCopy(obj));
|
|
}
|
|
|
|
|
|
Expression *TypeidExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("TypeidExp::semantic() %s\n", toChars());
|
|
#endif
|
|
Type *ta = isType(obj);
|
|
Expression *ea = isExpression(obj);
|
|
Dsymbol *sa = isDsymbol(obj);
|
|
|
|
//printf("ta %p ea %p sa %p\n", ta, ea, sa);
|
|
|
|
if (ta)
|
|
{
|
|
ta->resolve(loc, sc, &ea, &ta, &sa);
|
|
}
|
|
|
|
if (ea)
|
|
{
|
|
ea = ea->semantic(sc);
|
|
ea = resolveProperties(sc, ea);
|
|
ta = ea->type;
|
|
if (ea->op == TOKtype)
|
|
ea = NULL;
|
|
}
|
|
|
|
if (!ta)
|
|
{
|
|
//printf("ta %p ea %p sa %p\n", ta, ea, sa);
|
|
error("no type for typeid(%s)", ea ? ea->toChars() : (sa ? sa->toChars() : ""));
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (ea && ta->toBasetype()->ty == Tclass)
|
|
{ /* Get the dynamic type, which is .classinfo
|
|
*/
|
|
e = new DotIdExp(ea->loc, ea, Id::classinfo);
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
{ /* Get the static type
|
|
*/
|
|
e = ta->getTypeInfo(sc);
|
|
if (e->loc.linnum == 0)
|
|
e->loc = loc; // so there's at least some line number info
|
|
if (ea)
|
|
{
|
|
e = new CommaExp(loc, ea, e); // execute ea
|
|
e = e->semantic(sc);
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void TypeidExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("typeid(");
|
|
ObjectToCBuffer(buf, hgs, obj);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
/************************ TraitsExp ************************************/
|
|
#if DMDV2
|
|
/*
|
|
* __traits(identifier, args...)
|
|
*/
|
|
|
|
TraitsExp::TraitsExp(Loc loc, Identifier *ident, Objects *args)
|
|
: Expression(loc, TOKtraits, sizeof(TraitsExp))
|
|
{
|
|
this->ident = ident;
|
|
this->args = args;
|
|
}
|
|
|
|
|
|
Expression *TraitsExp::syntaxCopy()
|
|
{
|
|
return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args));
|
|
}
|
|
|
|
|
|
void TraitsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("__traits(");
|
|
buf->writestring(ident->toChars());
|
|
if (args)
|
|
{
|
|
for (size_t i = 0; i < args->dim; i++)
|
|
{
|
|
buf->writeByte(',');
|
|
Object *oarg = args->tdata()[i];
|
|
ObjectToCBuffer(buf, hgs, oarg);
|
|
}
|
|
}
|
|
buf->writeByte(')');
|
|
}
|
|
#endif
|
|
|
|
/************************************************************/
|
|
|
|
HaltExp::HaltExp(Loc loc)
|
|
: Expression(loc, TOKhalt, sizeof(HaltExp))
|
|
{
|
|
}
|
|
|
|
Expression *HaltExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("HaltExp::semantic()\n");
|
|
#endif
|
|
type = Type::tvoid;
|
|
return this;
|
|
}
|
|
|
|
|
|
void HaltExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("halt");
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
IsExp::IsExp(Loc loc, Type *targ, Identifier *id, enum TOK tok,
|
|
Type *tspec, enum TOK tok2, TemplateParameters *parameters)
|
|
: Expression(loc, TOKis, sizeof(IsExp))
|
|
{
|
|
this->targ = targ;
|
|
this->id = id;
|
|
this->tok = tok;
|
|
this->tspec = tspec;
|
|
this->tok2 = tok2;
|
|
this->parameters = parameters;
|
|
}
|
|
|
|
Expression *IsExp::syntaxCopy()
|
|
{
|
|
// This section is identical to that in TemplateDeclaration::syntaxCopy()
|
|
TemplateParameters *p = NULL;
|
|
if (parameters)
|
|
{
|
|
p = new TemplateParameters();
|
|
p->setDim(parameters->dim);
|
|
for (size_t i = 0; i < p->dim; i++)
|
|
{ TemplateParameter *tp = parameters->tdata()[i];
|
|
p->tdata()[i] = tp->syntaxCopy();
|
|
}
|
|
}
|
|
|
|
return new IsExp(loc,
|
|
targ->syntaxCopy(),
|
|
id,
|
|
tok,
|
|
tspec ? tspec->syntaxCopy() : NULL,
|
|
tok2,
|
|
p);
|
|
}
|
|
|
|
Expression *IsExp::semantic(Scope *sc)
|
|
{ Type *tded;
|
|
|
|
/* is(targ id tok tspec)
|
|
* is(targ id : tok2)
|
|
* is(targ id == tok2)
|
|
*/
|
|
|
|
//printf("IsExp::semantic(%s)\n", toChars());
|
|
if (id && !(sc->flags & (SCOPEstaticif | SCOPEstaticassert)))
|
|
{ error("can only declare type aliases within static if conditionals or static asserts");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Type *t = targ->trySemantic(loc, sc);
|
|
if (!t)
|
|
goto Lno; // errors, so condition is false
|
|
targ = t;
|
|
if (tok2 != TOKreserved)
|
|
{
|
|
switch (tok2)
|
|
{
|
|
case TOKtypedef:
|
|
if (targ->ty != Ttypedef)
|
|
goto Lno;
|
|
tded = ((TypeTypedef *)targ)->sym->basetype;
|
|
break;
|
|
|
|
case TOKstruct:
|
|
if (targ->ty != Tstruct)
|
|
goto Lno;
|
|
if (((TypeStruct *)targ)->sym->isUnionDeclaration())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKunion:
|
|
if (targ->ty != Tstruct)
|
|
goto Lno;
|
|
if (!((TypeStruct *)targ)->sym->isUnionDeclaration())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKclass:
|
|
if (targ->ty != Tclass)
|
|
goto Lno;
|
|
if (((TypeClass *)targ)->sym->isInterfaceDeclaration())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKinterface:
|
|
if (targ->ty != Tclass)
|
|
goto Lno;
|
|
if (!((TypeClass *)targ)->sym->isInterfaceDeclaration())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
#if DMDV2
|
|
case TOKconst:
|
|
if (!targ->isConst())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKinvariant:
|
|
if (!global.params.useDeprecated)
|
|
error("use of 'invariant' rather than 'immutable' is deprecated");
|
|
case TOKimmutable:
|
|
if (!targ->isImmutable())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKshared:
|
|
if (!targ->isShared())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
|
|
case TOKwild:
|
|
if (!targ->isWild())
|
|
goto Lno;
|
|
tded = targ;
|
|
break;
|
|
#endif
|
|
|
|
case TOKsuper:
|
|
// If class or interface, get the base class and interfaces
|
|
if (targ->ty != Tclass)
|
|
goto Lno;
|
|
else
|
|
{ ClassDeclaration *cd = ((TypeClass *)targ)->sym;
|
|
Parameters *args = new Parameters;
|
|
args->reserve(cd->baseclasses->dim);
|
|
for (size_t i = 0; i < cd->baseclasses->dim; i++)
|
|
{ BaseClass *b = cd->baseclasses->tdata()[i];
|
|
args->push(new Parameter(STCin, b->type, NULL, NULL));
|
|
}
|
|
tded = new TypeTuple(args);
|
|
}
|
|
break;
|
|
|
|
case TOKenum:
|
|
if (targ->ty != Tenum)
|
|
goto Lno;
|
|
tded = ((TypeEnum *)targ)->sym->memtype;
|
|
break;
|
|
|
|
case TOKdelegate:
|
|
if (targ->ty != Tdelegate)
|
|
goto Lno;
|
|
tded = ((TypeDelegate *)targ)->next; // the underlying function type
|
|
break;
|
|
|
|
case TOKfunction:
|
|
{
|
|
if (targ->ty != Tfunction)
|
|
goto Lno;
|
|
tded = targ;
|
|
|
|
/* Generate tuple from function parameter types.
|
|
*/
|
|
assert(tded->ty == Tfunction);
|
|
Parameters *params = ((TypeFunction *)tded)->parameters;
|
|
size_t dim = Parameter::dim(params);
|
|
Parameters *args = new Parameters;
|
|
args->reserve(dim);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Parameter *arg = Parameter::getNth(params, i);
|
|
assert(arg && arg->type);
|
|
args->push(new Parameter(arg->storageClass, arg->type, NULL, NULL));
|
|
}
|
|
tded = new TypeTuple(args);
|
|
break;
|
|
}
|
|
case TOKreturn:
|
|
/* Get the 'return type' for the function,
|
|
* delegate, or pointer to function.
|
|
*/
|
|
if (targ->ty == Tfunction)
|
|
tded = ((TypeFunction *)targ)->next;
|
|
else if (targ->ty == Tdelegate)
|
|
{ tded = ((TypeDelegate *)targ)->next;
|
|
tded = ((TypeFunction *)tded)->next;
|
|
}
|
|
else if (targ->ty == Tpointer &&
|
|
((TypePointer *)targ)->next->ty == Tfunction)
|
|
{ tded = ((TypePointer *)targ)->next;
|
|
tded = ((TypeFunction *)tded)->next;
|
|
}
|
|
else
|
|
goto Lno;
|
|
break;
|
|
|
|
case TOKargTypes:
|
|
/* Generate a type tuple of the equivalent types used to determine if a
|
|
* function argument of this type can be passed in registers.
|
|
* The results of this are highly platform dependent, and intended
|
|
* primarly for use in implementing va_arg().
|
|
*/
|
|
tded = targ->toArgTypes();
|
|
if (!tded)
|
|
goto Lno; // not valid for a parameter
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
goto Lyes;
|
|
}
|
|
else if (id && tspec)
|
|
{
|
|
/* Evaluate to TRUE if targ matches tspec.
|
|
* If TRUE, declare id as an alias for the specialized type.
|
|
*/
|
|
|
|
assert(parameters && parameters->dim);
|
|
|
|
Objects dedtypes;
|
|
dedtypes.setDim(parameters->dim);
|
|
dedtypes.zero();
|
|
|
|
MATCH m = targ->deduceType(sc, tspec, parameters, &dedtypes);
|
|
//printf("targ: %s\n", targ->toChars());
|
|
//printf("tspec: %s\n", tspec->toChars());
|
|
if (m == MATCHnomatch ||
|
|
(m != MATCHexact && tok == TOKequal))
|
|
{
|
|
goto Lno;
|
|
}
|
|
else
|
|
{
|
|
tded = (Type *)dedtypes.tdata()[0];
|
|
if (!tded)
|
|
tded = targ;
|
|
#if DMDV2
|
|
Objects tiargs;
|
|
tiargs.setDim(1);
|
|
tiargs.tdata()[0] = targ;
|
|
|
|
/* Declare trailing parameters
|
|
*/
|
|
for (size_t i = 1; i < parameters->dim; i++)
|
|
{ TemplateParameter *tp = (*parameters)[i];
|
|
Declaration *s = NULL;
|
|
|
|
m = tp->matchArg(sc, &tiargs, i, parameters, &dedtypes, &s);
|
|
if (m == MATCHnomatch)
|
|
goto Lno;
|
|
s->semantic(sc);
|
|
if (sc->sd)
|
|
s->addMember(sc, sc->sd, 1);
|
|
else if (!sc->insert(s))
|
|
error("declaration %s is already defined", s->toChars());
|
|
}
|
|
#endif
|
|
goto Lyes;
|
|
}
|
|
}
|
|
else if (id)
|
|
{
|
|
/* Declare id as an alias for type targ. Evaluate to TRUE
|
|
*/
|
|
tded = targ;
|
|
goto Lyes;
|
|
}
|
|
else if (tspec)
|
|
{
|
|
/* Evaluate to TRUE if targ matches tspec
|
|
* is(targ == tspec)
|
|
* is(targ : tspec)
|
|
*/
|
|
tspec = tspec->semantic(loc, sc);
|
|
//printf("targ = %s, %s\n", targ->toChars(), targ->deco);
|
|
//printf("tspec = %s, %s\n", tspec->toChars(), tspec->deco);
|
|
if (tok == TOKcolon)
|
|
{ if (targ->implicitConvTo(tspec))
|
|
goto Lyes;
|
|
else
|
|
goto Lno;
|
|
}
|
|
else /* == */
|
|
{ if (targ->equals(tspec))
|
|
goto Lyes;
|
|
else
|
|
goto Lno;
|
|
}
|
|
}
|
|
|
|
Lyes:
|
|
if (id)
|
|
{
|
|
Dsymbol *s;
|
|
Tuple *tup = isTuple(tded);
|
|
if (tup)
|
|
s = new TupleDeclaration(loc, id, &(tup->objects));
|
|
else
|
|
s = new AliasDeclaration(loc, id, tded);
|
|
s->semantic(sc);
|
|
/* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
|
|
* More investigation is needed.
|
|
*/
|
|
if (!tup && !sc->insert(s))
|
|
error("declaration %s is already defined", s->toChars());
|
|
if (sc->sd)
|
|
s->addMember(sc, sc->sd, 1);
|
|
}
|
|
//printf("Lyes\n");
|
|
return new IntegerExp(loc, 1, Type::tbool);
|
|
|
|
Lno:
|
|
//printf("Lno\n");
|
|
return new IntegerExp(loc, 0, Type::tbool);
|
|
}
|
|
|
|
void IsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("is(");
|
|
targ->toCBuffer(buf, id, hgs);
|
|
if (tok2 != TOKreserved)
|
|
{
|
|
buf->printf(" %s %s", Token::toChars(tok), Token::toChars(tok2));
|
|
}
|
|
else if (tspec)
|
|
{
|
|
if (tok == TOKcolon)
|
|
buf->writestring(" : ");
|
|
else
|
|
buf->writestring(" == ");
|
|
tspec->toCBuffer(buf, NULL, hgs);
|
|
}
|
|
#if DMDV2
|
|
if (parameters)
|
|
{ // First parameter is already output, so start with second
|
|
for (size_t i = 1; i < parameters->dim; i++)
|
|
{
|
|
buf->writeByte(',');
|
|
TemplateParameter *tp = parameters->tdata()[i];
|
|
tp->toCBuffer(buf, hgs);
|
|
}
|
|
}
|
|
#endif
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
UnaExp::UnaExp(Loc loc, enum TOK op, int size, Expression *e1)
|
|
: Expression(loc, op, size)
|
|
{
|
|
this->e1 = e1;
|
|
}
|
|
|
|
Expression *UnaExp::syntaxCopy()
|
|
{
|
|
UnaExp *e = (UnaExp *)copy();
|
|
e->type = NULL;
|
|
e->e1 = e->e1->syntaxCopy();
|
|
return e;
|
|
}
|
|
|
|
Expression *UnaExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("UnaExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
e1 = e1->semantic(sc);
|
|
// if (!e1->type)
|
|
// error("%s has no value", e1->toChars());
|
|
return this;
|
|
}
|
|
|
|
Expression *UnaExp::resolveLoc(Loc loc, Scope *sc)
|
|
{
|
|
e1 = e1->resolveLoc(loc, sc);
|
|
return this;
|
|
}
|
|
|
|
void UnaExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(Token::toChars(op));
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
BinExp::BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2)
|
|
: Expression(loc, op, size)
|
|
{
|
|
this->e1 = e1;
|
|
this->e2 = e2;
|
|
}
|
|
|
|
Expression *BinExp::syntaxCopy()
|
|
{
|
|
BinExp *e = (BinExp *)copy();
|
|
e->type = NULL;
|
|
e->e1 = e->e1->syntaxCopy();
|
|
e->e2 = e->e2->syntaxCopy();
|
|
return e;
|
|
}
|
|
|
|
Expression *BinExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("BinExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
e1 = e1->semantic(sc);
|
|
e2 = e2->semantic(sc);
|
|
if (e1->op == TOKerror || e2->op == TOKerror)
|
|
return new ErrorExp();
|
|
return this;
|
|
}
|
|
|
|
Expression *BinExp::semanticp(Scope *sc)
|
|
{
|
|
BinExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e2 = resolveProperties(sc, e2);
|
|
return this;
|
|
}
|
|
|
|
|
|
Expression *BinExp::checkComplexOpAssign(Scope *sc)
|
|
{
|
|
// generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
|
|
if (op == TOKmulass || op == TOKdivass || op == TOKmodass)
|
|
{
|
|
// Any multiplication by an imaginary or complex number yields a complex result.
|
|
// r *= c, i*=c, r*=i, i*=i are all forbidden operations.
|
|
const char *opstr = Token::toChars(op);
|
|
if ( e1->type->isreal() && e2->type->iscomplex())
|
|
{
|
|
error("%s %s %s is undefined. Did you mean %s %s %s.re ?",
|
|
e1->type->toChars(), opstr, e2->type->toChars(),
|
|
e1->type->toChars(), opstr, e2->type->toChars());
|
|
}
|
|
else if (e1->type->isimaginary() && e2->type->iscomplex())
|
|
{
|
|
error("%s %s %s is undefined. Did you mean %s %s %s.im ?",
|
|
e1->type->toChars(), opstr, e2->type->toChars(),
|
|
e1->type->toChars(), opstr, e2->type->toChars());
|
|
}
|
|
else if ((e1->type->isreal() || e1->type->isimaginary()) &&
|
|
e2->type->isimaginary())
|
|
{
|
|
error("%s %s %s is an undefined operation", e1->type->toChars(),
|
|
opstr, e2->type->toChars());
|
|
}
|
|
}
|
|
|
|
// generate an error if this is a nonsensical += or -=, eg real += imaginary
|
|
if (op == TOKaddass || op == TOKminass)
|
|
{
|
|
// Addition or subtraction of a real and an imaginary is a complex result.
|
|
// Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
|
|
if ( (e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) ||
|
|
(e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex()))
|
|
)
|
|
{
|
|
error("%s %s %s is undefined (result is complex)",
|
|
e1->type->toChars(), Token::toChars(op), e2->type->toChars());
|
|
}
|
|
if (type->isreal() || type->isimaginary())
|
|
{
|
|
assert(global.errors || e2->type->isfloating());
|
|
e2 = e2->castTo(sc, e1->type);
|
|
}
|
|
}
|
|
|
|
if (op == TOKmulass)
|
|
{
|
|
if (e2->type->isfloating())
|
|
{
|
|
Type *t1 = e1->type;
|
|
Type *t2 = e2->type;
|
|
if (t1->isreal())
|
|
{
|
|
if (t2->isimaginary() || t2->iscomplex())
|
|
{
|
|
e2 = e2->castTo(sc, t1);
|
|
}
|
|
}
|
|
else if (t1->isimaginary())
|
|
{
|
|
if (t2->isimaginary() || t2->iscomplex())
|
|
{
|
|
switch (t1->ty)
|
|
{
|
|
case Timaginary32: t2 = Type::tfloat32; break;
|
|
case Timaginary64: t2 = Type::tfloat64; break;
|
|
case Timaginary80: t2 = Type::tfloat80; break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
e2 = e2->castTo(sc, t2);
|
|
}
|
|
}
|
|
}
|
|
} else if (op == TOKdivass)
|
|
{
|
|
if (e2->type->isimaginary())
|
|
{
|
|
Type *t1 = e1->type;
|
|
if (t1->isreal())
|
|
{ // x/iv = i(-x/v)
|
|
// Therefore, the result is 0
|
|
e2 = new CommaExp(loc, e2, new RealExp(loc, ldouble(0.0), t1));
|
|
e2->type = t1;
|
|
Expression *e = new AssignExp(loc, e1, e2);
|
|
e->type = t1;
|
|
return e;
|
|
}
|
|
else if (t1->isimaginary())
|
|
{ Type *t2;
|
|
|
|
switch (t1->ty)
|
|
{
|
|
case Timaginary32: t2 = Type::tfloat32; break;
|
|
case Timaginary64: t2 = Type::tfloat64; break;
|
|
case Timaginary80: t2 = Type::tfloat80; break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
e2 = e2->castTo(sc, t2);
|
|
Expression *e = new AssignExp(loc, e1, e2);
|
|
e->type = t1;
|
|
return e;
|
|
}
|
|
}
|
|
} else if (op == TOKmodass)
|
|
{
|
|
if (e2->type->iscomplex())
|
|
{
|
|
error("cannot perform modulo complex arithmetic");
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void BinExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
buf->writeByte(' ');
|
|
buf->writestring(Token::toChars(op));
|
|
buf->writeByte(' ');
|
|
expToCBuffer(buf, hgs, e2, (enum PREC)(precedence[op] + 1));
|
|
}
|
|
|
|
int BinExp::isunsigned()
|
|
{
|
|
return e1->type->isunsigned() || e2->type->isunsigned();
|
|
}
|
|
|
|
Expression *BinExp::incompatibleTypes()
|
|
{
|
|
if (e1->type->toBasetype() != Type::terror &&
|
|
e2->type->toBasetype() != Type::terror
|
|
)
|
|
{
|
|
if (e1->op == TOKtype || e2->op == TOKtype)
|
|
{
|
|
error("incompatible types for ((%s) %s (%s)): cannot use '%s' with types",
|
|
e1->toChars(), Token::toChars(op), e2->toChars(), Token::toChars(op));
|
|
}
|
|
else
|
|
{
|
|
error("incompatible types for ((%s) %s (%s)): '%s' and '%s'",
|
|
e1->toChars(), Token::toChars(op), e2->toChars(),
|
|
e1->type->toChars(), e2->type->toChars());
|
|
}
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/********************** BinAssignExp **************************************/
|
|
|
|
Expression *BinAssignExp::semantic(Scope *sc)
|
|
{
|
|
Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
if (e1->op == TOKarraylength)
|
|
{
|
|
e = ArrayLengthExp::rewriteOpAssign(this);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
if (e1->op == TOKslice)
|
|
{
|
|
// T[] op= ...
|
|
e = typeCombine(sc);
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
type = e1->type;
|
|
return arrayOp(sc);
|
|
}
|
|
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
e1 = e1->semantic(sc);
|
|
type = e1->type;
|
|
checkScalar();
|
|
|
|
int arith = (op == TOKaddass || op == TOKminass || op == TOKmulass ||
|
|
op == TOKdivass || op == TOKmodass || op == TOKpowass);
|
|
int bitwise = (op == TOKandass || op == TOKorass || op == TOKxorass);
|
|
int shift = (op == TOKshlass || op == TOKshrass || op == TOKushrass);
|
|
|
|
if (bitwise && type->toBasetype()->ty == Tbool)
|
|
e2 = e2->implicitCastTo(sc, type);
|
|
else
|
|
checkNoBool();
|
|
|
|
if ((op == TOKaddass || op == TOKminass) &&
|
|
e1->type->toBasetype()->ty == Tpointer &&
|
|
e2->type->toBasetype()->isintegral())
|
|
return scaleFactor(sc);
|
|
|
|
typeCombine(sc);
|
|
if (arith)
|
|
{
|
|
e1 = e1->checkArithmetic();
|
|
e2 = e2->checkArithmetic();
|
|
}
|
|
if (bitwise || shift)
|
|
{
|
|
e1 = e1->checkIntegral();
|
|
e2 = e2->checkIntegral();
|
|
}
|
|
if (shift)
|
|
{
|
|
e2 = e2->castTo(sc, Type::tshiftcnt);
|
|
}
|
|
|
|
// vectors
|
|
if (shift && (e1->type->toBasetype()->ty == Tvector ||
|
|
e2->type->toBasetype()->ty == Tvector))
|
|
return incompatibleTypes();
|
|
|
|
int isvector = type->toBasetype()->ty == Tvector;
|
|
|
|
if (op == TOKmulass && isvector && !e2->type->isfloating() &&
|
|
((TypeVector *)type->toBasetype())->elementType()->size(loc) != 2)
|
|
return incompatibleTypes(); // Only short[8] and ushort[8] work with multiply
|
|
|
|
if (op == TOKdivass && isvector && !e1->type->isfloating())
|
|
return incompatibleTypes();
|
|
|
|
if (op == TOKmodass && isvector)
|
|
return incompatibleTypes();
|
|
|
|
if (e1->op == TOKerror || e2->op == TOKerror)
|
|
return new ErrorExp();
|
|
|
|
return checkComplexOpAssign(sc);
|
|
}
|
|
|
|
#if DMDV2
|
|
int BinAssignExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
Expression *BinAssignExp::toLvalue(Scope *sc, Expression *ex)
|
|
{ Expression *e;
|
|
|
|
if (e1->op == TOKvar)
|
|
{
|
|
/* Convert (e1 op= e2) to
|
|
* e1 op= e2;
|
|
* e1
|
|
*/
|
|
e = e1->copy();
|
|
e = new CommaExp(loc, this, e);
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
{
|
|
/* Convert (e1 op= e2) to
|
|
* ref v = e1;
|
|
* v op= e2;
|
|
* v
|
|
*/
|
|
|
|
// ref v = e1;
|
|
Identifier *id = Lexer::uniqueId("__assignop");
|
|
ExpInitializer *ei = new ExpInitializer(loc, e1);
|
|
VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei);
|
|
v->storage_class |= STCref | STCforeach;
|
|
Expression *de = new DeclarationExp(loc, v);
|
|
|
|
// v op= e2
|
|
e1 = new VarExp(e1->loc, v);
|
|
|
|
e = new CommaExp(loc, de, this);
|
|
e = new CommaExp(loc, e, new VarExp(loc, v));
|
|
e = e->semantic(sc);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *BinAssignExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
return toLvalue(sc, this);
|
|
}
|
|
|
|
#endif
|
|
|
|
/************************************************************/
|
|
|
|
CompileExp::CompileExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKmixin, sizeof(CompileExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *CompileExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("CompileExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
if (!e1->type->isString())
|
|
{
|
|
error("argument to mixin must be a string type, not %s\n", e1->type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
e1 = e1->optimize(WANTvalue | WANTinterpret);
|
|
StringExp *se = e1->toString();
|
|
if (!se)
|
|
{ error("argument to mixin must be a string, not (%s)", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
se = se->toUTF8(sc);
|
|
Parser p(sc->module, (unsigned char *)se->string, se->len, 0);
|
|
p.loc = loc;
|
|
p.nextToken();
|
|
//printf("p.loc.linnum = %d\n", p.loc.linnum);
|
|
Expression *e = p.parseExpression();
|
|
if (p.token.value != TOKeof)
|
|
{ error("incomplete mixin expression (%s)", se->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
return e->semantic(sc);
|
|
}
|
|
|
|
void CompileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("mixin(");
|
|
expToCBuffer(buf, hgs, e1, PREC_assign);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
FileExp::FileExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKmixin, sizeof(FileExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *FileExp::semantic(Scope *sc)
|
|
{ char *name;
|
|
StringExp *se;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("FileExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->optimize(WANTvalue | WANTinterpret);
|
|
if (e1->op != TOKstring)
|
|
{ error("file name argument must be a string, not (%s)", e1->toChars());
|
|
goto Lerror;
|
|
}
|
|
se = (StringExp *)e1;
|
|
se = se->toUTF8(sc);
|
|
name = (char *)se->string;
|
|
|
|
if (!global.params.fileImppath)
|
|
{ error("need -Jpath switch to import text file %s", name);
|
|
goto Lerror;
|
|
}
|
|
|
|
/* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
|
|
* ('Path Traversal') attacks.
|
|
* http://cwe.mitre.org/data/definitions/22.html
|
|
*/
|
|
|
|
name = FileName::safeSearchPath(global.filePath, name);
|
|
if (!name)
|
|
{ error("file %s cannot be found or not in a path specified with -J", se->toChars());
|
|
goto Lerror;
|
|
}
|
|
|
|
if (global.params.verbose)
|
|
printf("file %s\t(%s)\n", (char *)se->string, name);
|
|
|
|
{ File f(name);
|
|
if (f.read())
|
|
{ error("cannot read file %s", f.toChars());
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{
|
|
f.ref = 1;
|
|
se = new StringExp(loc, f.buffer, f.len);
|
|
}
|
|
}
|
|
return se->semantic(sc);
|
|
|
|
Lerror:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
void FileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("import(");
|
|
expToCBuffer(buf, hgs, e1, PREC_assign);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg)
|
|
: UnaExp(loc, TOKassert, sizeof(AssertExp), e)
|
|
{
|
|
this->msg = msg;
|
|
}
|
|
|
|
Expression *AssertExp::syntaxCopy()
|
|
{
|
|
AssertExp *ae = new AssertExp(loc, e1->syntaxCopy(),
|
|
msg ? msg->syntaxCopy() : NULL);
|
|
return ae;
|
|
}
|
|
|
|
Expression *AssertExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("AssertExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
// BUG: see if we can do compile time elimination of the Assert
|
|
e1 = e1->optimize(WANTvalue);
|
|
e1 = e1->checkToBoolean(sc);
|
|
if (msg)
|
|
{
|
|
msg = msg->semantic(sc);
|
|
msg = resolveProperties(sc, msg);
|
|
msg = msg->implicitCastTo(sc, Type::tchar->constOf()->arrayOf());
|
|
msg = msg->optimize(WANTvalue);
|
|
}
|
|
if (e1->isBool(FALSE))
|
|
{
|
|
FuncDeclaration *fd = sc->parent->isFuncDeclaration();
|
|
if (fd)
|
|
fd->hasReturnExp |= 4;
|
|
|
|
if (!global.params.useAssert)
|
|
{ Expression *e = new HaltExp(loc);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
type = Type::tvoid;
|
|
return this;
|
|
}
|
|
|
|
|
|
void AssertExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("assert(");
|
|
expToCBuffer(buf, hgs, e1, PREC_assign);
|
|
if (msg)
|
|
{
|
|
buf->writeByte(',');
|
|
expToCBuffer(buf, hgs, msg, PREC_assign);
|
|
}
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident)
|
|
: UnaExp(loc, TOKdot, sizeof(DotIdExp), e)
|
|
{
|
|
this->ident = ident;
|
|
}
|
|
|
|
Expression *DotIdExp::semantic(Scope *sc)
|
|
{
|
|
// Indicate we need to resolve by UFCS.
|
|
return semantic(sc, 0);
|
|
}
|
|
|
|
Expression *DotIdExp::semantic(Scope *sc, int flag)
|
|
{ Expression *e;
|
|
Expression *eleft;
|
|
Expression *eright;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("DotIdExp::semantic(this = %p, '%s')\n", this, toChars());
|
|
//printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
|
|
#endif
|
|
|
|
//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
|
|
|
|
#if 0
|
|
/* Don't do semantic analysis if we'll be converting
|
|
* it to a string.
|
|
*/
|
|
if (ident == Id::stringof)
|
|
{ char *s = e1->toChars();
|
|
e = new StringExp(loc, s, strlen(s), 'c');
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#endif
|
|
|
|
/* Special case: rewrite this.id and super.id
|
|
* to be classtype.id and baseclasstype.id
|
|
* if we have no this pointer.
|
|
*/
|
|
if ((e1->op == TOKthis || e1->op == TOKsuper) && !hasThis(sc))
|
|
{ ClassDeclaration *cd;
|
|
StructDeclaration *sd;
|
|
AggregateDeclaration *ad;
|
|
|
|
ad = sc->getStructClassScope();
|
|
if (ad)
|
|
{
|
|
cd = ad->isClassDeclaration();
|
|
if (cd)
|
|
{
|
|
if (e1->op == TOKthis)
|
|
{
|
|
e = typeDotIdExp(loc, cd->type, ident);
|
|
return e->semantic(sc);
|
|
}
|
|
else if (cd->baseClass && e1->op == TOKsuper)
|
|
{
|
|
e = typeDotIdExp(loc, cd->baseClass->type, ident);
|
|
return e->semantic(sc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sd = ad->isStructDeclaration();
|
|
if (sd)
|
|
{
|
|
if (e1->op == TOKthis)
|
|
{
|
|
e = typeDotIdExp(loc, sd->type, ident);
|
|
return e->semantic(sc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UnaExp::semantic(sc);
|
|
|
|
if (ident == Id::mangleof)
|
|
{ // symbol.mangleof
|
|
Dsymbol *ds;
|
|
switch (e1->op)
|
|
{
|
|
case TOKimport: ds = ((ScopeExp *)e1)->sds; goto L1;
|
|
case TOKvar: ds = ((VarExp *)e1)->var; goto L1;
|
|
case TOKdotvar: ds = ((DotVarExp *)e1)->var; goto L1;
|
|
L1:
|
|
char* s = ds->mangle();
|
|
e = new StringExp(loc, s, strlen(s), 'c');
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (e1->op == TOKdotexp)
|
|
{
|
|
DotExp *de = (DotExp *)e1;
|
|
eleft = de->e1;
|
|
eright = de->e2;
|
|
}
|
|
else
|
|
{
|
|
if (e1->op != TOKtype)
|
|
e1 = resolveProperties(sc, e1);
|
|
eleft = NULL;
|
|
eright = e1;
|
|
}
|
|
#if DMDV2
|
|
if (e1->op == TOKtuple && ident == Id::offsetof)
|
|
{ /* 'distribute' the .offsetof to each of the tuple elements.
|
|
*/
|
|
TupleExp *te = (TupleExp *)e1;
|
|
Expressions *exps = new Expressions();
|
|
exps->setDim(te->exps->dim);
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (*te->exps)[i];
|
|
e = e->semantic(sc);
|
|
e = new DotIdExp(e->loc, e, Id::offsetof);
|
|
(*exps)[i] = e;
|
|
}
|
|
e = new TupleExp(loc, exps);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#endif
|
|
|
|
if (e1->op == TOKtuple && ident == Id::length)
|
|
{
|
|
TupleExp *te = (TupleExp *)e1;
|
|
e = new IntegerExp(loc, te->exps->dim, Type::tsize_t);
|
|
return e;
|
|
}
|
|
|
|
if (e1->op == TOKdottd)
|
|
{
|
|
error("template %s does not have property %s", e1->toChars(), ident->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (!e1->type)
|
|
{
|
|
error("expression %s does not have property %s", e1->toChars(), ident->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Type *t1b = e1->type->toBasetype();
|
|
|
|
if (eright->op == TOKimport) // also used for template alias's
|
|
{
|
|
ScopeExp *ie = (ScopeExp *)eright;
|
|
|
|
/* Disable access to another module's private imports.
|
|
* The check for 'is sds our current module' is because
|
|
* the current module should have access to its own imports.
|
|
*/
|
|
Dsymbol *s = ie->sds->search(loc, ident,
|
|
(ie->sds->isModule() && ie->sds != sc->module) ? 1 : 0);
|
|
if (s)
|
|
{
|
|
/* Check for access before resolving aliases because public
|
|
* aliases to private symbols are public.
|
|
*/
|
|
if (Declaration *d = s->isDeclaration())
|
|
accessCheck(loc, sc, 0, d);
|
|
|
|
s = s->toAlias();
|
|
checkDeprecated(sc, s);
|
|
|
|
EnumMember *em = s->isEnumMember();
|
|
if (em)
|
|
{
|
|
e = em->value;
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
|
|
if (v->inuse)
|
|
{
|
|
error("circular reference to '%s'", v->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
type = v->type;
|
|
if (v->needThis())
|
|
{
|
|
if (!eleft)
|
|
eleft = new ThisExp(loc);
|
|
e = new DotVarExp(loc, eleft, v);
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
{
|
|
e = new VarExp(loc, v);
|
|
if (eleft)
|
|
{ e = new CommaExp(loc, eleft, e);
|
|
e->type = v->type;
|
|
}
|
|
}
|
|
e = e->deref();
|
|
return e->semantic(sc);
|
|
}
|
|
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
if (f)
|
|
{
|
|
//printf("it's a function\n");
|
|
if (f->needThis())
|
|
{
|
|
if (!eleft)
|
|
eleft = new ThisExp(loc);
|
|
e = new DotVarExp(loc, eleft, f);
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
{
|
|
e = new VarExp(loc, f, 1);
|
|
if (eleft)
|
|
{ e = new CommaExp(loc, eleft, e);
|
|
e->type = f->type;
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
#if DMDV2
|
|
OverloadSet *o = s->isOverloadSet();
|
|
if (o)
|
|
{ //printf("'%s' is an overload set\n", o->toChars());
|
|
return new OverExp(o);
|
|
}
|
|
#endif
|
|
|
|
Type *t = s->getType();
|
|
if (t)
|
|
{
|
|
return new TypeExp(loc, t);
|
|
}
|
|
|
|
TupleDeclaration *tup = s->isTupleDeclaration();
|
|
if (tup)
|
|
{
|
|
if (eleft)
|
|
{ error("cannot have e.tuple");
|
|
return new ErrorExp();
|
|
}
|
|
e = new TupleExp(loc, tup);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
ScopeDsymbol *sds = s->isScopeDsymbol();
|
|
if (sds)
|
|
{
|
|
//printf("it's a ScopeDsymbol\n");
|
|
e = new ScopeExp(loc, sds);
|
|
e = e->semantic(sc);
|
|
if (eleft)
|
|
e = new DotExp(loc, eleft, e);
|
|
return e;
|
|
}
|
|
|
|
Import *imp = s->isImport();
|
|
if (imp)
|
|
{
|
|
ScopeExp *ie;
|
|
|
|
ie = new ScopeExp(loc, imp->pkg);
|
|
return ie->semantic(sc);
|
|
}
|
|
|
|
// BUG: handle other cases like in IdentifierExp::semantic()
|
|
#ifdef DEBUG
|
|
printf("s = '%s', kind = '%s'\n", s->toChars(), s->kind());
|
|
#endif
|
|
assert(0);
|
|
}
|
|
else if (ident == Id::stringof)
|
|
{ char *s = ie->toChars();
|
|
e = new StringExp(loc, s, strlen(s), 'c');
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
s = ie->sds->search_correct(ident);
|
|
if (s)
|
|
error("undefined identifier '%s', did you mean '%s %s'?",
|
|
ident->toChars(), s->kind(), s->toChars());
|
|
else
|
|
error("undefined identifier '%s'", ident->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else if (t1b->ty == Tpointer &&
|
|
ident != Id::init && ident != Id::__sizeof &&
|
|
ident != Id::__xalignof && ident != Id::offsetof &&
|
|
ident != Id::mangleof && ident != Id::stringof)
|
|
{ /* Rewrite:
|
|
* p.ident
|
|
* as:
|
|
* (*p).ident
|
|
*/
|
|
e = new PtrExp(loc, e1);
|
|
e->type = ((TypePointer *)t1b)->next;
|
|
return e->type->dotExp(sc, e, ident);
|
|
}
|
|
#if DMDV2
|
|
else if (!flag)
|
|
{ /* If ident is not a valid property, rewrite:
|
|
* e1.ident
|
|
* as:
|
|
* .ident(e1)
|
|
*/
|
|
if (e1->op == TOKtype ||
|
|
t1b->ty == Tvoid ||
|
|
(t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Taarray) &&
|
|
(ident == Id::sort || ident == Id::reverse || ident == Id::dup || ident == Id::idup))
|
|
{ goto L2;
|
|
}
|
|
|
|
unsigned errors = global.startGagging();
|
|
Type *t1 = e1->type;
|
|
e = e1->type->dotExp(sc, e1, ident);
|
|
if (global.endGagging(errors)) // if failed to find the property
|
|
{
|
|
e1->type = t1; // kludge to restore type
|
|
e = resolveUFCSProperties(sc, this);
|
|
}
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
L2:
|
|
e = e1->type->dotExp(sc, e1, ident);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
void DotIdExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
//printf("DotIdExp::toCBuffer()\n");
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
buf->writestring(ident->toChars());
|
|
}
|
|
|
|
/********************** DotTemplateExp ***********************************/
|
|
|
|
// Mainly just a placeholder
|
|
|
|
DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td)
|
|
: UnaExp(loc, TOKdottd, sizeof(DotTemplateExp), e)
|
|
|
|
{
|
|
this->td = td;
|
|
}
|
|
|
|
void DotTemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
buf->writestring(td->toChars());
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *v, int hasOverloads)
|
|
: UnaExp(loc, TOKdotvar, sizeof(DotVarExp), e)
|
|
{
|
|
//printf("DotVarExp()\n");
|
|
this->var = v;
|
|
this->hasOverloads = hasOverloads;
|
|
}
|
|
|
|
Expression *DotVarExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DotVarExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
var = var->toAlias()->isDeclaration();
|
|
|
|
TupleDeclaration *tup = var->isTupleDeclaration();
|
|
if (tup)
|
|
{ /* Replace:
|
|
* e1.tuple(a, b, c)
|
|
* with:
|
|
* tuple(e1.a, e1.b, e1.c)
|
|
*/
|
|
Expressions *exps = new Expressions;
|
|
Expression *ev = e1;
|
|
|
|
exps->reserve(tup->objects->dim);
|
|
for (size_t i = 0; i < tup->objects->dim; i++)
|
|
{ Object *o = tup->objects->tdata()[i];
|
|
if (o->dyncast() != DYNCAST_EXPRESSION)
|
|
{
|
|
error("%s is not an expression", o->toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
Expression *e = (Expression *)o;
|
|
if (e->op != TOKdsymbol)
|
|
{ error("%s is not a member", e->toChars());
|
|
goto Lerr;
|
|
}
|
|
|
|
Dsymbol *s = ((DsymbolExp *)e)->s;
|
|
if (i == 0 && sc->func && tup->objects->dim > 1 &&
|
|
e1->hasSideEffect())
|
|
{
|
|
Identifier *id = Lexer::uniqueId("__tup");
|
|
ExpInitializer *ei = new ExpInitializer(e1->loc, e1);
|
|
VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei);
|
|
v->storage_class |= STCctfe | STCref | STCforeach;
|
|
|
|
ev = new VarExp(e->loc, v);
|
|
e = new CommaExp(e1->loc, new DeclarationExp(e1->loc, v), ev);
|
|
e = new DotVarExp(loc, e, s->isDeclaration());
|
|
}
|
|
else
|
|
e = new DotVarExp(loc, ev, s->isDeclaration());
|
|
exps->push(e);
|
|
}
|
|
Expression *e = new TupleExp(loc, exps);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
e1 = e1->semantic(sc);
|
|
e1 = e1->addDtorHook(sc);
|
|
type = var->type;
|
|
if (!type && global.errors)
|
|
{ // var is goofed up, just return 0
|
|
return new ErrorExp();
|
|
}
|
|
assert(type);
|
|
|
|
Type *t1 = e1->type;
|
|
if (!var->isFuncDeclaration()) // for functions, do checks after overload resolution
|
|
{
|
|
if (t1->ty == Tpointer)
|
|
t1 = t1->nextOf();
|
|
|
|
type = type->addMod(t1->mod);
|
|
|
|
Dsymbol *vparent = var->toParent();
|
|
AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL;
|
|
e1 = getRightThis(loc, sc, ad, e1, var);
|
|
if (!sc->noaccesscheck)
|
|
accessCheck(loc, sc, e1, var);
|
|
|
|
VarDeclaration *v = var->isVarDeclaration();
|
|
Expression *e = expandVar(WANTvalue, v);
|
|
if (e)
|
|
return e;
|
|
}
|
|
Dsymbol *s;
|
|
if (sc->func && !sc->intypeof && t1->hasPointers() &&
|
|
(s = t1->toDsymbol(sc)) != NULL)
|
|
{
|
|
AggregateDeclaration *ad = s->isAggregateDeclaration();
|
|
if (ad && ad->hasUnions)
|
|
{
|
|
if (sc->func->setUnsafe())
|
|
{ error("union %s containing pointers are not allowed in @safe functions", t1->toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//printf("-DotVarExp::semantic('%s')\n", toChars());
|
|
return this;
|
|
|
|
Lerr:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
#if DMDV2
|
|
int DotVarExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *DotVarExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("DotVarExp::toLvalue(%s)\n", toChars());
|
|
return this;
|
|
}
|
|
|
|
/***********************************************
|
|
* Mark variable v as modified if it is inside a constructor that var
|
|
* is a field in.
|
|
*/
|
|
void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1)
|
|
{
|
|
//printf("modifyFieldVar(var = %s)\n", var->toChars());
|
|
Dsymbol *s = sc->func;
|
|
while (1)
|
|
{
|
|
FuncDeclaration *fd = NULL;
|
|
if (s)
|
|
fd = s->isFuncDeclaration();
|
|
if (fd &&
|
|
((fd->isCtorDeclaration() && var->storage_class & STCfield) ||
|
|
(fd->isStaticCtorDeclaration() && !(var->storage_class & STCfield))) &&
|
|
fd->toParent2() == var->toParent2() &&
|
|
(!e1 || e1->op == TOKthis)
|
|
)
|
|
{
|
|
var->ctorinit = 1;
|
|
//printf("setting ctorinit\n");
|
|
}
|
|
else
|
|
{
|
|
if (s)
|
|
{ s = s->toParent2();
|
|
continue;
|
|
}
|
|
else if (var->storage_class & STCctorinit)
|
|
{
|
|
const char *p = var->isStatic() ? "static " : "";
|
|
error(loc, "can only initialize %sconst member %s inside %sconstructor",
|
|
p, var->toChars(), p);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
#if 0
|
|
printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
|
|
printf("e1->type = %s\n", e1->type->toChars());
|
|
printf("var->type = %s\n", var->type->toChars());
|
|
#endif
|
|
|
|
Type *t1 = e1->type->toBasetype();
|
|
|
|
if (!t1->isMutable() ||
|
|
(t1->ty == Tpointer && !t1->nextOf()->isMutable()) ||
|
|
!var->type->isMutable() ||
|
|
!var->type->isAssignable() ||
|
|
var->storage_class & STCmanifest
|
|
)
|
|
{
|
|
if (var->isCtorinit())
|
|
{ // It's only modifiable if inside the right constructor
|
|
modifyFieldVar(loc, sc, var->isVarDeclaration(), e1);
|
|
}
|
|
else
|
|
{
|
|
error("cannot modify const/immutable/inout expression %s", toChars());
|
|
}
|
|
}
|
|
else if (var->storage_class & STCnodefaultctor)
|
|
{
|
|
modifyFieldVar(loc, sc, var->isVarDeclaration(), e1);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void DotVarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
buf->writestring(var->toChars());
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
/* Things like:
|
|
* foo.bar!(args)
|
|
*/
|
|
|
|
DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs)
|
|
: UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
|
|
{
|
|
//printf("DotTemplateInstanceExp()\n");
|
|
this->ti = new TemplateInstance(loc, name);
|
|
this->ti->tiargs = tiargs;
|
|
}
|
|
|
|
Expression *DotTemplateInstanceExp::syntaxCopy()
|
|
{
|
|
DotTemplateInstanceExp *de = new DotTemplateInstanceExp(loc,
|
|
e1->syntaxCopy(),
|
|
ti->name,
|
|
TemplateInstance::arraySyntaxCopy(ti->tiargs));
|
|
return de;
|
|
}
|
|
|
|
TemplateDeclaration *DotTemplateInstanceExp::getTempdecl(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DotTemplateInstanceExp::getTempdecl('%s')\n", toChars());
|
|
#endif
|
|
if (!ti->tempdecl)
|
|
{
|
|
Expression *e = new DotIdExp(loc, e1, ti->name);
|
|
e = e->semantic(sc);
|
|
if (e->op == TOKdottd)
|
|
{
|
|
DotTemplateExp *dte = (DotTemplateExp *)e;
|
|
ti->tempdecl = dte->td;
|
|
}
|
|
else if (e->op == TOKimport)
|
|
{ ScopeExp *se = (ScopeExp *)e;
|
|
ti->tempdecl = se->sds->isTemplateDeclaration();
|
|
}
|
|
}
|
|
return ti->tempdecl;
|
|
}
|
|
|
|
Expression *DotTemplateInstanceExp::semantic(Scope *sc)
|
|
{
|
|
// Indicate we need to resolve by UFCS.
|
|
return semantic(sc, 0);
|
|
}
|
|
Expression *DotTemplateInstanceExp::semantic(Scope *sc, int flag)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DotTemplateInstanceExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
|
|
UnaExp::semantic(sc);
|
|
Expression *e = new DotIdExp(loc, e1, ti->name);
|
|
|
|
if (e1->op == TOKimport && ((ScopeExp *)e1)->sds->isModule())
|
|
e = ((DotIdExp *)e)->semantic(sc, 1);
|
|
else
|
|
{
|
|
unsigned errors = global.startGagging();
|
|
e = ((DotIdExp *)e)->semantic(sc, 1);
|
|
if (global.endGagging(errors) && !flag)
|
|
{
|
|
return resolveUFCSProperties(sc, this);
|
|
}
|
|
}
|
|
|
|
L1:
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
if (e->op == TOKdottd)
|
|
{
|
|
if (ti->errors)
|
|
return new ErrorExp();
|
|
DotTemplateExp *dte = (DotTemplateExp *)e;
|
|
TemplateDeclaration *td = dte->td;
|
|
Expression *eleft = dte->e1;
|
|
ti->tempdecl = td;
|
|
if (ti->needsTypeInference(sc))
|
|
{
|
|
e1 = eleft; // save result of semantic()
|
|
return this;
|
|
}
|
|
else
|
|
ti->semantic(sc);
|
|
if (!ti->inst) // if template failed to expand
|
|
return new ErrorExp();
|
|
Dsymbol *s = ti->inst->toAlias();
|
|
Declaration *v = s->isDeclaration();
|
|
if (v)
|
|
{
|
|
/* Fix for Bugzilla 4003
|
|
* The problem is a class template member function v returning a reference to the same
|
|
* type as the enclosing template instantiation. This results in a nested instantiation,
|
|
* which of course gets short circuited. The return type then gets set to
|
|
* the template instance type before instantiation, rather than after.
|
|
* We can detect this by the deco not being set. If so, go ahead and retry
|
|
* the return type semantic.
|
|
* The offending code is the return type from std.typecons.Tuple.slice:
|
|
* ref Tuple!(Types[from .. to]) slice(uint from, uint to)()
|
|
* {
|
|
* return *cast(typeof(return) *) &(field[from]);
|
|
* }
|
|
* and this line from the following unittest:
|
|
* auto s = a.slice!(1, 3);
|
|
* where s's type wound up not having semantic() run on it.
|
|
*/
|
|
if (v->type && !v->type->deco)
|
|
v->type = v->type->semantic(v->loc, sc);
|
|
|
|
e = new DotVarExp(loc, eleft, v);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
e = new ScopeExp(loc, ti);
|
|
e = new DotExp(loc, eleft, e);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (e->op == TOKimport)
|
|
{ ScopeExp *se = (ScopeExp *)e;
|
|
TemplateDeclaration *td = se->sds->isTemplateDeclaration();
|
|
if (!td)
|
|
{ error("%s is not a template", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
ti->tempdecl = td;
|
|
e = new ScopeExp(loc, ti);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (e->op == TOKdotexp)
|
|
{ DotExp *de = (DotExp *)e;
|
|
|
|
if (de->e2->op == TOKoverloadset)
|
|
{
|
|
return e;
|
|
}
|
|
|
|
if (de->e2->op == TOKimport)
|
|
{ // This should *really* be moved to ScopeExp::semantic()
|
|
ScopeExp *se = (ScopeExp *)de->e2;
|
|
de->e2 = new DsymbolExp(loc, se->sds);
|
|
de->e2 = de->e2->semantic(sc);
|
|
}
|
|
|
|
if (de->e2->op == TOKtemplate)
|
|
{ TemplateExp *te = (TemplateExp *) de->e2;
|
|
e = new DotTemplateExp(loc,de->e1,te->td);
|
|
}
|
|
else
|
|
goto Lerr;
|
|
|
|
e = e->semantic(sc);
|
|
if (e == de)
|
|
goto Lerr;
|
|
goto L1;
|
|
}
|
|
Lerr:
|
|
error("%s isn't a template", e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
void DotTemplateInstanceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
ti->toCBuffer(buf, hgs);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f, int hasOverloads)
|
|
: UnaExp(loc, TOKdelegate, sizeof(DelegateExp), e)
|
|
{
|
|
this->func = f;
|
|
this->hasOverloads = hasOverloads;
|
|
}
|
|
|
|
Expression *DelegateExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DelegateExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
e1 = e1->semantic(sc);
|
|
type = new TypeDelegate(func->type);
|
|
type = type->semantic(loc, sc);
|
|
AggregateDeclaration *ad = func->toParent()->isAggregateDeclaration();
|
|
if (func->needThis())
|
|
e1 = getRightThis(loc, sc, ad, e1, func);
|
|
if (ad && ad->isClassDeclaration() && ad->type != e1->type)
|
|
{ // A downcast is required for interfaces, see Bugzilla 3706
|
|
e1 = new CastExp(loc, e1, ad->type);
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void DelegateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writeByte('&');
|
|
if (!func->isNested())
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
}
|
|
buf->writestring(func->toChars());
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s)
|
|
: UnaExp(loc, TOKdottype, sizeof(DotTypeExp), e)
|
|
{
|
|
this->sym = s;
|
|
this->type = s->getType();
|
|
}
|
|
|
|
Expression *DotTypeExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DotTypeExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
UnaExp::semantic(sc);
|
|
return this;
|
|
}
|
|
|
|
void DotTypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
buf->writestring(sym->toChars());
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
CallExp::CallExp(Loc loc, Expression *e, Expressions *exps)
|
|
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
|
|
{
|
|
this->arguments = exps;
|
|
this->f = NULL;
|
|
}
|
|
|
|
CallExp::CallExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
|
|
{
|
|
this->arguments = NULL;
|
|
}
|
|
|
|
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1)
|
|
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
|
|
{
|
|
Expressions *arguments = new Expressions();
|
|
if (earg1)
|
|
{ arguments->setDim(1);
|
|
arguments->tdata()[0] = earg1;
|
|
}
|
|
this->arguments = arguments;
|
|
}
|
|
|
|
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2)
|
|
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
|
|
{
|
|
Expressions *arguments = new Expressions();
|
|
arguments->setDim(2);
|
|
arguments->tdata()[0] = earg1;
|
|
arguments->tdata()[1] = earg2;
|
|
|
|
this->arguments = arguments;
|
|
}
|
|
|
|
Expression *CallExp::syntaxCopy()
|
|
{
|
|
return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
|
|
}
|
|
|
|
|
|
Expression *CallExp::resolveUFCS(Scope *sc)
|
|
{
|
|
Expression *e = NULL;
|
|
DotIdExp *dotid;
|
|
DotTemplateInstanceExp *dotti;
|
|
Identifier *ident;
|
|
|
|
if (e1->op == TOKdot)
|
|
{
|
|
dotid = (DotIdExp *)e1;
|
|
ident = dotid->ident;
|
|
e = dotid->e1 = dotid->e1->semantic(sc);
|
|
if (e->op == TOKdotexp)
|
|
return NULL;
|
|
e = resolveProperties(sc, e);
|
|
}
|
|
else if (e1->op == TOKdotti)
|
|
{
|
|
dotti = (DotTemplateInstanceExp *)e1;
|
|
ident = dotti->ti->name;
|
|
e = dotti->e1 = dotti->e1->semantic(sc);
|
|
if (e->op == TOKdotexp)
|
|
return NULL;
|
|
e = resolveProperties(sc, e);
|
|
}
|
|
|
|
if (e && e->type)
|
|
{
|
|
if (e->op == TOKtype || e->op == TOKimport)
|
|
return NULL;
|
|
//printf("resolveUCSS %s, e->op = %s\n", toChars(), Token::toChars(e->op));
|
|
AggregateDeclaration *ad;
|
|
Expression *esave = e;
|
|
Lagain:
|
|
Type *t = e->type->toBasetype();
|
|
if (t->ty == Tpointer)
|
|
{ Type *tn = t->nextOf();
|
|
if (tn->ty == Tclass || tn->ty == Tstruct)
|
|
{
|
|
e = new PtrExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
t = e->type->toBasetype();
|
|
}
|
|
}
|
|
if (t->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t)->sym;
|
|
goto L1;
|
|
}
|
|
else if (t->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t)->sym;
|
|
L1:
|
|
if (ad->search(loc, ident, 0))
|
|
return NULL;
|
|
if (ad->aliasthis)
|
|
{
|
|
e = resolveAliasThis(sc, e);
|
|
goto Lagain;
|
|
}
|
|
if (ad->search(loc, Id::opDot, 0))
|
|
{
|
|
e = new DotIdExp(e->loc, e, Id::opDot);
|
|
e = e->semantic(sc);
|
|
e = resolveProperties(sc, e);
|
|
goto Lagain;
|
|
}
|
|
if (ad->search(loc, Id::opDispatch, 0))
|
|
return NULL;
|
|
e = esave;
|
|
goto Lshift;
|
|
}
|
|
else if ((t->isTypeBasic() && t->ty != Tvoid) ||
|
|
t->ty == Tenum || t->ty == Tnull)
|
|
{
|
|
goto Lshift;
|
|
}
|
|
else if (t->ty == Taarray && e1->op == TOKdot)
|
|
{
|
|
if (ident == Id::remove)
|
|
{
|
|
/* Transform:
|
|
* aa.remove(arg) into delete aa[arg]
|
|
*/
|
|
if (!arguments || arguments->dim != 1)
|
|
{ error("expected key as argument to aa.remove()");
|
|
return new ErrorExp();
|
|
}
|
|
if (!e->type->isMutable())
|
|
{ const char *p = NULL;
|
|
if (e->type->isConst())
|
|
p = "const";
|
|
else if (e->type->isImmutable())
|
|
p = "immutable";
|
|
else
|
|
p = "inout";
|
|
error("cannot remove key from %s associative array %s", p, e->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
Expression *key = arguments->tdata()[0];
|
|
key = key->semantic(sc);
|
|
key = resolveProperties(sc, key);
|
|
|
|
TypeAArray *taa = (TypeAArray *)t;
|
|
key = key->implicitCastTo(sc, taa->index);
|
|
|
|
if (!key->rvalue())
|
|
return new ErrorExp();
|
|
|
|
return new RemoveExp(loc, e, key);
|
|
}
|
|
else if (ident == Id::apply || ident == Id::applyReverse)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{ TypeAArray *taa = (TypeAArray *)t;
|
|
assert(taa->ty == Taarray);
|
|
StructDeclaration *sd = taa->getImpl();
|
|
Dsymbol *s = sd->search(0, ident, 2);
|
|
if (s)
|
|
return NULL;
|
|
goto Lshift;
|
|
}
|
|
}
|
|
else if (t->ty == Tarray || t->ty == Tsarray)
|
|
{
|
|
Lshift:
|
|
if (!arguments)
|
|
arguments = new Expressions();
|
|
arguments->shift(e);
|
|
if (e1->op == TOKdot)
|
|
{
|
|
/* Transform:
|
|
* array.id(args) into .id(array,args)
|
|
*/
|
|
#if DMDV2
|
|
e1 = new DotIdExp(dotid->loc,
|
|
new IdentifierExp(dotid->loc, Id::empty),
|
|
ident);
|
|
#else
|
|
e1 = new IdentifierExp(dotid->loc, ident);
|
|
#endif
|
|
}
|
|
else if (e1->op == TOKdotti)
|
|
{
|
|
/* Transform:
|
|
* array.foo!(tiargs)(args) into .foo!(tiargs)(array,args)
|
|
*/
|
|
#if DMDV2
|
|
e1 = new DotTemplateInstanceExp(dotti->loc,
|
|
new IdentifierExp(dotti->loc, Id::empty),
|
|
dotti->ti->name, dotti->ti->tiargs);
|
|
#else
|
|
e1 = new ScopeExp(dotti->loc, dotti->ti);
|
|
#endif
|
|
}
|
|
//printf("-> this = %s\n", toChars());
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Expression *CallExp::semantic(Scope *sc)
|
|
{
|
|
Type *t1;
|
|
int istemp;
|
|
Objects *targsi = NULL; // initial list of template arguments
|
|
TemplateInstance *tierror = NULL;
|
|
Expression *ethis = NULL;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("CallExp::semantic() %s\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this; // semantic() already run
|
|
#if 0
|
|
if (arguments && arguments->dim)
|
|
{
|
|
Expression *earg = arguments->tdata()[0];
|
|
earg->print();
|
|
if (earg->type) earg->type->print();
|
|
}
|
|
#endif
|
|
|
|
if (e1->op == TOKcomma)
|
|
{ /* Rewrite (a,b)(args) as (a,(b(args)))
|
|
*/
|
|
CommaExp *ce = (CommaExp *)e1;
|
|
|
|
e1 = ce->e2;
|
|
e1->type = ce->type;
|
|
ce->e2 = this;
|
|
ce->type = NULL;
|
|
return ce->semantic(sc);
|
|
}
|
|
|
|
if (e1->op == TOKdelegate)
|
|
{ DelegateExp *de = (DelegateExp *)e1;
|
|
|
|
e1 = new DotVarExp(de->loc, de->e1, de->func);
|
|
return semantic(sc);
|
|
}
|
|
|
|
if (e1->op == TOKfunction)
|
|
{ FuncExp *fe = (FuncExp *)e1;
|
|
|
|
arguments = arrayExpressionSemantic(arguments, sc);
|
|
preFunctionParameters(loc, sc, arguments);
|
|
e1 = fe->semantic(sc, arguments);
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
}
|
|
|
|
Expression *e = resolveUFCS(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
#if 1
|
|
/* This recognizes:
|
|
* foo!(tiargs)(funcargs)
|
|
*/
|
|
if (e1->op == TOKimport && !e1->type)
|
|
{ ScopeExp *se = (ScopeExp *)e1;
|
|
TemplateInstance *ti = se->sds->isTemplateInstance();
|
|
if (ti && !ti->semanticRun)
|
|
{
|
|
/* Attempt to instantiate ti. If that works, go with it.
|
|
* If not, go with partial explicit specialization.
|
|
*/
|
|
unsigned olderrors = global.errors;
|
|
ti->semanticTiargs(sc);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
if (ti->needsTypeInference(sc))
|
|
{
|
|
/* Go with partial explicit specialization
|
|
*/
|
|
targsi = ti->tiargs;
|
|
tierror = ti; // for error reporting
|
|
e1 = new IdentifierExp(loc, ti->name);
|
|
}
|
|
else
|
|
{
|
|
ti->semantic(sc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This recognizes:
|
|
* expr.foo!(tiargs)(funcargs)
|
|
*/
|
|
Ldotti:
|
|
if (e1->op == TOKdotti && !e1->type)
|
|
{ DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)e1;
|
|
TemplateInstance *ti = se->ti;
|
|
if (!ti->semanticRun)
|
|
{
|
|
/* Attempt to instantiate ti. If that works, go with it.
|
|
* If not, go with partial explicit specialization.
|
|
*/
|
|
ti->semanticTiargs(sc);
|
|
#if 0
|
|
Expression *etmp = e1->trySemantic(sc);
|
|
if (etmp)
|
|
e1 = etmp; // it worked
|
|
else // didn't work
|
|
{
|
|
targsi = ti->tiargs;
|
|
tierror = ti; // for error reporting
|
|
e1 = new DotIdExp(loc, se->e1, ti->name);
|
|
}
|
|
#else
|
|
if (!ti->tempdecl)
|
|
{
|
|
se->getTempdecl(sc);
|
|
}
|
|
if (ti->tempdecl && ti->needsTypeInference(sc))
|
|
{
|
|
/* Go with partial explicit specialization
|
|
*/
|
|
targsi = ti->tiargs;
|
|
tierror = ti; // for error reporting
|
|
e1 = new DotIdExp(loc, se->e1, ti->name);
|
|
}
|
|
else
|
|
{
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
istemp = 0;
|
|
Lagain:
|
|
//printf("Lagain: %s\n", toChars());
|
|
f = NULL;
|
|
if (e1->op == TOKthis || e1->op == TOKsuper)
|
|
{
|
|
// semantic() run later for these
|
|
}
|
|
else
|
|
{
|
|
if (e1->op == TOKdot)
|
|
{ DotIdExp *die = (DotIdExp *)e1;
|
|
e1 = die->semantic(sc);
|
|
/* Look for e1 having been rewritten to expr.opDispatch!(string)
|
|
* We handle such earlier, so go back.
|
|
* Note that in the rewrite, we carefully did not run semantic() on e1
|
|
*/
|
|
if (e1->op == TOKdotti && !e1->type)
|
|
{
|
|
goto Ldotti;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
static int nest;
|
|
if (++nest > 500)
|
|
{
|
|
error("recursive evaluation of %s", toChars());
|
|
--nest;
|
|
return new ErrorExp();
|
|
}
|
|
UnaExp::semantic(sc);
|
|
--nest;
|
|
}
|
|
|
|
/* Look for e1 being a lazy parameter
|
|
*/
|
|
if (e1->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)e1;
|
|
|
|
if (ve->var->storage_class & STClazy)
|
|
{
|
|
// lazy paramaters can be called without violating purity and safety
|
|
Type *tw = ve->var->type;
|
|
Type *tc = ve->var->type->substWildTo(MODconst);
|
|
TypeFunction *tf = new TypeFunction(NULL, tc, 0, LINKd, STCsafe | STCpure);
|
|
(tf = (TypeFunction *)tf->semantic(loc, sc))->next = tw; // hack for bug7757
|
|
TypeDelegate *t = new TypeDelegate(tf);
|
|
ve->type = t->semantic(loc, sc);
|
|
}
|
|
}
|
|
|
|
if (e1->op == TOKimport)
|
|
{ // Perhaps this should be moved to ScopeExp::semantic()
|
|
ScopeExp *se = (ScopeExp *)e1;
|
|
e1 = new DsymbolExp(loc, se->sds);
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
else if (e1->op == TOKsymoff && ((SymOffExp *)e1)->hasOverloads)
|
|
{
|
|
SymOffExp *se = (SymOffExp *)e1;
|
|
e1 = new VarExp(se->loc, se->var, 1);
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
#if 1 // patch for #540 by Oskar Linde
|
|
else if (e1->op == TOKdotexp)
|
|
{
|
|
DotExp *de = (DotExp *) e1;
|
|
|
|
if (de->e2->op == TOKoverloadset)
|
|
{
|
|
ethis = de->e1;
|
|
e1 = de->e2;
|
|
}
|
|
|
|
if (de->e2->op == TOKimport)
|
|
{ // This should *really* be moved to ScopeExp::semantic()
|
|
ScopeExp *se = (ScopeExp *)de->e2;
|
|
de->e2 = new DsymbolExp(loc, se->sds);
|
|
de->e2 = de->e2->semantic(sc);
|
|
}
|
|
|
|
if (de->e2->op == TOKtemplate)
|
|
{ TemplateExp *te = (TemplateExp *) de->e2;
|
|
e1 = new DotTemplateExp(loc,de->e1,te->td);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
t1 = NULL;
|
|
if (e1->type)
|
|
t1 = e1->type->toBasetype();
|
|
|
|
// Check for call operator overload
|
|
if (t1)
|
|
{ AggregateDeclaration *ad;
|
|
|
|
if (t1->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t1)->sym;
|
|
#if DMDV2
|
|
// First look for constructor
|
|
if (ad->ctor && arguments && arguments->dim)
|
|
{
|
|
// Create variable that will get constructed
|
|
Identifier *idtmp = Lexer::uniqueId("__ctmp");
|
|
VarDeclaration *tmp = new VarDeclaration(loc, t1, idtmp, NULL);
|
|
tmp->storage_class |= STCctfe;
|
|
Expression *av = new DeclarationExp(loc, tmp);
|
|
av = new CommaExp(loc, av, new VarExp(loc, tmp));
|
|
|
|
Expression *e;
|
|
CtorDeclaration *cf = ad->ctor->isCtorDeclaration();
|
|
if (cf)
|
|
e = new DotVarExp(loc, av, cf, 1);
|
|
else
|
|
{ TemplateDeclaration *td = ad->ctor->isTemplateDeclaration();
|
|
assert(td);
|
|
e = new DotTemplateExp(loc, av, td);
|
|
}
|
|
e = new CallExp(loc, e, arguments);
|
|
#if !STRUCTTHISREF
|
|
/* Constructors return a pointer to the instance
|
|
*/
|
|
e = new PtrExp(loc, e);
|
|
#endif
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#endif
|
|
// No constructor, look for overload of opCall
|
|
if (search_function(ad, Id::call))
|
|
goto L1; // overload of opCall, therefore it's a call
|
|
|
|
if (e1->op != TOKtype)
|
|
{ error("%s %s does not overload ()", ad->kind(), ad->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
/* It's a struct literal
|
|
*/
|
|
Expression *e = new StructLiteralExp(loc, (StructDeclaration *)ad, arguments, e1->type);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (t1->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t1)->sym;
|
|
goto L1;
|
|
L1:
|
|
// Rewrite as e1.call(arguments)
|
|
Expression *e = new DotIdExp(loc, e1, Id::call);
|
|
e = new CallExp(loc, e, arguments);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
arguments = arrayExpressionSemantic(arguments, sc);
|
|
preFunctionParameters(loc, sc, arguments);
|
|
|
|
// If there was an error processing any argument, or the call,
|
|
// return an error without trying to resolve the function call.
|
|
if (arguments && arguments->dim)
|
|
{
|
|
for (size_t k = 0; k < arguments->dim; k++)
|
|
{ Expression *checkarg = arguments->tdata()[k];
|
|
if (checkarg->op == TOKerror)
|
|
return checkarg;
|
|
}
|
|
}
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
|
|
// If there was an error processing any template argument,
|
|
// return an error without trying to resolve the template.
|
|
if (targsi && targsi->dim)
|
|
{
|
|
for (size_t k = 0; k < targsi->dim; k++)
|
|
{ Object *o = targsi->tdata()[k];
|
|
if (isError(o))
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
|
|
if (e1->op == TOKdotvar && t1->ty == Tfunction ||
|
|
e1->op == TOKdottd)
|
|
{
|
|
DotVarExp *dve;
|
|
DotTemplateExp *dte;
|
|
AggregateDeclaration *ad;
|
|
UnaExp *ue = (UnaExp *)(e1);
|
|
|
|
if (e1->op == TOKdotvar)
|
|
{ // Do overload resolution
|
|
dve = (DotVarExp *)(e1);
|
|
|
|
f = dve->var->isFuncDeclaration();
|
|
assert(f);
|
|
f = f->overloadResolve(loc, ue->e1, arguments);
|
|
|
|
ad = f->toParent()->isAggregateDeclaration();
|
|
}
|
|
else
|
|
{ dte = (DotTemplateExp *)(e1);
|
|
TemplateDeclaration *td = dte->td;
|
|
assert(td);
|
|
if (!arguments)
|
|
// Should fix deduceFunctionTemplate() so it works on NULL argument
|
|
arguments = new Expressions();
|
|
f = td->deduceFunctionTemplate(sc, loc, targsi, ue->e1, arguments);
|
|
if (!f)
|
|
return new ErrorExp();
|
|
ad = td->toParent()->isAggregateDeclaration();
|
|
}
|
|
if (f->needThis())
|
|
{
|
|
ue->e1 = getRightThis(loc, sc, ad, ue->e1, f);
|
|
ethis = ue->e1;
|
|
}
|
|
|
|
/* Cannot call public functions from inside invariant
|
|
* (because then the invariant would have infinite recursion)
|
|
*/
|
|
if (sc->func && sc->func->isInvariantDeclaration() &&
|
|
ue->e1->op == TOKthis &&
|
|
f->addPostInvariant()
|
|
)
|
|
{
|
|
error("cannot call public/export function %s from invariant", f->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
checkDeprecated(sc, f);
|
|
#if DMDV2
|
|
checkPurity(sc, f);
|
|
checkSafety(sc, f);
|
|
#endif
|
|
accessCheck(loc, sc, ue->e1, f);
|
|
if (!f->needThis())
|
|
{
|
|
VarExp *ve = new VarExp(loc, f);
|
|
if ((ue->e1)->op == TOKtype) // just a FQN
|
|
e1 = ve;
|
|
else // things like (new Foo).bar()
|
|
e1 = new CommaExp(loc, ue->e1, ve);
|
|
e1->type = f->type;
|
|
}
|
|
else
|
|
{
|
|
if (e1->op == TOKdotvar)
|
|
{
|
|
dve->var = f;
|
|
e1->type = f->type;
|
|
}
|
|
else
|
|
{
|
|
e1 = new DotVarExp(loc, dte->e1, f);
|
|
e1 = e1->semantic(sc);
|
|
}
|
|
#if 0
|
|
printf("ue->e1 = %s\n", ue->e1->toChars());
|
|
printf("f = %s\n", f->toChars());
|
|
printf("t = %s\n", t->toChars());
|
|
printf("e1 = %s\n", e1->toChars());
|
|
printf("e1->type = %s\n", e1->type->toChars());
|
|
#endif
|
|
// Const member function can take const/immutable/mutable/inout this
|
|
if (!(f->type->isConst()))
|
|
{
|
|
// Check for const/immutable compatibility
|
|
Type *tthis = ue->e1->type->toBasetype();
|
|
if (tthis->ty == Tpointer)
|
|
tthis = tthis->nextOf()->toBasetype();
|
|
#if 0 // this checking should have been already done
|
|
if (f->type->isImmutable())
|
|
{
|
|
if (tthis->mod != MODimmutable)
|
|
error("%s can only be called with an immutable object", e1->toChars());
|
|
}
|
|
else if (f->type->isShared())
|
|
{
|
|
if (tthis->mod != MODimmutable &&
|
|
tthis->mod != MODshared &&
|
|
tthis->mod != (MODshared | MODconst))
|
|
error("shared %s can only be called with a shared or immutable object", e1->toChars());
|
|
}
|
|
else
|
|
{
|
|
if (tthis->mod != 0)
|
|
{ //printf("mod = %x\n", tthis->mod);
|
|
error("%s can only be called with a mutable object, not %s", e1->toChars(), tthis->toChars());
|
|
}
|
|
}
|
|
#endif
|
|
/* Cannot call mutable method on a final struct
|
|
*/
|
|
if (tthis->ty == Tstruct &&
|
|
ue->e1->op == TOKvar)
|
|
{ VarExp *v = (VarExp *)ue->e1;
|
|
if (v->var->storage_class & STCfinal)
|
|
error("cannot call mutable method on final struct");
|
|
}
|
|
}
|
|
|
|
// See if we need to adjust the 'this' pointer
|
|
AggregateDeclaration *ad = f->isThis();
|
|
ClassDeclaration *cd = ue->e1->type->isClassHandle();
|
|
if (ad && cd && ad->isClassDeclaration() && ad != cd &&
|
|
ue->e1->op != TOKsuper)
|
|
{
|
|
ue->e1 = ue->e1->castTo(sc, ad->type); //new CastExp(loc, ue->e1, ad->type);
|
|
ue->e1 = ue->e1->semantic(sc);
|
|
}
|
|
}
|
|
t1 = e1->type;
|
|
}
|
|
else if (e1->op == TOKsuper)
|
|
{
|
|
// Base class constructor call
|
|
ClassDeclaration *cd = NULL;
|
|
|
|
if (sc->func && sc->func->isThis())
|
|
cd = sc->func->isThis()->isClassDeclaration();
|
|
if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration())
|
|
{
|
|
error("super class constructor call must be in a constructor");
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
if (!cd->baseClass->ctor)
|
|
{ error("no super class constructor for %s", cd->baseClass->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
if (!sc->intypeof)
|
|
{
|
|
#if 0
|
|
if (sc->callSuper & (CSXthis | CSXsuper))
|
|
error("reference to this before super()");
|
|
#endif
|
|
if (sc->noctor || sc->callSuper & CSXlabel)
|
|
error("constructor calls not allowed in loops or after labels");
|
|
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
|
|
error("multiple constructor calls");
|
|
sc->callSuper |= CSXany_ctor | CSXsuper_ctor;
|
|
}
|
|
|
|
f = resolveFuncCall(sc, loc, cd->baseClass->ctor, NULL, NULL, arguments, 0);
|
|
accessCheck(loc, sc, NULL, f);
|
|
checkDeprecated(sc, f);
|
|
#if DMDV2
|
|
checkPurity(sc, f);
|
|
checkSafety(sc, f);
|
|
#endif
|
|
e1 = new DotVarExp(e1->loc, e1, f);
|
|
e1 = e1->semantic(sc);
|
|
t1 = e1->type;
|
|
}
|
|
}
|
|
}
|
|
else if (e1->op == TOKthis)
|
|
{
|
|
// same class constructor call
|
|
AggregateDeclaration *cd = NULL;
|
|
|
|
if (sc->func && sc->func->isThis())
|
|
cd = sc->func->isThis()->isAggregateDeclaration();
|
|
if (!cd || !sc->func->isCtorDeclaration())
|
|
{
|
|
error("constructor call must be in a constructor");
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
if (!sc->intypeof)
|
|
{
|
|
#if 0
|
|
if (sc->callSuper & (CSXthis | CSXsuper))
|
|
error("reference to this before super()");
|
|
#endif
|
|
if (sc->noctor || sc->callSuper & CSXlabel)
|
|
error("constructor calls not allowed in loops or after labels");
|
|
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
|
|
error("multiple constructor calls");
|
|
sc->callSuper |= CSXany_ctor | CSXthis_ctor;
|
|
}
|
|
|
|
f = resolveFuncCall(sc, loc, cd->ctor, NULL, NULL, arguments, 0);
|
|
checkDeprecated(sc, f);
|
|
#if DMDV2
|
|
checkPurity(sc, f);
|
|
checkSafety(sc, f);
|
|
#endif
|
|
e1 = new DotVarExp(e1->loc, e1, f);
|
|
e1 = e1->semantic(sc);
|
|
t1 = e1->type;
|
|
|
|
// BUG: this should really be done by checking the static
|
|
// call graph
|
|
if (f == sc->func)
|
|
{ error("cyclic constructor call");
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
}
|
|
else if (e1->op == TOKoverloadset)
|
|
{
|
|
OverExp *eo = (OverExp *)e1;
|
|
FuncDeclaration *f = NULL;
|
|
Dsymbol *s = NULL;
|
|
for (size_t i = 0; i < eo->vars->a.dim; i++)
|
|
{ s = eo->vars->a.tdata()[i];
|
|
FuncDeclaration *f2 = s->isFuncDeclaration();
|
|
if (f2)
|
|
{
|
|
f2 = f2->overloadResolve(loc, ethis, arguments, 1);
|
|
}
|
|
else
|
|
{ TemplateDeclaration *td = s->isTemplateDeclaration();
|
|
assert(td);
|
|
f2 = td->deduceFunctionTemplate(sc, loc, targsi, ethis, arguments, 1);
|
|
}
|
|
if (f2)
|
|
{ if (f)
|
|
/* Error if match in more than one overload set,
|
|
* even if one is a 'better' match than the other.
|
|
*/
|
|
ScopeDsymbol::multiplyDefined(loc, f, f2);
|
|
else
|
|
f = f2;
|
|
}
|
|
}
|
|
if (!f)
|
|
{ /* No overload matches
|
|
*/
|
|
error("no overload matches for %s", s->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (ethis)
|
|
e1 = new DotVarExp(loc, ethis, f);
|
|
else
|
|
e1 = new VarExp(loc, f);
|
|
goto Lagain;
|
|
}
|
|
else if (!t1)
|
|
{
|
|
error("function expected before (), not '%s'", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else if (t1->ty != Tfunction)
|
|
{
|
|
TypeFunction *tf;
|
|
const char *p;
|
|
if (t1->ty == Tdelegate)
|
|
{ TypeDelegate *td = (TypeDelegate *)t1;
|
|
assert(td->next->ty == Tfunction);
|
|
tf = (TypeFunction *)(td->next);
|
|
p = "delegate";
|
|
}
|
|
else if (t1->ty == Tpointer && ((TypePointer *)t1)->next->ty == Tfunction)
|
|
{
|
|
tf = (TypeFunction *)(((TypePointer *)t1)->next);
|
|
p = "function pointer";
|
|
}
|
|
else if (e1->op == TOKtemplate)
|
|
{
|
|
TemplateExp *te = (TemplateExp *)e1;
|
|
f = te->td->deduceFunctionTemplate(sc, loc, targsi, NULL, arguments);
|
|
if (!f)
|
|
{ if (tierror)
|
|
tierror->error("errors instantiating template"); // give better error message
|
|
return new ErrorExp();
|
|
}
|
|
if (f->needThis() && hasThis(sc))
|
|
{
|
|
// Supply an implicit 'this', as in
|
|
// this.ident
|
|
|
|
e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td);
|
|
goto Lagain;
|
|
}
|
|
|
|
e1 = new VarExp(loc, f);
|
|
goto Lagain;
|
|
}
|
|
else
|
|
{ error("function expected before (), not %s of type %s", e1->toChars(), e1->type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug))
|
|
{
|
|
if (sc->func->setImpure())
|
|
error("pure function '%s' cannot call impure %s '%s'", sc->func->toChars(), p, e1->toChars());
|
|
}
|
|
if (sc->func && tf->trust <= TRUSTsystem)
|
|
{
|
|
if (sc->func->setUnsafe())
|
|
error("safe function '%s' cannot call system %s '%s'", sc->func->toChars(), p, e1->toChars());
|
|
}
|
|
|
|
if (!tf->callMatch(NULL, arguments))
|
|
{
|
|
OutBuffer buf;
|
|
|
|
buf.writeByte('(');
|
|
if (arguments)
|
|
{
|
|
HdrGenState hgs;
|
|
|
|
argExpTypesToCBuffer(&buf, arguments, &hgs);
|
|
buf.writeByte(')');
|
|
if (ethis)
|
|
ethis->type->modToBuffer(&buf);
|
|
}
|
|
else
|
|
buf.writeByte(')');
|
|
|
|
//printf("tf = %s, args = %s\n", tf->deco, arguments->tdata()[0]->type->deco);
|
|
::error(loc, "%s %s %s is not callable using argument types %s",
|
|
p, e1->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs),
|
|
buf.toChars());
|
|
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (t1->ty == Tpointer)
|
|
{
|
|
Expression *e = new PtrExp(loc, e1);
|
|
e->type = tf;
|
|
e1 = e;
|
|
}
|
|
t1 = tf;
|
|
}
|
|
else if (e1->op == TOKvar)
|
|
{
|
|
// Do overload resolution
|
|
VarExp *ve = (VarExp *)e1;
|
|
|
|
f = ve->var->isFuncDeclaration();
|
|
assert(f);
|
|
|
|
if (ve->hasOverloads)
|
|
f = f->overloadResolve(loc, NULL, arguments);
|
|
checkDeprecated(sc, f);
|
|
#if DMDV2
|
|
checkPurity(sc, f);
|
|
checkSafety(sc, f);
|
|
#endif
|
|
f->checkNestedReference(sc, loc);
|
|
|
|
if (f->needThis() && hasThis(sc))
|
|
{
|
|
// Supply an implicit 'this', as in
|
|
// this.ident
|
|
|
|
e1 = new DotVarExp(loc, new ThisExp(loc), f);
|
|
goto Lagain;
|
|
}
|
|
|
|
accessCheck(loc, sc, NULL, f);
|
|
|
|
ethis = NULL;
|
|
|
|
ve->var = f;
|
|
// ve->hasOverloads = 0;
|
|
ve->type = f->type;
|
|
t1 = f->type;
|
|
}
|
|
assert(t1->ty == Tfunction);
|
|
TypeFunction *tf = (TypeFunction *)(t1);
|
|
|
|
if (!arguments)
|
|
arguments = new Expressions();
|
|
int olderrors = global.errors;
|
|
type = functionParameters(loc, sc, tf, ethis, arguments, f);
|
|
if (olderrors != global.errors)
|
|
return new ErrorExp();
|
|
|
|
if (!type)
|
|
{
|
|
error("forward reference to inferred return type of function call %s", toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (f && f->tintro)
|
|
{
|
|
Type *t = type;
|
|
int offset = 0;
|
|
TypeFunction *tf = (TypeFunction *)f->tintro;
|
|
|
|
if (tf->next->isBaseOf(t, &offset) && offset)
|
|
{
|
|
type = tf->next;
|
|
return castTo(sc, t);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
#if DMDV2
|
|
int CallExp::isLvalue()
|
|
{
|
|
Type *tb = e1->type->toBasetype();
|
|
if (tb->ty == Tfunction && ((TypeFunction *)tb)->isref)
|
|
{
|
|
if (e1->op == TOKdotvar)
|
|
if (((DotVarExp *)e1)->var->isCtorDeclaration())
|
|
return 0;
|
|
return 1; // function returns a reference
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
Expression *CallExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
if (isLvalue())
|
|
return this;
|
|
return Expression::toLvalue(sc, e);
|
|
}
|
|
|
|
Expression *CallExp::addDtorHook(Scope *sc)
|
|
{
|
|
/* Only need to add dtor hook if it's a type that needs destruction.
|
|
* Use same logic as VarDeclaration::callScopeDtor()
|
|
*/
|
|
|
|
if (e1->type && e1->type->ty == Tfunction)
|
|
{
|
|
TypeFunction *tf = (TypeFunction *)e1->type;
|
|
if (tf->isref)
|
|
return this;
|
|
}
|
|
|
|
Type *tv = type->toBasetype();
|
|
while (tv->ty == Tsarray)
|
|
{ TypeSArray *ta = (TypeSArray *)tv;
|
|
tv = tv->nextOf()->toBasetype();
|
|
}
|
|
if (tv->ty == Tstruct)
|
|
{ TypeStruct *ts = (TypeStruct *)tv;
|
|
StructDeclaration *sd = ts->sym;
|
|
if (sd->dtor)
|
|
{ /* Type needs destruction, so declare a tmp
|
|
* which the back end will recognize and call dtor on
|
|
*/
|
|
Identifier *idtmp = Lexer::uniqueId("__tmpfordtor");
|
|
VarDeclaration *tmp = new VarDeclaration(loc, type, idtmp, new ExpInitializer(loc, this));
|
|
tmp->storage_class |= STCctfe;
|
|
Expression *ae = new DeclarationExp(loc, tmp);
|
|
Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp));
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
Lnone:
|
|
return this;
|
|
}
|
|
|
|
void CallExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
if (e1->op == TOKtype)
|
|
/* Avoid parens around type to prevent forbidden cast syntax:
|
|
* (sometype)(arg1)
|
|
* This is ok since types in constructor calls
|
|
* can never depend on parens anyway
|
|
*/
|
|
e1->toCBuffer(buf, hgs);
|
|
else
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
buf->writeByte('(');
|
|
argsToCBuffer(buf, arguments, hgs);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
AddrExp::AddrExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *AddrExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("AddrExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
UnaExp::semantic(sc);
|
|
if (e1->type == Type::terror)
|
|
return new ErrorExp();
|
|
e1 = e1->toLvalue(sc, NULL);
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
if (!e1->type)
|
|
{
|
|
error("cannot take address of %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (!e1->type->deco)
|
|
{
|
|
/* No deco means semantic() was not run on the type.
|
|
* We have to run semantic() on the symbol to get the right type:
|
|
* auto x = &bar;
|
|
* pure: int bar() { return 1;}
|
|
* otherwise the 'pure' is missing from the type assigned to x.
|
|
*/
|
|
|
|
error("forward reference to %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
type = e1->type->pointerTo();
|
|
|
|
// See if this should really be a delegate
|
|
if (e1->op == TOKdotvar)
|
|
{
|
|
DotVarExp *dve = (DotVarExp *)e1;
|
|
FuncDeclaration *f = dve->var->isFuncDeclaration();
|
|
|
|
if (f)
|
|
{
|
|
if (!dve->hasOverloads)
|
|
f->tookAddressOf++;
|
|
Expression *e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
else if (e1->op == TOKvar)
|
|
{
|
|
VarExp *ve = (VarExp *)e1;
|
|
|
|
VarDeclaration *v = ve->var->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
if (!v->canTakeAddressOf())
|
|
{ error("cannot take address of %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (sc->func && !sc->intypeof && !v->isDataseg())
|
|
{
|
|
if (sc->func->setUnsafe())
|
|
{
|
|
error("cannot take address of %s %s in @safe function %s",
|
|
v->isParameter() ? "parameter" : "local",
|
|
v->toChars(),
|
|
sc->func->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
FuncDeclaration *f = ve->var->isFuncDeclaration();
|
|
|
|
if (f)
|
|
{
|
|
if (!ve->hasOverloads ||
|
|
/* Because nested functions cannot be overloaded,
|
|
* mark here that we took its address because castTo()
|
|
* may not be called with an exact match.
|
|
*/
|
|
f->isNested())
|
|
f->tookAddressOf++;
|
|
if (f->isNested())
|
|
{
|
|
if (f->isFuncLiteralDeclaration())
|
|
{
|
|
if (!f->FuncDeclaration::isNested())
|
|
{ /* Supply a 'null' for a this pointer if no this is available
|
|
*/
|
|
Expression *e = new DelegateExp(loc, new NullExp(loc, Type::tnull), f, ve->hasOverloads);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
Expression *e = new DelegateExp(loc, e1, f, ve->hasOverloads);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
if (f->needThis() && hasThis(sc))
|
|
{
|
|
/* Should probably supply 'this' after overload resolution,
|
|
* not before.
|
|
*/
|
|
Expression *ethis = new ThisExp(loc);
|
|
Expression *e = new DelegateExp(loc, ethis, f, ve->hasOverloads);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
}
|
|
return optimize(WANTvalue);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void AddrExp::checkEscape()
|
|
{
|
|
e1->checkEscapeRef();
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
PtrExp::PtrExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
|
|
{
|
|
// if (e->type)
|
|
// type = ((TypePointer *)e->type)->next;
|
|
}
|
|
|
|
PtrExp::PtrExp(Loc loc, Expression *e, Type *t)
|
|
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
|
|
{
|
|
type = t;
|
|
}
|
|
|
|
Expression *PtrExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("PtrExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
Expression *e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
Type *tb = e1->type->toBasetype();
|
|
switch (tb->ty)
|
|
{
|
|
case Tpointer:
|
|
type = ((TypePointer *)tb)->next;
|
|
break;
|
|
|
|
case Tsarray:
|
|
case Tarray:
|
|
if (!global.params.useDeprecated)
|
|
error("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars());
|
|
type = ((TypeArray *)tb)->next;
|
|
e1 = e1->castTo(sc, type->pointerTo());
|
|
break;
|
|
|
|
default:
|
|
error("can only * a pointer, not a '%s'", e1->type->toChars());
|
|
case Terror:
|
|
return new ErrorExp();
|
|
}
|
|
if (!rvalue())
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
#if DMDV2
|
|
int PtrExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
void PtrExp::checkEscapeRef()
|
|
{
|
|
e1->checkEscape();
|
|
}
|
|
|
|
Expression *PtrExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
#if 0
|
|
tym = tybasic(e1->ET->Tty);
|
|
if (!(tyscalar(tym) ||
|
|
tym == TYstruct ||
|
|
tym == TYarray && e->Eoper == TOKaddr))
|
|
synerr(EM_lvalue); // lvalue expected
|
|
#endif
|
|
return this;
|
|
}
|
|
|
|
#if DMDV2
|
|
Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars());
|
|
|
|
if (e1->op == TOKsymoff)
|
|
{ SymOffExp *se = (SymOffExp *)e1;
|
|
se->var->checkModify(loc, sc, type);
|
|
//return toLvalue(sc, e);
|
|
}
|
|
|
|
return Expression::modifiableLvalue(sc, e);
|
|
}
|
|
#endif
|
|
|
|
void PtrExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writeByte('*');
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
NegExp::NegExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKneg, sizeof(NegExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *NegExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("NegExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
e1->checkNoBool();
|
|
if (!e1->isArrayOperand())
|
|
e1->checkArithmetic();
|
|
type = e1->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
UAddExp::UAddExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKuadd, sizeof(UAddExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *UAddExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("UAddExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
assert(!type);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
e1->checkNoBool();
|
|
e1->checkArithmetic();
|
|
return e1;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ComExp::ComExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKtilde, sizeof(ComExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *ComExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
e1->checkNoBool();
|
|
if (!e1->isArrayOperand())
|
|
e1 = e1->checkIntegral();
|
|
type = e1->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
NotExp::NotExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKnot, sizeof(NotExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *NotExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
{ // Note there is no operator overload
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->checkToBoolean(sc);
|
|
type = Type::tboolean;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
int NotExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************/
|
|
|
|
BoolExp::BoolExp(Loc loc, Expression *e, Type *t)
|
|
: UnaExp(loc, TOKtobool, sizeof(BoolExp), e)
|
|
{
|
|
type = t;
|
|
}
|
|
|
|
Expression *BoolExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
{ // Note there is no operator overload
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->checkToBoolean(sc);
|
|
type = Type::tboolean;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
int BoolExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DeleteExp::DeleteExp(Loc loc, Expression *e)
|
|
: UnaExp(loc, TOKdelete, sizeof(DeleteExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *DeleteExp::semantic(Scope *sc)
|
|
{
|
|
Type *tb;
|
|
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->modifiableLvalue(sc, NULL);
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
type = Type::tvoid;
|
|
|
|
tb = e1->type->toBasetype();
|
|
switch (tb->ty)
|
|
{ case Tclass:
|
|
{ TypeClass *tc = (TypeClass *)tb;
|
|
ClassDeclaration *cd = tc->sym;
|
|
|
|
if (cd->isCOMinterface())
|
|
{ /* Because COM classes are deleted by IUnknown.Release()
|
|
*/
|
|
error("cannot delete instance of COM interface %s", cd->toChars());
|
|
}
|
|
break;
|
|
}
|
|
case Tpointer:
|
|
tb = ((TypePointer *)tb)->next->toBasetype();
|
|
if (tb->ty == Tstruct)
|
|
{
|
|
TypeStruct *ts = (TypeStruct *)tb;
|
|
StructDeclaration *sd = ts->sym;
|
|
FuncDeclaration *f = sd->aggDelete;
|
|
FuncDeclaration *fd = sd->dtor;
|
|
|
|
if (!f && !fd)
|
|
break;
|
|
|
|
/* Construct:
|
|
* ea = copy e1 to a tmp to do side effects only once
|
|
* eb = call destructor
|
|
* ec = call deallocator
|
|
*/
|
|
Expression *ea = NULL;
|
|
Expression *eb = NULL;
|
|
Expression *ec = NULL;
|
|
VarDeclaration *v;
|
|
|
|
if (fd && f)
|
|
{ Identifier *id = Lexer::idPool("__tmp");
|
|
v = new VarDeclaration(loc, e1->type, id, new ExpInitializer(loc, e1));
|
|
v->semantic(sc);
|
|
v->parent = sc->parent;
|
|
ea = new DeclarationExp(loc, v);
|
|
ea->type = v->type;
|
|
}
|
|
|
|
if (fd)
|
|
{ Expression *e = ea ? new VarExp(loc, v) : e1;
|
|
e = new DotVarExp(0, e, fd, 0);
|
|
eb = new CallExp(loc, e);
|
|
eb = eb->semantic(sc);
|
|
}
|
|
|
|
if (f)
|
|
{
|
|
Type *tpv = Type::tvoid->pointerTo();
|
|
Expression *e = ea ? new VarExp(loc, v) : e1->castTo(sc, tpv);
|
|
e = new CallExp(loc, new VarExp(loc, f), e);
|
|
ec = e->semantic(sc);
|
|
}
|
|
ea = combine(ea, eb);
|
|
ea = combine(ea, ec);
|
|
assert(ea);
|
|
return ea;
|
|
}
|
|
break;
|
|
|
|
case Tarray:
|
|
/* BUG: look for deleting arrays of structs with dtors.
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
if (e1->op == TOKindex)
|
|
{
|
|
IndexExp *ae = (IndexExp *)(e1);
|
|
Type *tb1 = ae->e1->type->toBasetype();
|
|
if (tb1->ty == Taarray)
|
|
break;
|
|
}
|
|
error("cannot delete type %s", e1->type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (e1->op == TOKindex)
|
|
{
|
|
IndexExp *ae = (IndexExp *)(e1);
|
|
Type *tb1 = ae->e1->type->toBasetype();
|
|
if (tb1->ty == Taarray)
|
|
{ if (!global.params.useDeprecated)
|
|
error("delete aa[key] deprecated, use aa.remove(key)");
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
Expression *DeleteExp::checkToBoolean(Scope *sc)
|
|
{
|
|
error("delete does not give a boolean result");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
void DeleteExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("delete ");
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
CastExp::CastExp(Loc loc, Expression *e, Type *t)
|
|
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
|
|
{
|
|
to = t;
|
|
this->mod = ~0;
|
|
}
|
|
|
|
#if DMDV2
|
|
/* For cast(const) and cast(immutable)
|
|
*/
|
|
CastExp::CastExp(Loc loc, Expression *e, unsigned mod)
|
|
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
|
|
{
|
|
to = NULL;
|
|
this->mod = mod;
|
|
}
|
|
#endif
|
|
|
|
Expression *CastExp::syntaxCopy()
|
|
{
|
|
return to ? new CastExp(loc, e1->syntaxCopy(), to->syntaxCopy())
|
|
: new CastExp(loc, e1->syntaxCopy(), mod);
|
|
}
|
|
|
|
|
|
Expression *CastExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("CastExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
|
|
//static int x; assert(++x < 10);
|
|
|
|
if (type)
|
|
return this;
|
|
UnaExp::semantic(sc);
|
|
if (e1->type) // if not a tuple
|
|
{
|
|
e1 = resolveProperties(sc, e1);
|
|
|
|
if (!to)
|
|
{
|
|
/* Handle cast(const) and cast(immutable), etc.
|
|
*/
|
|
to = e1->type->castMod(mod);
|
|
}
|
|
else
|
|
to = to->semantic(loc, sc);
|
|
|
|
if (!to->equals(e1->type))
|
|
{
|
|
#if 0 // attempt at fixing 6720
|
|
if (e1->type->ty == Tvoid)
|
|
{
|
|
error("cannot cast from void to %s", to->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
#endif
|
|
Expression *e = op_overload(sc);
|
|
if (e)
|
|
{
|
|
return e->implicitCastTo(sc, to);
|
|
}
|
|
}
|
|
|
|
if (e1->op == TOKtemplate)
|
|
{
|
|
error("cannot cast template %s to type %s", e1->toChars(), to->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Type *t1b = e1->type->toBasetype();
|
|
Type *tob = to->toBasetype();
|
|
|
|
if (tob->ty == Tstruct &&
|
|
!tob->equals(t1b)
|
|
)
|
|
{
|
|
/* Look to replace:
|
|
* cast(S)t
|
|
* with:
|
|
* S(t)
|
|
*/
|
|
|
|
// Rewrite as to.call(e1)
|
|
Expression *e = new TypeExp(loc, to);
|
|
e = new CallExp(loc, e, e1);
|
|
e = e->trySemantic(sc);
|
|
if (e)
|
|
return e;
|
|
}
|
|
|
|
// Struct casts are possible only when the sizes match
|
|
// Same with static array -> static array
|
|
if (tob->ty == Tstruct || t1b->ty == Tstruct ||
|
|
(tob->ty == Tsarray && t1b->ty == Tsarray))
|
|
{
|
|
size_t fromsize = t1b->size(loc);
|
|
size_t tosize = tob->size(loc);
|
|
if (fromsize != tosize)
|
|
{
|
|
error("cannot cast from %s to %s", e1->type->toChars(), to->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
|
|
// Look for casting to a vector type
|
|
if (tob->ty == Tvector && t1b->ty != Tvector)
|
|
{
|
|
return new VectorExp(loc, e1, to);
|
|
}
|
|
}
|
|
else if (!to)
|
|
{ error("cannot cast tuple");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (!e1->type)
|
|
{ error("cannot cast %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
// Check for unsafe casts
|
|
if (sc->func && !sc->intypeof)
|
|
{ // Disallow unsafe casts
|
|
Type *tob = to->toBasetype();
|
|
Type *t1b = e1->type->toBasetype();
|
|
|
|
// Implicit conversions are always safe
|
|
if (t1b->implicitConvTo(tob))
|
|
goto Lsafe;
|
|
|
|
if (!t1b->isMutable() && tob->isMutable())
|
|
goto Lunsafe;
|
|
|
|
if (t1b->isShared() && !tob->isShared())
|
|
// Cast away shared
|
|
goto Lunsafe;
|
|
|
|
if (!tob->hasPointers())
|
|
goto Lsafe;
|
|
|
|
if (tob->ty == Tclass && t1b->ty == Tclass)
|
|
{
|
|
ClassDeclaration *cdfrom = t1b->isClassHandle();
|
|
ClassDeclaration *cdto = tob->isClassHandle();
|
|
|
|
int offset;
|
|
if (!cdfrom->isBaseOf(cdto, &offset))
|
|
goto Lunsafe;
|
|
|
|
if (cdfrom->isCPPinterface() ||
|
|
cdto->isCPPinterface())
|
|
goto Lunsafe;
|
|
|
|
goto Lsafe;
|
|
}
|
|
|
|
if (tob->ty == Tarray && t1b->ty == Tarray)
|
|
{
|
|
Type* tobn = tob->nextOf()->toBasetype();
|
|
Type* t1bn = t1b->nextOf()->toBasetype();
|
|
if (!tobn->hasPointers() && MODimplicitConv(t1bn->mod, tobn->mod))
|
|
goto Lsafe;
|
|
}
|
|
if (tob->ty == Tpointer && t1b->ty == Tpointer)
|
|
{
|
|
Type* tobn = tob->nextOf()->toBasetype();
|
|
Type* t1bn = t1b->nextOf()->toBasetype();
|
|
if (!tobn->hasPointers() &&
|
|
tobn->ty != Tfunction && t1bn->ty != Tfunction &&
|
|
tobn->size() <= t1bn->size() &&
|
|
MODimplicitConv(t1bn->mod, tobn->mod))
|
|
goto Lsafe;
|
|
}
|
|
|
|
Lunsafe:
|
|
if (sc->func->setUnsafe())
|
|
{ error("cast from %s to %s not allowed in safe code", e1->type->toChars(), to->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
|
|
Lsafe:
|
|
Expression *e = e1->castTo(sc, to);
|
|
return e;
|
|
}
|
|
|
|
|
|
void CastExp::checkEscape()
|
|
{ Type *tb = type->toBasetype();
|
|
if (tb->ty == Tarray && e1->op == TOKvar &&
|
|
e1->type->toBasetype()->ty == Tsarray)
|
|
{ VarExp *ve = (VarExp *)e1;
|
|
VarDeclaration *v = ve->var->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
if (!v->isDataseg() && !v->isParameter())
|
|
error("escaping reference to local %s", v->toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CastExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("cast(");
|
|
#if DMDV1
|
|
to->toCBuffer(buf, NULL, hgs);
|
|
#else
|
|
if (to)
|
|
to->toCBuffer(buf, NULL, hgs);
|
|
else
|
|
{
|
|
MODtoBuffer(buf, mod);
|
|
}
|
|
#endif
|
|
buf->writeByte(')');
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
VectorExp::VectorExp(Loc loc, Expression *e, Type *t)
|
|
: UnaExp(loc, TOKvector, sizeof(VectorExp), e)
|
|
{
|
|
assert(t->ty == Tvector);
|
|
to = t;
|
|
dim = ~0;
|
|
}
|
|
|
|
Expression *VectorExp::syntaxCopy()
|
|
{
|
|
return new VectorExp(loc, e1->syntaxCopy(), to->syntaxCopy());
|
|
}
|
|
|
|
Expression *VectorExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("VectorExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
|
|
if (type)
|
|
return this;
|
|
e1 = e1->semantic(sc);
|
|
type = to->semantic(loc, sc);
|
|
if (e1->op == TOKerror || type->ty == Terror)
|
|
return e1;
|
|
Type *tb = type->toBasetype();
|
|
assert(tb->ty == Tvector);
|
|
TypeVector *tv = (TypeVector *)tb;
|
|
Type *te = tv->elementType();
|
|
dim = tv->size(loc) / te->size(loc);
|
|
return this;
|
|
}
|
|
|
|
void VectorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring("cast(");
|
|
to->toCBuffer(buf, NULL, hgs);
|
|
buf->writeByte(')');
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
SliceExp::SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr)
|
|
: UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
|
|
{
|
|
this->upr = upr;
|
|
this->lwr = lwr;
|
|
lengthVar = NULL;
|
|
}
|
|
|
|
Expression *SliceExp::syntaxCopy()
|
|
{
|
|
Expression *lwr = NULL;
|
|
if (this->lwr)
|
|
lwr = this->lwr->syntaxCopy();
|
|
|
|
Expression *upr = NULL;
|
|
if (this->upr)
|
|
upr = this->upr->syntaxCopy();
|
|
|
|
return new SliceExp(loc, e1->syntaxCopy(), lwr, upr);
|
|
}
|
|
|
|
Expression *SliceExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
AggregateDeclaration *ad;
|
|
//FuncDeclaration *fd;
|
|
ScopeDsymbol *sym;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("SliceExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
Lagain:
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
|
|
e = this;
|
|
|
|
Type *t = e1->type->toBasetype();
|
|
if (t->ty == Tpointer)
|
|
{
|
|
if (!lwr || !upr)
|
|
{ error("need upper and lower bound to slice pointer");
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
else if (t->ty == Tarray)
|
|
{
|
|
}
|
|
else if (t->ty == Tsarray)
|
|
{
|
|
}
|
|
else if (t->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t)->sym;
|
|
goto L1;
|
|
}
|
|
else if (t->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t)->sym;
|
|
|
|
L1:
|
|
if (search_function(ad, Id::slice))
|
|
{
|
|
// Rewrite as e1.slice(lwr, upr)
|
|
e = new DotIdExp(loc, e1, Id::slice);
|
|
|
|
if (lwr)
|
|
{
|
|
assert(upr);
|
|
e = new CallExp(loc, e, lwr, upr);
|
|
}
|
|
else
|
|
{ assert(!upr);
|
|
e = new CallExp(loc, e);
|
|
}
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
if (ad->aliasthis)
|
|
{
|
|
e1 = resolveAliasThis(sc, e1);
|
|
goto Lagain;
|
|
}
|
|
goto Lerror;
|
|
}
|
|
else if (t->ty == Ttuple)
|
|
{
|
|
if (!lwr && !upr)
|
|
return e1;
|
|
if (!lwr || !upr)
|
|
{ error("need upper and lower bound to slice tuple");
|
|
goto Lerror;
|
|
}
|
|
}
|
|
else if (t == Type::terror)
|
|
goto Lerr;
|
|
else
|
|
goto Lerror;
|
|
|
|
{
|
|
Scope *sc2 = sc;
|
|
if (t->ty == Tsarray || t->ty == Tarray || t->ty == Ttuple)
|
|
{
|
|
sym = new ArrayScopeSymbol(sc, this);
|
|
sym->loc = loc;
|
|
sym->parent = sc->scopesym;
|
|
sc2 = sc->push(sym);
|
|
}
|
|
|
|
if (lwr)
|
|
{ lwr = lwr->semantic(sc2);
|
|
lwr = resolveProperties(sc2, lwr);
|
|
lwr = lwr->implicitCastTo(sc2, Type::tsize_t);
|
|
if (lwr->type == Type::terror)
|
|
goto Lerr;
|
|
}
|
|
if (upr)
|
|
{ upr = upr->semantic(sc2);
|
|
upr = resolveProperties(sc2, upr);
|
|
upr = upr->implicitCastTo(sc2, Type::tsize_t);
|
|
if (upr->type == Type::terror)
|
|
goto Lerr;
|
|
}
|
|
|
|
if (sc2 != sc)
|
|
sc2->pop();
|
|
}
|
|
|
|
if (t->ty == Ttuple)
|
|
{
|
|
lwr = lwr->optimize(WANTvalue | WANTinterpret);
|
|
upr = upr->optimize(WANTvalue | WANTinterpret);
|
|
uinteger_t i1 = lwr->toUInteger();
|
|
uinteger_t i2 = upr->toUInteger();
|
|
|
|
size_t length;
|
|
TupleExp *te;
|
|
TypeTuple *tup;
|
|
|
|
if (e1->op == TOKtuple) // slicing an expression tuple
|
|
{ te = (TupleExp *)e1;
|
|
length = te->exps->dim;
|
|
}
|
|
else if (e1->op == TOKtype) // slicing a type tuple
|
|
{ tup = (TypeTuple *)t;
|
|
length = Parameter::dim(tup->arguments);
|
|
}
|
|
else
|
|
assert(0);
|
|
|
|
if (i1 <= i2 && i2 <= length)
|
|
{ size_t j1 = (size_t) i1;
|
|
size_t j2 = (size_t) i2;
|
|
|
|
if (e1->op == TOKtuple)
|
|
{ Expressions *exps = new Expressions;
|
|
exps->setDim(j2 - j1);
|
|
for (size_t i = 0; i < j2 - j1; i++)
|
|
{ Expression *e = (*te->exps)[j1 + i];
|
|
(*exps)[i] = e;
|
|
}
|
|
if (j1 > 0 && j2 - j1 > 0 && sc->func && (*te->exps)[0]->op == TOKdotvar)
|
|
{
|
|
Expression *einit = ((DotVarExp *)(*te->exps)[0])->e1->isTemp();
|
|
if (einit)
|
|
((DotVarExp *)(*exps)[0])->e1 = einit;
|
|
}
|
|
e = new TupleExp(loc, exps);
|
|
}
|
|
else
|
|
{ Parameters *args = new Parameters;
|
|
args->reserve(j2 - j1);
|
|
for (size_t i = j1; i < j2; i++)
|
|
{ Parameter *arg = Parameter::getNth(tup->arguments, i);
|
|
args->push(arg);
|
|
}
|
|
e = new TypeExp(e1->loc, new TypeTuple(args));
|
|
}
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
{
|
|
error("string slice [%llu .. %llu] is out of bounds", i1, i2);
|
|
goto Lerr;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
type = t->nextOf()->arrayOf();
|
|
// Allow typedef[] -> typedef[]
|
|
if (type->equals(t))
|
|
type = e1->type;
|
|
|
|
return e;
|
|
|
|
Lerror:
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
char *s;
|
|
if (t->ty == Tvoid)
|
|
s = e1->toChars();
|
|
else
|
|
s = t->toChars();
|
|
error("%s cannot be sliced with []", s);
|
|
Lerr:
|
|
e = new ErrorExp();
|
|
return e;
|
|
}
|
|
|
|
void SliceExp::checkEscape()
|
|
{
|
|
e1->checkEscape();
|
|
}
|
|
|
|
void SliceExp::checkEscapeRef()
|
|
{
|
|
e1->checkEscapeRef();
|
|
}
|
|
|
|
#if DMDV2
|
|
int SliceExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *SliceExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Expression *SliceExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
error("slice expression %s is not a modifiable lvalue", toChars());
|
|
return this;
|
|
}
|
|
|
|
int SliceExp::isBool(int result)
|
|
{
|
|
return e1->isBool(result);
|
|
}
|
|
|
|
void SliceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
buf->writeByte('[');
|
|
if (upr || lwr)
|
|
{
|
|
if (lwr)
|
|
expToCBuffer(buf, hgs, lwr, PREC_assign);
|
|
else
|
|
buf->writeByte('0');
|
|
buf->writestring("..");
|
|
if (upr)
|
|
expToCBuffer(buf, hgs, upr, PREC_assign);
|
|
else
|
|
buf->writestring("length"); // BUG: should be array.length
|
|
}
|
|
buf->writeByte(']');
|
|
}
|
|
|
|
/********************** ArrayLength **************************************/
|
|
|
|
ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1)
|
|
: UnaExp(loc, TOKarraylength, sizeof(ArrayLengthExp), e1)
|
|
{
|
|
}
|
|
|
|
Expression *ArrayLengthExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("ArrayLengthExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
|
|
type = Type::tsize_t;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Expression *opAssignToOp(Loc loc, enum TOK op, Expression *e1, Expression *e2)
|
|
{ Expression *e;
|
|
|
|
switch (op)
|
|
{
|
|
case TOKaddass: e = new AddExp(loc, e1, e2); break;
|
|
case TOKminass: e = new MinExp(loc, e1, e2); break;
|
|
case TOKmulass: e = new MulExp(loc, e1, e2); break;
|
|
case TOKdivass: e = new DivExp(loc, e1, e2); break;
|
|
case TOKmodass: e = new ModExp(loc, e1, e2); break;
|
|
case TOKandass: e = new AndExp(loc, e1, e2); break;
|
|
case TOKorass: e = new OrExp (loc, e1, e2); break;
|
|
case TOKxorass: e = new XorExp(loc, e1, e2); break;
|
|
case TOKshlass: e = new ShlExp(loc, e1, e2); break;
|
|
case TOKshrass: e = new ShrExp(loc, e1, e2); break;
|
|
case TOKushrass: e = new UshrExp(loc, e1, e2); break;
|
|
default: assert(0);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/*********************
|
|
* Rewrite:
|
|
* array.length op= e2
|
|
* as:
|
|
* array.length = array.length op e2
|
|
* or:
|
|
* auto tmp = &array;
|
|
* (*tmp).length = (*tmp).length op e2
|
|
*/
|
|
|
|
Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp)
|
|
{ Expression *e;
|
|
|
|
assert(exp->e1->op == TOKarraylength);
|
|
ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1;
|
|
if (ale->e1->op == TOKvar)
|
|
{ e = opAssignToOp(exp->loc, exp->op, ale, exp->e2);
|
|
e = new AssignExp(exp->loc, ale->syntaxCopy(), e);
|
|
}
|
|
else
|
|
{
|
|
/* auto tmp = &array;
|
|
* (*tmp).length = (*tmp).length op e2
|
|
*/
|
|
Identifier *id = Lexer::uniqueId("__arraylength");
|
|
ExpInitializer *ei = new ExpInitializer(ale->loc, new AddrExp(ale->loc, ale->e1));
|
|
VarDeclaration *tmp = new VarDeclaration(ale->loc, ale->e1->type->pointerTo(), id, ei);
|
|
|
|
Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp)));
|
|
Expression *elvalue = e1->syntaxCopy();
|
|
e = opAssignToOp(exp->loc, exp->op, e1, exp->e2);
|
|
e = new AssignExp(exp->loc, elvalue, e);
|
|
e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void ArrayLengthExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writestring(".length");
|
|
}
|
|
|
|
/*********************** ArrayExp *************************************/
|
|
|
|
// e1 [ i1, i2, i3, ... ]
|
|
|
|
ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args)
|
|
: UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
|
|
{
|
|
arguments = args;
|
|
lengthVar = NULL;
|
|
currentDimension = 0;
|
|
}
|
|
|
|
Expression *ArrayExp::syntaxCopy()
|
|
{
|
|
return new ArrayExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
|
|
}
|
|
|
|
Expression *ArrayExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
Type *t1;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("ArrayExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
UnaExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
|
|
t1 = e1->type->toBasetype();
|
|
if (t1->ty != Tclass && t1->ty != Tstruct)
|
|
{ // Convert to IndexExp
|
|
if (arguments->dim != 1)
|
|
{ error("only one index allowed to index %s", t1->toChars());
|
|
goto Lerr;
|
|
}
|
|
e = new IndexExp(loc, e1, arguments->tdata()[0]);
|
|
return e->semantic(sc);
|
|
}
|
|
|
|
e = op_overload(sc);
|
|
if (!e)
|
|
{ error("no [] operator overload for type %s", e1->type->toChars());
|
|
goto Lerr;
|
|
}
|
|
return e;
|
|
|
|
Lerr:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
#if DMDV2
|
|
int ArrayExp::isLvalue()
|
|
{
|
|
if (type && type->toBasetype()->ty == Tvoid)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *ArrayExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
if (type && type->toBasetype()->ty == Tvoid)
|
|
error("voids have no value");
|
|
return this;
|
|
}
|
|
|
|
|
|
void ArrayExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('[');
|
|
argsToCBuffer(buf, arguments, hgs);
|
|
buf->writeByte(']');
|
|
}
|
|
|
|
/************************* DotExp ***********************************/
|
|
|
|
DotExp::DotExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKdotexp, sizeof(DotExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *DotExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("DotExp::semantic('%s')\n", toChars());
|
|
if (type) printf("\ttype = %s\n", type->toChars());
|
|
#endif
|
|
e1 = e1->semantic(sc);
|
|
e2 = e2->semantic(sc);
|
|
if (e2->op == TOKimport)
|
|
{
|
|
ScopeExp *se = (ScopeExp *)e2;
|
|
TemplateDeclaration *td = se->sds->isTemplateDeclaration();
|
|
if (td)
|
|
{ Expression *e = new DotTemplateExp(loc, e1, td);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
if (!type)
|
|
type = e2->type;
|
|
return this;
|
|
}
|
|
|
|
void DotExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('.');
|
|
expToCBuffer(buf, hgs, e2, PREC_primary);
|
|
}
|
|
|
|
/************************* CommaExp ***********************************/
|
|
|
|
CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKcomma, sizeof(CommaExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *CommaExp::semantic(Scope *sc)
|
|
{
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e1 = e1->addDtorHook(sc);
|
|
type = e2->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void CommaExp::checkEscape()
|
|
{
|
|
e2->checkEscape();
|
|
}
|
|
|
|
void CommaExp::checkEscapeRef()
|
|
{
|
|
e2->checkEscapeRef();
|
|
}
|
|
|
|
#if DMDV2
|
|
int CommaExp::isLvalue()
|
|
{
|
|
return e2->isLvalue();
|
|
}
|
|
#endif
|
|
|
|
Expression *CommaExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
e2 = e2->toLvalue(sc, NULL);
|
|
return this;
|
|
}
|
|
|
|
Expression *CommaExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
e2 = e2->modifiableLvalue(sc, e);
|
|
return this;
|
|
}
|
|
|
|
int CommaExp::isBool(int result)
|
|
{
|
|
return e2->isBool(result);
|
|
}
|
|
|
|
|
|
Expression *CommaExp::addDtorHook(Scope *sc)
|
|
{
|
|
e2 = e2->addDtorHook(sc);
|
|
return this;
|
|
}
|
|
|
|
/************************** IndexExp **********************************/
|
|
|
|
// e1 [ e2 ]
|
|
|
|
IndexExp::IndexExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKindex, sizeof(IndexExp), e1, e2)
|
|
{
|
|
//printf("IndexExp::IndexExp('%s')\n", toChars());
|
|
lengthVar = NULL;
|
|
modifiable = 0; // assume it is an rvalue
|
|
}
|
|
|
|
Expression *IndexExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
Type *t1;
|
|
ScopeDsymbol *sym;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("IndexExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
if (!e1->type)
|
|
e1 = e1->semantic(sc);
|
|
assert(e1->type); // semantic() should already be run on it
|
|
if (e1->op == TOKerror)
|
|
goto Lerr;
|
|
e = this;
|
|
|
|
// Note that unlike C we do not implement the int[ptr]
|
|
|
|
t1 = e1->type->toBasetype();
|
|
|
|
if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple)
|
|
{ // Create scope for 'length' variable
|
|
sym = new ArrayScopeSymbol(sc, this);
|
|
sym->loc = loc;
|
|
sym->parent = sc->scopesym;
|
|
sc = sc->push(sym);
|
|
}
|
|
|
|
e2 = e2->semantic(sc);
|
|
e2 = resolveProperties(sc, e2);
|
|
if (e2->type == Type::terror)
|
|
goto Lerr;
|
|
if (e2->type->ty == Ttuple && ((TupleExp *)e2)->exps->dim == 1) // bug 4444 fix
|
|
e2 = ((TupleExp *)e2)->exps->tdata()[0];
|
|
|
|
if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple)
|
|
sc = sc->pop();
|
|
|
|
switch (t1->ty)
|
|
{
|
|
case Tpointer:
|
|
case Tarray:
|
|
e2 = e2->implicitCastTo(sc, Type::tsize_t);
|
|
e->type = ((TypeNext *)t1)->next;
|
|
break;
|
|
|
|
case Tsarray:
|
|
{
|
|
e2 = e2->implicitCastTo(sc, Type::tsize_t);
|
|
|
|
TypeSArray *tsa = (TypeSArray *)t1;
|
|
|
|
#if 0 // Don't do now, because it might be short-circuit evaluated
|
|
// Do compile time array bounds checking if possible
|
|
e2 = e2->optimize(WANTvalue);
|
|
if (e2->op == TOKint64)
|
|
{
|
|
dinteger_t index = e2->toInteger();
|
|
dinteger_t length = tsa->dim->toInteger();
|
|
if (index < 0 || index >= length)
|
|
error("array index [%lld] is outside array bounds [0 .. %lld]",
|
|
index, length);
|
|
}
|
|
#endif
|
|
e->type = t1->nextOf();
|
|
break;
|
|
}
|
|
|
|
case Taarray:
|
|
{ TypeAArray *taa = (TypeAArray *)t1;
|
|
/* We can skip the implicit conversion if they differ only by
|
|
* constness (Bugzilla 2684, see also bug 2954b)
|
|
*/
|
|
if (!arrayTypeCompatibleWithoutCasting(e2->loc, e2->type, taa->index))
|
|
{
|
|
e2 = e2->implicitCastTo(sc, taa->index); // type checking
|
|
}
|
|
type = taa->next;
|
|
break;
|
|
}
|
|
|
|
case Ttuple:
|
|
{
|
|
e2 = e2->implicitCastTo(sc, Type::tsize_t);
|
|
e2 = e2->optimize(WANTvalue | WANTinterpret);
|
|
uinteger_t index = e2->toUInteger();
|
|
size_t length;
|
|
TupleExp *te;
|
|
TypeTuple *tup;
|
|
|
|
if (e1->op == TOKtuple)
|
|
{ te = (TupleExp *)e1;
|
|
length = te->exps->dim;
|
|
}
|
|
else if (e1->op == TOKtype)
|
|
{
|
|
tup = (TypeTuple *)t1;
|
|
length = Parameter::dim(tup->arguments);
|
|
}
|
|
else
|
|
assert(0);
|
|
|
|
if (index < length)
|
|
{
|
|
|
|
if (e1->op == TOKtuple)
|
|
{
|
|
e = (*te->exps)[(size_t)index];
|
|
if (sc->func && (*te->exps)[0]->op == TOKdotvar)
|
|
{
|
|
Expression *einit = ((DotVarExp *)(*te->exps)[0])->e1->isTemp();
|
|
if (einit)
|
|
((DotVarExp *)e)->e1 = einit;
|
|
}
|
|
}
|
|
else
|
|
e = new TypeExp(e1->loc, Parameter::getNth(tup->arguments, (size_t)index)->type);
|
|
}
|
|
else
|
|
{
|
|
error("array index [%llu] is outside array bounds [0 .. %llu]",
|
|
index, (ulonglong)length);
|
|
e = e1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if (e1->op == TOKerror)
|
|
goto Lerr;
|
|
error("%s must be an array or pointer type, not %s",
|
|
e1->toChars(), e1->type->toChars());
|
|
case Terror:
|
|
goto Lerr;
|
|
}
|
|
return e;
|
|
|
|
Lerr:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
#if DMDV2
|
|
int IndexExp::isLvalue()
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
Expression *IndexExp::toLvalue(Scope *sc, Expression *e)
|
|
{
|
|
// if (type && type->toBasetype()->ty == Tvoid)
|
|
// error("voids have no value");
|
|
return this;
|
|
}
|
|
|
|
Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//printf("IndexExp::modifiableLvalue(%s)\n", toChars());
|
|
modifiable = 1;
|
|
if (e1->op == TOKstring)
|
|
error("string literals are immutable");
|
|
if (type && (!type->isMutable() || !type->isAssignable()))
|
|
error("%s isn't mutable", e->toChars());
|
|
Type *t1 = e1->type->toBasetype();
|
|
if (t1->ty == Taarray)
|
|
{ TypeAArray *taa = (TypeAArray *)t1;
|
|
Type *t2b = e2->type->toBasetype();
|
|
if (t2b->ty == Tarray && t2b->nextOf()->isMutable())
|
|
error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars());
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
}
|
|
return toLvalue(sc, e);
|
|
}
|
|
|
|
void IndexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writeByte('[');
|
|
expToCBuffer(buf, hgs, e2, PREC_assign);
|
|
buf->writeByte(']');
|
|
}
|
|
|
|
|
|
/************************* PostExp ***********************************/
|
|
|
|
PostExp::PostExp(enum TOK op, Loc loc, Expression *e)
|
|
: BinExp(loc, op, sizeof(PostExp), e,
|
|
new IntegerExp(loc, 1, Type::tint32))
|
|
{
|
|
}
|
|
|
|
Expression *PostExp::semantic(Scope *sc)
|
|
{ Expression *e = this;
|
|
|
|
if (!type)
|
|
{
|
|
BinExp::semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
if (e1->op == TOKslice)
|
|
{
|
|
const char *s = op == TOKplusplus ? "increment" : "decrement";
|
|
error("cannot post-%s array slice '%s', use pre-%s instead", s, e1->toChars(), s);
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if (e1->op != TOKarraylength)
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
|
|
Type *t1 = e1->type->toBasetype();
|
|
if (t1->ty == Tclass || t1->ty == Tstruct || e1->op == TOKarraylength)
|
|
{ /* Check for operator overloading,
|
|
* but rewrite in terms of ++e instead of e++
|
|
*/
|
|
|
|
/* If e1 is not trivial, take a reference to it
|
|
*/
|
|
Expression *de = NULL;
|
|
if (e1->op != TOKvar && e1->op != TOKarraylength)
|
|
{
|
|
// ref v = e1;
|
|
Identifier *id = Lexer::uniqueId("__postref");
|
|
ExpInitializer *ei = new ExpInitializer(loc, e1);
|
|
VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei);
|
|
v->storage_class |= STCref | STCforeach;
|
|
de = new DeclarationExp(loc, v);
|
|
e1 = new VarExp(e1->loc, v);
|
|
}
|
|
|
|
/* Rewrite as:
|
|
* auto tmp = e1; ++e1; tmp
|
|
*/
|
|
Identifier *id = Lexer::uniqueId("__pitmp");
|
|
ExpInitializer *ei = new ExpInitializer(loc, e1);
|
|
VarDeclaration *tmp = new VarDeclaration(loc, e1->type, id, ei);
|
|
Expression *ea = new DeclarationExp(loc, tmp);
|
|
|
|
Expression *eb = e1->syntaxCopy();
|
|
eb = new PreExp(op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, loc, eb);
|
|
|
|
Expression *ec = new VarExp(loc, tmp);
|
|
|
|
// Combine de,ea,eb,ec
|
|
if (de)
|
|
ea = new CommaExp(loc, de, ea);
|
|
e = new CommaExp(loc, ea, eb);
|
|
e = new CommaExp(loc, e, ec);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
e = this;
|
|
e1->checkScalar();
|
|
e1->checkNoBool();
|
|
if (e1->type->ty == Tpointer)
|
|
e = scaleFactor(sc);
|
|
else
|
|
e2 = e2->castTo(sc, e1->type);
|
|
e->type = e1->type;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void PostExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
buf->writestring(Token::toChars(op));
|
|
}
|
|
|
|
/************************* PreExp ***********************************/
|
|
|
|
PreExp::PreExp(enum TOK op, Loc loc, Expression *e)
|
|
: UnaExp(loc, op, sizeof(PreExp), e)
|
|
{
|
|
}
|
|
|
|
Expression *PreExp::semantic(Scope *sc)
|
|
{
|
|
Expression *e;
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
// Rewrite as e1+=1 or e1-=1
|
|
if (op == TOKpreplusplus)
|
|
e = new AddAssignExp(loc, e1, new IntegerExp(loc, 1, Type::tint32));
|
|
else
|
|
e = new MinAssignExp(loc, e1, new IntegerExp(loc, 1, Type::tint32));
|
|
return e->semantic(sc);
|
|
}
|
|
|
|
void PreExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(Token::toChars(op));
|
|
expToCBuffer(buf, hgs, e1, precedence[op]);
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
/* op can be TOKassign, TOKconstruct, or TOKblit */
|
|
|
|
AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKassign, sizeof(AssignExp), e1, e2)
|
|
{
|
|
ismemset = 0;
|
|
}
|
|
|
|
Expression *AssignExp::semantic(Scope *sc)
|
|
{
|
|
Expression *e1old = e1;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("AssignExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
//printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
|
|
//printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op));
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
if (e2->op == TOKcomma)
|
|
{ /* Rewrite to get rid of the comma from rvalue
|
|
*/
|
|
AssignExp *ea = new AssignExp(loc, e1, ((CommaExp *)e2)->e2);
|
|
ea->op = op;
|
|
Expression *e = new CommaExp(loc, ((CommaExp *)e2)->e1, ea);
|
|
return e->semantic(sc);
|
|
}
|
|
|
|
/* Look for operator overloading of a[i]=value.
|
|
* Do it before semantic() otherwise the a[i] will have been
|
|
* converted to a.opIndex() already.
|
|
*/
|
|
if (e1->op == TOKarray)
|
|
{
|
|
ArrayExp *ae = (ArrayExp *)e1;
|
|
AggregateDeclaration *ad = NULL;
|
|
Identifier *id = Id::index;
|
|
|
|
ae->e1 = ae->e1->semantic(sc);
|
|
ae->e1 = resolveProperties(sc, ae->e1);
|
|
Type *t1 = ae->e1->type->toBasetype();
|
|
if (t1->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t1)->sym;
|
|
goto L1;
|
|
}
|
|
else if (t1->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t1)->sym;
|
|
L1:
|
|
// Rewrite (a[i] = value) to (a.opIndexAssign(value, i))
|
|
if (search_function(ad, Id::indexass))
|
|
{ Expression *e = new DotIdExp(loc, ae->e1, Id::indexass);
|
|
// Deal with $
|
|
for (size_t i = 0; i < ae->arguments->dim; i++)
|
|
{ Expression *x = ae->arguments->tdata()[i];
|
|
// Create scope for '$' variable for this dimension
|
|
ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
|
|
sym->loc = loc;
|
|
sym->parent = sc->scopesym;
|
|
sc = sc->push(sym);
|
|
ae->lengthVar = NULL; // Create it only if required
|
|
ae->currentDimension = i; // Dimension for $, if required
|
|
|
|
x = x->semantic(sc);
|
|
if (!x->type)
|
|
ae->error("%s has no value", x->toChars());
|
|
if (ae->lengthVar)
|
|
{ // If $ was used, declare it now
|
|
Expression *av = new DeclarationExp(ae->loc, ae->lengthVar);
|
|
x = new CommaExp(0, av, x);
|
|
x->semantic(sc);
|
|
}
|
|
ae->arguments->tdata()[i] = x;
|
|
sc = sc->pop();
|
|
}
|
|
Expressions *a = (Expressions *)ae->arguments->copy();
|
|
|
|
a->insert(0, e2);
|
|
e = new CallExp(loc, e, a);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
#if 0 // Turned off to allow rewriting (a[i]=value) to (a.opIndex(i)=value)
|
|
else
|
|
{
|
|
// Rewrite (a[i] = value) to (a.opIndex(i, value))
|
|
if (search_function(ad, id))
|
|
{ Expression *e = new DotIdExp(loc, ae->e1, id);
|
|
|
|
if (1 || !global.params.useDeprecated)
|
|
{ error("operator [] assignment overload with opIndex(i, value) illegal, use opIndexAssign(value, i)");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
e = new CallExp(loc, e, ae->arguments->tdata()[0], e2);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// No opIndexAssign found yet, but there might be an alias this to try.
|
|
if (ad && ad->aliasthis)
|
|
{ Expression *e = resolveAliasThis(sc, ae->e1);
|
|
Type *t = e->type->toBasetype();
|
|
|
|
if (t->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t)->sym;
|
|
goto L1;
|
|
}
|
|
else if (t->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t)->sym;
|
|
goto L1;
|
|
}
|
|
}
|
|
}
|
|
/* Look for operator overloading of a[i..j]=value.
|
|
* Do it before semantic() otherwise the a[i..j] will have been
|
|
* converted to a.opSlice() already.
|
|
*/
|
|
if (e1->op == TOKslice)
|
|
{ Type *t1;
|
|
SliceExp *ae = (SliceExp *)e1;
|
|
AggregateDeclaration *ad = NULL;
|
|
Identifier *id = Id::index;
|
|
|
|
ae->e1 = ae->e1->semantic(sc);
|
|
ae->e1 = resolveProperties(sc, ae->e1);
|
|
t1 = ae->e1->type->toBasetype();
|
|
if (t1->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t1)->sym;
|
|
goto L2;
|
|
}
|
|
else if (t1->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t1)->sym;
|
|
L2:
|
|
// Rewrite (a[i..j] = value) to (a.opSliceAssign(value, i, j))
|
|
if (search_function(ad, Id::sliceass))
|
|
{ Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass);
|
|
Expressions *a = new Expressions();
|
|
|
|
a->push(e2);
|
|
if (ae->lwr)
|
|
{ a->push(ae->lwr);
|
|
assert(ae->upr);
|
|
a->push(ae->upr);
|
|
}
|
|
else
|
|
assert(!ae->upr);
|
|
e = new CallExp(loc, e, a);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
// No opSliceAssign found yet, but there might be an alias this to try.
|
|
if (ad && ad->aliasthis)
|
|
{ Expression *e = resolveAliasThis(sc, ae->e1);
|
|
Type *t = e->type->toBasetype();
|
|
|
|
if (t->ty == Tstruct)
|
|
{
|
|
ad = ((TypeStruct *)t)->sym;
|
|
goto L2;
|
|
}
|
|
else if (t->ty == Tclass)
|
|
{
|
|
ad = ((TypeClass *)t)->sym;
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
|
|
e2 = e2->semantic(sc);
|
|
if (e2->op == TOKerror)
|
|
return new ErrorExp();
|
|
e2 = resolveProperties(sc, e2);
|
|
|
|
/* With UFCS, e.f = value
|
|
* Could mean:
|
|
* .f(e, value)
|
|
* or:
|
|
* .f(e) = value
|
|
*/
|
|
if (e1->op == TOKdotti)
|
|
{
|
|
DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1;
|
|
dti->e1 = dti->e1->semantic(sc);
|
|
if (!global.errors && dti->e1->type)
|
|
{
|
|
unsigned errors = global.startGagging();
|
|
e1 = dti->semantic(sc, 1);
|
|
if (global.endGagging(errors) || e1->op == TOKerror)
|
|
{
|
|
return resolveUFCSProperties(sc, dti, e2);
|
|
}
|
|
}
|
|
}
|
|
else if (e1->op == TOKdot)
|
|
{
|
|
DotIdExp *die = (DotIdExp *)e1;
|
|
die->e1 = die->e1->semantic(sc);
|
|
if (!global.errors && die->e1->type)
|
|
{
|
|
unsigned errors = global.startGagging();
|
|
e1 = die->semantic(sc, 1);
|
|
if (global.endGagging(errors) || e1->op == TOKerror)
|
|
{
|
|
return resolveUFCSProperties(sc, die, e2);
|
|
}
|
|
}
|
|
}
|
|
Le1:
|
|
e1 = e1->semantic(sc);
|
|
if (e1->op == TOKerror)
|
|
return new ErrorExp();
|
|
|
|
/* We have f = value.
|
|
* Could mean:
|
|
* f(value)
|
|
* or:
|
|
* f() = value
|
|
*/
|
|
TemplateDeclaration *td;
|
|
Objects *targsi;
|
|
FuncDeclaration *fd;
|
|
Expression *ethis;
|
|
if (e1->op == TOKdotti)
|
|
{
|
|
DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1;
|
|
td = dti->getTempdecl(sc);
|
|
dti->ti->semanticTiargs(sc);
|
|
targsi = dti->ti->tiargs;
|
|
ethis = dti->e1;
|
|
goto L3;
|
|
}
|
|
else if (e1->op == TOKdottd)
|
|
{
|
|
DotTemplateExp *dte = (DotTemplateExp *)e1;
|
|
td = dte->td;
|
|
targsi = NULL;
|
|
ethis = dte->e1;
|
|
goto L3;
|
|
}
|
|
else if (e1->op == TOKtemplate)
|
|
{
|
|
td = ((TemplateExp *)e1)->td;
|
|
targsi = NULL;
|
|
ethis = NULL;
|
|
L3:
|
|
{
|
|
assert(td);
|
|
Expressions a;
|
|
a.push(e2);
|
|
|
|
fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, &a, 1);
|
|
if (fd && fd->type)
|
|
goto Lsetter;
|
|
|
|
fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, NULL, 1);
|
|
if (fd && fd->type)
|
|
goto Lgetter;
|
|
}
|
|
goto Leprop;
|
|
}
|
|
else if (e1->op == TOKdotvar && e1->type->toBasetype()->ty == Tfunction)
|
|
{
|
|
DotVarExp *dve = (DotVarExp *)e1;
|
|
fd = dve->var->isFuncDeclaration();
|
|
ethis = dve->e1;
|
|
goto L4;
|
|
}
|
|
else if (e1->op == TOKvar && e1->type->toBasetype()->ty == Tfunction)
|
|
{
|
|
fd = ((VarExp *)e1)->var->isFuncDeclaration();
|
|
ethis = NULL;
|
|
L4:
|
|
{
|
|
assert(fd);
|
|
FuncDeclaration *f = fd;
|
|
Expressions a;
|
|
a.push(e2);
|
|
|
|
fd = f->overloadResolve(loc, ethis, &a, 1);
|
|
if (fd && fd->type)
|
|
goto Lsetter;
|
|
|
|
fd = f->overloadResolve(loc, ethis, NULL, 1);
|
|
if (fd && fd->type)
|
|
goto Lgetter;
|
|
|
|
goto Leprop;
|
|
}
|
|
|
|
Expression *e;
|
|
TypeFunction *tf;
|
|
|
|
Lsetter:
|
|
assert(fd->type->ty == Tfunction);
|
|
tf = (TypeFunction *)fd->type;
|
|
if (!tf->isproperty && global.params.enforcePropertySyntax)
|
|
goto Leprop;
|
|
e = new CallExp(loc, e1, e2);
|
|
return e->semantic(sc);
|
|
|
|
Lgetter:
|
|
assert(fd->type->ty == Tfunction);
|
|
tf = (TypeFunction *)fd->type;
|
|
if (!tf->isref)
|
|
goto Leprop;
|
|
if (!tf->isproperty && global.params.enforcePropertySyntax)
|
|
goto Leprop;
|
|
e = new CallExp(loc, e1);
|
|
e = new AssignExp(loc, e, e2);
|
|
return e->semantic(sc);
|
|
|
|
Leprop:
|
|
::error(e1->loc, "not a property %s", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
|
|
assert(e1->type);
|
|
|
|
/* Rewrite tuple assignment as a tuple of assignments.
|
|
*/
|
|
Ltupleassign:
|
|
if (e1->op == TOKtuple && e2->op == TOKtuple)
|
|
{ TupleExp *tup1 = (TupleExp *)e1;
|
|
TupleExp *tup2 = (TupleExp *)e2;
|
|
size_t dim = tup1->exps->dim;
|
|
if (dim != tup2->exps->dim)
|
|
{
|
|
error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim);
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{ Expressions *exps = new Expressions;
|
|
exps->setDim(dim);
|
|
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Expression *ex1 = (*tup1->exps)[i];
|
|
Expression *ex2 = (*tup2->exps)[i];
|
|
(*exps)[i] = new AssignExp(loc, ex1, ex2);
|
|
}
|
|
Expression *e = new TupleExp(loc, exps);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (e1->op == TOKtuple)
|
|
{
|
|
if (TupleDeclaration *td = isAliasThisTuple(e2))
|
|
{
|
|
assert(e1->type->ty == Ttuple);
|
|
TypeTuple *tt = (TypeTuple *)e1->type;
|
|
|
|
Identifier *id = Lexer::uniqueId("__tup");
|
|
ExpInitializer *ei = new ExpInitializer(e2->loc, e2);
|
|
VarDeclaration *v = new VarDeclaration(e2->loc, NULL, id, ei);
|
|
v->storage_class = STCctfe | STCref | STCforeach;
|
|
Expression *ve = new VarExp(e2->loc, v);
|
|
ve->type = e2->type;
|
|
|
|
Expressions *iexps = new Expressions();
|
|
iexps->push(ve);
|
|
|
|
for (size_t u = 0; u < iexps->dim ; u++)
|
|
{
|
|
Lexpand:
|
|
Expression *e = iexps->tdata()[u];
|
|
|
|
Parameter *arg = Parameter::getNth(tt->arguments, u);
|
|
//printf("[%d] iexps->dim = %d, ", u, iexps->dim);
|
|
//printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
|
|
//printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
|
|
|
|
if (!e->type->implicitConvTo(arg->type))
|
|
{
|
|
// expand initializer to tuple
|
|
if (expandAliasThisTuples(iexps, u) != -1)
|
|
goto Lexpand;
|
|
|
|
goto Lnomatch;
|
|
}
|
|
}
|
|
iexps->tdata()[0] = new CommaExp(loc, new DeclarationExp(e2->loc, v), iexps->tdata()[0]);
|
|
e2 = new TupleExp(e2->loc, iexps);
|
|
e2 = e2->semantic(sc);
|
|
goto Ltupleassign;
|
|
|
|
Lnomatch:
|
|
;
|
|
}
|
|
}
|
|
|
|
// Determine if this is an initialization of a reference
|
|
int refinit = 0;
|
|
if (op == TOKconstruct && e1->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)e1;
|
|
VarDeclaration *v = ve->var->isVarDeclaration();
|
|
if (v->storage_class & (STCout | STCref))
|
|
refinit = 1;
|
|
}
|
|
|
|
Type *t1 = e1->type->toBasetype();
|
|
|
|
/* If it is an assignment from a 'foreign' type,
|
|
* check for operator overloading.
|
|
*/
|
|
if (t1->ty == Tstruct)
|
|
{
|
|
StructDeclaration *sd = ((TypeStruct *)t1)->sym;
|
|
if (op == TOKassign)
|
|
{
|
|
/* See if we need to set ctorinit, i.e. track
|
|
* assignments to fields. An assignment to a field counts even
|
|
* if done through an opAssign overload.
|
|
*/
|
|
if (e1->op == TOKdotvar)
|
|
{ DotVarExp *dve = (DotVarExp *)e1;
|
|
VarDeclaration *v = dve->var->isVarDeclaration();
|
|
if (v && v->storage_class & STCnodefaultctor)
|
|
modifyFieldVar(loc, sc, v, dve->e1);
|
|
}
|
|
|
|
Expression *e = op_overload(sc);
|
|
if (e && e1->op == TOKindex &&
|
|
((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
|
|
{
|
|
// Deal with AAs (Bugzilla 2451)
|
|
// Rewrite as:
|
|
// e1 = (typeof(aa.value) tmp = void, tmp = e2, tmp);
|
|
Type * aaValueType = ((TypeAArray *)((IndexExp*)e1)->e1->type->toBasetype())->next;
|
|
Identifier *id = Lexer::uniqueId("__aatmp");
|
|
VarDeclaration *v = new VarDeclaration(loc, aaValueType,
|
|
id, new VoidInitializer(0));
|
|
v->storage_class |= STCctfe;
|
|
v->semantic(sc);
|
|
v->parent = sc->parent;
|
|
|
|
Expression *de = new DeclarationExp(loc, v);
|
|
VarExp *ve = new VarExp(loc, v);
|
|
|
|
AssignExp *ae = new AssignExp(loc, ve, e2);
|
|
e = ae->op_overload(sc);
|
|
e2 = new CommaExp(loc, new CommaExp(loc, de, e), ve);
|
|
e2 = e2->semantic(sc);
|
|
}
|
|
else if (e)
|
|
return e;
|
|
}
|
|
else if (op == TOKconstruct && !refinit)
|
|
{ Type *t2 = e2->type->toBasetype();
|
|
if (t2->ty == Tstruct &&
|
|
sd == ((TypeStruct *)t2)->sym &&
|
|
sd->cpctor)
|
|
{ /* We have a copy constructor for this
|
|
*/
|
|
// Scan past commma's
|
|
Expression *ec = NULL;
|
|
while (e2->op == TOKcomma)
|
|
{ CommaExp *ecomma = (CommaExp *)e2;
|
|
e2 = ecomma->e2;
|
|
if (ec)
|
|
ec = new CommaExp(ecomma->loc, ec, ecomma->e1);
|
|
else
|
|
ec = ecomma->e1;
|
|
}
|
|
if (e2->op == TOKquestion)
|
|
{ /* Write as:
|
|
* a ? e1 = b : e1 = c;
|
|
*/
|
|
CondExp *econd = (CondExp *)e2;
|
|
AssignExp *ea1 = new AssignExp(econd->e1->loc, e1, econd->e1);
|
|
ea1->op = op;
|
|
AssignExp *ea2 = new AssignExp(econd->e1->loc, e1, econd->e2);
|
|
ea2->op = op;
|
|
Expression *e = new CondExp(loc, econd->econd, ea1, ea2);
|
|
if (ec)
|
|
e = new CommaExp(loc, ec, e);
|
|
return e->semantic(sc);
|
|
}
|
|
else if (e2->op == TOKvar ||
|
|
e2->op == TOKdotvar ||
|
|
e2->op == TOKstar ||
|
|
e2->op == TOKthis ||
|
|
e2->op == TOKindex)
|
|
{ /* Write as:
|
|
* e1.cpctor(e2);
|
|
*/
|
|
if (!e2->type->implicitConvTo(e1->type))
|
|
error("conversion error from %s to %s", e2->type->toChars(), e1->type->toChars());
|
|
|
|
Expression *e = new DotVarExp(loc, e1, sd->cpctor, 0);
|
|
e = new CallExp(loc, e, e2);
|
|
if (ec)
|
|
e = new CommaExp(loc, ec, e);
|
|
return e->semantic(sc);
|
|
}
|
|
else if (e2->op == TOKcall)
|
|
{
|
|
/* The struct value returned from the function is transferred
|
|
* so should not call the destructor on it.
|
|
*/
|
|
valueNoDtor(e2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (t1->ty == Tclass)
|
|
{ // Disallow assignment operator overloads for same type
|
|
if (op == TOKassign && !e2->implicitConvTo(e1->type))
|
|
{
|
|
Expression *e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if (t1->ty == Tsarray && !refinit)
|
|
{
|
|
if (e1->op == TOKindex &&
|
|
((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
|
|
{
|
|
// Assignment to an AA of fixed-length arrays.
|
|
// Convert T[n][U] = T[] into T[n][U] = T[n]
|
|
e2 = e2->implicitCastTo(sc, e1->type);
|
|
if (e2->type == Type::terror)
|
|
return e2;
|
|
}
|
|
else
|
|
{
|
|
Type *t2 = e2->type->toBasetype();
|
|
// Convert e2 to e2[], unless e2-> e1[0]
|
|
if (t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf()))
|
|
{
|
|
e2 = new SliceExp(e2->loc, e2, NULL, NULL);
|
|
e2 = e2->semantic(sc);
|
|
}
|
|
|
|
// Convert e1 to e1[]
|
|
Expression *e = new SliceExp(e1->loc, e1, NULL, NULL);
|
|
e1 = e->semantic(sc);
|
|
t1 = e1->type->toBasetype();
|
|
}
|
|
}
|
|
|
|
e2 = e2->inferType(t1);
|
|
if (!e2->rvalue())
|
|
return new ErrorExp();
|
|
|
|
if (e1->op == TOKarraylength)
|
|
{
|
|
// e1 is not an lvalue, but we let code generator handle it
|
|
ArrayLengthExp *ale = (ArrayLengthExp *)e1;
|
|
|
|
ale->e1 = ale->e1->modifiableLvalue(sc, e1);
|
|
}
|
|
else if (e1->op == TOKslice)
|
|
{
|
|
Type *tn = e1->type->nextOf();
|
|
if (op == TOKassign && tn && (!tn->isMutable() || !tn->isAssignable()))
|
|
{ error("slice %s is not mutable", e1->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
else
|
|
{ // Try to do a decent error message with the expression
|
|
// before it got constant folded
|
|
if (e1->op != TOKvar)
|
|
e1 = e1->optimize(WANTvalue);
|
|
|
|
if (op != TOKconstruct)
|
|
e1 = e1->modifiableLvalue(sc, e1old);
|
|
}
|
|
|
|
Type *t2 = e2->type->toBasetype();
|
|
#if 0
|
|
if (t1->ty == Tvector && t2->ty != Tvector &&
|
|
e2->implicitConvTo(((TypeVector *)t1)->basetype->nextOf())
|
|
)
|
|
{ // memset
|
|
ismemset = 1; // make it easy for back end to tell what this is
|
|
e2 = e2->implicitCastTo(sc, ((TypeVector *)t1)->basetype->nextOf());
|
|
}
|
|
else
|
|
#endif
|
|
// If it is a array, get the element type. Note that it may be
|
|
// multi-dimensional.
|
|
Type *telem = t1;
|
|
while (telem->ty == Tarray)
|
|
telem = telem->nextOf();
|
|
|
|
// Check for block assignment. If it is of type void[], void[][], etc,
|
|
// '= null' is the only allowable block assignment (Bug 7493)
|
|
if (e1->op == TOKslice &&
|
|
t1->nextOf() && (telem->ty != Tvoid || e2->op == TOKnull) &&
|
|
e2->implicitConvTo(t1->nextOf())
|
|
)
|
|
{ // memset
|
|
ismemset = 1; // make it easy for back end to tell what this is
|
|
e2 = e2->implicitCastTo(sc, t1->nextOf());
|
|
}
|
|
else if (t1->ty == Tsarray)
|
|
{
|
|
/* Should have already converted e1 => e1[]
|
|
* unless it is an AA
|
|
*/
|
|
if (!(e1->op == TOKindex && t2->ty == Tsarray &&
|
|
((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray))
|
|
{
|
|
assert(op == TOKconstruct);
|
|
}
|
|
//error("cannot assign to static array %s", e1->toChars());
|
|
}
|
|
else if (e1->op == TOKslice && t2->ty == Tarray &&
|
|
t2->nextOf()->implicitConvTo(t1->nextOf()))
|
|
{
|
|
e2 = e2->implicitCastTo(sc, e1->type->constOf());
|
|
}
|
|
else
|
|
{
|
|
e2 = e2->implicitCastTo(sc, e1->type);
|
|
}
|
|
|
|
/* Look for array operations
|
|
*/
|
|
if (e1->op == TOKslice && !ismemset &&
|
|
(e2->op == TOKadd || e2->op == TOKmin ||
|
|
e2->op == TOKmul || e2->op == TOKdiv ||
|
|
e2->op == TOKmod || e2->op == TOKxor ||
|
|
e2->op == TOKand || e2->op == TOKor ||
|
|
#if DMDV2
|
|
e2->op == TOKpow ||
|
|
#endif
|
|
e2->op == TOKtilde || e2->op == TOKneg))
|
|
{
|
|
type = e1->type;
|
|
return arrayOp(sc);
|
|
}
|
|
|
|
if (e1->op == TOKvar &&
|
|
(((VarExp *)e1)->var->storage_class & STCscope) &&
|
|
op == TOKassign)
|
|
{
|
|
error("cannot rebind scope variables");
|
|
}
|
|
|
|
type = e1->type;
|
|
assert(type);
|
|
return this;
|
|
}
|
|
|
|
Expression *AssignExp::checkToBoolean(Scope *sc)
|
|
{
|
|
// Things like:
|
|
// if (a = b) ...
|
|
// are usually mistakes.
|
|
|
|
error("assignment cannot be used as a condition, perhaps == was meant?");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2)
|
|
: AssignExp(loc, e1, e2)
|
|
{
|
|
op = TOKconstruct;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
MinAssignExp::MinAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKminass, sizeof(MinAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKcatass, sizeof(CatAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *CatAssignExp::semantic(Scope *sc)
|
|
{
|
|
//printf("CatAssignExp::semantic() %s\n", toChars());
|
|
Expression *e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
if (e1->op == TOKslice)
|
|
{ SliceExp *se = (SliceExp *)e1;
|
|
|
|
if (se->e1->type->toBasetype()->ty == Tsarray)
|
|
{ error("cannot append to static array %s", se->e1->type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
|
|
Type *tb1 = e1->type->toBasetype();
|
|
Type *tb1next = tb1->nextOf();
|
|
|
|
e2 = e2->inferType(tb1next);
|
|
if (!e2->rvalue())
|
|
return new ErrorExp();
|
|
|
|
Type *tb2 = e2->type->toBasetype();
|
|
|
|
if ((tb1->ty == Tarray) &&
|
|
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
|
|
(e2->implicitConvTo(e1->type)
|
|
#if DMDV2
|
|
|| tb2->nextOf()->implicitConvTo(tb1next)
|
|
#endif
|
|
)
|
|
)
|
|
{ // Append array
|
|
e2 = e2->castTo(sc, e1->type);
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else if ((tb1->ty == Tarray) &&
|
|
e2->implicitConvTo(tb1next)
|
|
)
|
|
{ // Append element
|
|
e2 = e2->castTo(sc, tb1next);
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else if (tb1->ty == Tarray &&
|
|
(tb1next->ty == Tchar || tb1next->ty == Twchar) &&
|
|
e2->type->ty != tb1next->ty &&
|
|
e2->implicitConvTo(Type::tdchar)
|
|
)
|
|
{ // Append dchar to char[] or wchar[]
|
|
e2 = e2->castTo(sc, Type::tdchar);
|
|
type = e1->type;
|
|
e = this;
|
|
|
|
/* Do not allow appending wchar to char[] because if wchar happens
|
|
* to be a surrogate pair, nothing good can result.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if (tb1 != Type::terror && tb2 != Type::terror)
|
|
error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
MulAssignExp::MulAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKmulass, sizeof(MulAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DivAssignExp::DivAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKdivass, sizeof(DivAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ModAssignExp::ModAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKmodass, sizeof(ModAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ShlAssignExp::ShlAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKshlass, sizeof(ShlAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ShrAssignExp::ShrAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKshrass, sizeof(ShrAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
UshrAssignExp::UshrAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKushrass, sizeof(UshrAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
AndAssignExp::AndAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKandass, sizeof(AndAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
OrAssignExp::OrAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKorass, sizeof(OrAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
XorAssignExp::XorAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKxorass, sizeof(XorAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
/***************** PowAssignExp *******************************************/
|
|
|
|
PowAssignExp::PowAssignExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinAssignExp(loc, TOKpowass, sizeof(PowAssignExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *PowAssignExp::semantic(Scope *sc)
|
|
{
|
|
Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
assert(e1->type && e2->type);
|
|
if (e1->op == TOKslice)
|
|
{ // T[] ^^= ...
|
|
e = typeCombine(sc);
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
|
|
// Check element types are arithmetic
|
|
Type *tb1 = e1->type->nextOf()->toBasetype();
|
|
Type *tb2 = e2->type->toBasetype();
|
|
if (tb2->ty == Tarray || tb2->ty == Tsarray)
|
|
tb2 = tb2->nextOf()->toBasetype();
|
|
|
|
if ( (tb1->isintegral() || tb1->isfloating()) &&
|
|
(tb2->isintegral() || tb2->isfloating()))
|
|
{
|
|
type = e1->type;
|
|
return arrayOp(sc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
}
|
|
|
|
if ( (e1->type->isintegral() || e1->type->isfloating()) &&
|
|
(e2->type->isintegral() || e2->type->isfloating()))
|
|
{
|
|
if (e1->op == TOKvar)
|
|
{ // Rewrite: e1 = e1 ^^ e2
|
|
e = new PowExp(loc, e1->syntaxCopy(), e2);
|
|
e = new AssignExp(loc, e1, e);
|
|
}
|
|
else
|
|
{ // Rewrite: ref tmp = e1; tmp = tmp ^^ e2
|
|
Identifier *id = Lexer::uniqueId("__powtmp");
|
|
VarDeclaration *v = new VarDeclaration(e1->loc, e1->type, id, new ExpInitializer(loc, e1));
|
|
v->storage_class |= STCref | STCforeach;
|
|
Expression *de = new DeclarationExp(e1->loc, v);
|
|
VarExp *ve = new VarExp(e1->loc, v);
|
|
e = new PowExp(loc, ve, e2);
|
|
e = new AssignExp(loc, new VarExp(e1->loc, v), e);
|
|
e = new CommaExp(loc, de, e);
|
|
}
|
|
e = e->semantic(sc);
|
|
if (e->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
return e;
|
|
}
|
|
return incompatibleTypes();
|
|
}
|
|
|
|
|
|
/************************* AddExp *****************************/
|
|
|
|
AddExp::AddExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKadd, sizeof(AddExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *AddExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("AddExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (!type)
|
|
{
|
|
BinExp::semanticp(sc);
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
Type *tb1 = e1->type->toBasetype();
|
|
Type *tb2 = e2->type->toBasetype();
|
|
|
|
if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
|
|
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
|
|
tb1->nextOf()->equals(tb2->nextOf())
|
|
)
|
|
{
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else if (tb1->ty == Tpointer && e2->type->isintegral() ||
|
|
tb2->ty == Tpointer && e1->type->isintegral())
|
|
e = scaleFactor(sc);
|
|
else if (tb1->ty == Tpointer && tb2->ty == Tpointer)
|
|
{
|
|
return incompatibleTypes();
|
|
}
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
Type *tb1 = e1->type->toBasetype();
|
|
if (tb1->ty == Tvector && !tb1->isscalar())
|
|
{
|
|
return incompatibleTypes();
|
|
}
|
|
if ((tb1->isreal() && e2->type->isimaginary()) ||
|
|
(tb1->isimaginary() && e2->type->isreal()))
|
|
{
|
|
switch (type->toBasetype()->ty)
|
|
{
|
|
case Tfloat32:
|
|
case Timaginary32:
|
|
type = Type::tcomplex32;
|
|
break;
|
|
|
|
case Tfloat64:
|
|
case Timaginary64:
|
|
type = Type::tcomplex64;
|
|
break;
|
|
|
|
case Tfloat80:
|
|
case Timaginary80:
|
|
type = Type::tcomplex80;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
e = this;
|
|
}
|
|
return e;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
MinExp::MinExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKmin, sizeof(MinExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *MinExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("MinExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
e = this;
|
|
Type *t1 = e1->type->toBasetype();
|
|
Type *t2 = e2->type->toBasetype();
|
|
if (t1->ty == Tpointer)
|
|
{
|
|
if (t2->ty == Tpointer)
|
|
{ // Need to divide the result by the stride
|
|
// Replace (ptr - ptr) with (ptr - ptr) / stride
|
|
d_int64 stride;
|
|
Expression *e;
|
|
|
|
typeCombine(sc); // make sure pointer types are compatible
|
|
type = Type::tptrdiff_t;
|
|
stride = t2->nextOf()->size();
|
|
if (stride == 0)
|
|
{
|
|
e = new IntegerExp(loc, 0, Type::tptrdiff_t);
|
|
}
|
|
else
|
|
{
|
|
e = new DivExp(loc, this, new IntegerExp(0, stride, Type::tptrdiff_t));
|
|
e->type = Type::tptrdiff_t;
|
|
}
|
|
return e;
|
|
}
|
|
else if (t2->isintegral())
|
|
e = scaleFactor(sc);
|
|
else
|
|
{ error("can't subtract %s from pointer", t2->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
else if (t2->ty == Tpointer)
|
|
{
|
|
type = e2->type;
|
|
error("can't subtract pointer from %s", e1->type->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
t1 = e1->type->toBasetype();
|
|
t2 = e2->type->toBasetype();
|
|
if (t1->ty == Tvector && !t1->isscalar())
|
|
{
|
|
return incompatibleTypes();
|
|
}
|
|
if ((t1->isreal() && t2->isimaginary()) ||
|
|
(t1->isimaginary() && t2->isreal()))
|
|
{
|
|
switch (type->ty)
|
|
{
|
|
case Tfloat32:
|
|
case Timaginary32:
|
|
type = Type::tcomplex32;
|
|
break;
|
|
|
|
case Tfloat64:
|
|
case Timaginary64:
|
|
type = Type::tcomplex64;
|
|
break;
|
|
|
|
case Tfloat80:
|
|
case Timaginary80:
|
|
type = Type::tcomplex80;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/************************* CatExp *****************************/
|
|
|
|
CatExp::CatExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKcat, sizeof(CatExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *CatExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
//printf("CatExp::semantic() %s\n", toChars());
|
|
if (!type)
|
|
{
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
Type *tb1 = e1->type->toBasetype();
|
|
Type *tb2 = e2->type->toBasetype();
|
|
|
|
|
|
/* BUG: Should handle things like:
|
|
* char c;
|
|
* c ~ ' '
|
|
* ' ' ~ c;
|
|
*/
|
|
|
|
#if 0
|
|
e1->type->print();
|
|
e2->type->print();
|
|
#endif
|
|
Type *tb1next = tb1->nextOf();
|
|
Type *tb2next = tb2->nextOf();
|
|
|
|
if ((tb1->ty == Tsarray || tb1->ty == Tarray) &&
|
|
e2->implicitConvTo(tb1next) >= MATCHconvert)
|
|
{
|
|
e2 = e2->implicitCastTo(sc, tb1next);
|
|
type = tb1next->arrayOf();
|
|
if (tb2->ty == Tarray)
|
|
{ // Make e2 into [e2]
|
|
e2 = new ArrayLiteralExp(e2->loc, e2);
|
|
e2->type = type;
|
|
}
|
|
return this;
|
|
}
|
|
else if ((tb2->ty == Tsarray || tb2->ty == Tarray) &&
|
|
e1->implicitConvTo(tb2next) >= MATCHconvert)
|
|
{
|
|
e1 = e1->implicitCastTo(sc, tb2next);
|
|
type = tb2next->arrayOf();
|
|
if (tb1->ty == Tarray)
|
|
{ // Make e1 into [e1]
|
|
e1 = new ArrayLiteralExp(e1->loc, e1);
|
|
e1->type = type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
if ((tb1->ty == Tsarray || tb1->ty == Tarray) &&
|
|
(tb2->ty == Tsarray || tb2->ty == Tarray) &&
|
|
(tb1next->mod || tb2next->mod) &&
|
|
(tb1next->mod != tb2next->mod)
|
|
)
|
|
{
|
|
Type *t1 = tb1next->mutableOf()->constOf()->arrayOf();
|
|
Type *t2 = tb2next->mutableOf()->constOf()->arrayOf();
|
|
if (e1->op == TOKstring && !((StringExp *)e1)->committed)
|
|
e1->type = t1;
|
|
else
|
|
e1 = e1->castTo(sc, t1);
|
|
if (e2->op == TOKstring && !((StringExp *)e2)->committed)
|
|
e2->type = t2;
|
|
else
|
|
e2 = e2->castTo(sc, t2);
|
|
}
|
|
|
|
typeCombine(sc);
|
|
type = type->toHeadMutable();
|
|
|
|
Type *tb = type->toBasetype();
|
|
if (tb->ty == Tsarray)
|
|
type = tb->nextOf()->arrayOf();
|
|
if (type->ty == Tarray && tb1next && tb2next &&
|
|
tb1next->mod != tb2next->mod)
|
|
{
|
|
type = type->nextOf()->toHeadMutable()->arrayOf();
|
|
}
|
|
#if 0
|
|
e1->type->print();
|
|
e2->type->print();
|
|
type->print();
|
|
print();
|
|
#endif
|
|
Type *t1 = e1->type->toBasetype();
|
|
Type *t2 = e2->type->toBasetype();
|
|
if (e1->op == TOKstring && e2->op == TOKstring)
|
|
e = optimize(WANTvalue);
|
|
else if ((t1->ty == Tarray || t1->ty == Tsarray) &&
|
|
(t2->ty == Tarray || t2->ty == Tsarray))
|
|
{
|
|
e = this;
|
|
}
|
|
else
|
|
{
|
|
//printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars());
|
|
incompatibleTypes();
|
|
return new ErrorExp();
|
|
}
|
|
e->type = e->type->semantic(loc, sc);
|
|
return e;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
MulExp::MulExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKmul, sizeof(MulExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *MulExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if 0
|
|
printf("MulExp::semantic() %s\n", toChars());
|
|
#endif
|
|
if (type)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkArithmetic();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkArithmetic();
|
|
if (type->isfloating())
|
|
{ Type *t1 = e1->type;
|
|
Type *t2 = e2->type;
|
|
|
|
if (t1->isreal())
|
|
{
|
|
type = t2;
|
|
}
|
|
else if (t2->isreal())
|
|
{
|
|
type = t1;
|
|
}
|
|
else if (t1->isimaginary())
|
|
{
|
|
if (t2->isimaginary())
|
|
{ Expression *e;
|
|
|
|
switch (t1->toBasetype()->ty)
|
|
{
|
|
case Timaginary32: type = Type::tfloat32; break;
|
|
case Timaginary64: type = Type::tfloat64; break;
|
|
case Timaginary80: type = Type::tfloat80; break;
|
|
default: assert(0);
|
|
}
|
|
|
|
// iy * iv = -yv
|
|
e1->type = type;
|
|
e2->type = type;
|
|
e = new NegExp(loc, this);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else
|
|
type = t2; // t2 is complex
|
|
}
|
|
else if (t2->isimaginary())
|
|
{
|
|
type = t1; // t1 is complex
|
|
}
|
|
}
|
|
else if (type->toBasetype()->ty == Tvector &&
|
|
((TypeVector *)type->toBasetype())->elementType()->size(loc) != 2)
|
|
{ // Only short[8] and ushort[8] work with multiply
|
|
return incompatibleTypes();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
DivExp::DivExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKdiv, sizeof(DivExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *DivExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkArithmetic();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkArithmetic();
|
|
if (type->isfloating())
|
|
{ Type *t1 = e1->type;
|
|
Type *t2 = e2->type;
|
|
|
|
if (t1->isreal())
|
|
{
|
|
type = t2;
|
|
if (t2->isimaginary())
|
|
{ Expression *e;
|
|
|
|
// x/iv = i(-x/v)
|
|
e2->type = t1;
|
|
e = new NegExp(loc, this);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
else if (t2->isreal())
|
|
{
|
|
type = t1;
|
|
}
|
|
else if (t1->isimaginary())
|
|
{
|
|
if (t2->isimaginary())
|
|
{
|
|
switch (t1->toBasetype()->ty)
|
|
{
|
|
case Timaginary32: type = Type::tfloat32; break;
|
|
case Timaginary64: type = Type::tfloat64; break;
|
|
case Timaginary80: type = Type::tfloat80; break;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
else
|
|
type = t2; // t2 is complex
|
|
}
|
|
else if (t2->isimaginary())
|
|
{
|
|
type = t1; // t1 is complex
|
|
}
|
|
}
|
|
else if (type->toBasetype()->ty == Tvector)
|
|
{ incompatibleTypes();
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ModExp::ModExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKmod, sizeof(ModExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *ModExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkArithmetic();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkArithmetic();
|
|
if (type->toBasetype()->ty == Tvector)
|
|
{ incompatibleTypes();
|
|
return new ErrorExp();
|
|
}
|
|
if (type->isfloating())
|
|
{ type = e1->type;
|
|
if (e2->type->iscomplex())
|
|
{ error("cannot perform modulo complex arithmetic");
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
PowExp::PowExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKpow, sizeof(PowExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *PowExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
//printf("PowExp::semantic() %s\n", toChars());
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
assert(e1->type && e2->type);
|
|
typeCombine(sc);
|
|
|
|
if (e1->op == TOKslice)
|
|
{
|
|
// Check element types are arithmetic
|
|
Type *tb1 = e1->type->nextOf()->toBasetype();
|
|
Type *tb2 = e2->type->toBasetype();
|
|
if (tb2->ty == Tarray || tb2->ty == Tsarray)
|
|
tb2 = tb2->nextOf()->toBasetype();
|
|
|
|
if ( (tb1->isintegral() || tb1->isfloating()) &&
|
|
(tb2->isintegral() || tb2->isfloating()))
|
|
{
|
|
type = e1->type;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
if ( (e1->type->isintegral() || e1->type->isfloating()) &&
|
|
(e2->type->isintegral() || e2->type->isfloating()))
|
|
{
|
|
// For built-in numeric types, there are several cases.
|
|
// TODO: backend support, especially for e1 ^^ 2.
|
|
|
|
bool wantSqrt = false;
|
|
|
|
// First, attempt to fold the expression.
|
|
e = optimize(WANTvalue);
|
|
if (e->op != TOKpow)
|
|
{
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
// Determine if we're raising to an integer power.
|
|
sinteger_t intpow = 0;
|
|
if (e2->op == TOKint64 && ((sinteger_t)e2->toInteger() == 2 || (sinteger_t)e2->toInteger() == 3))
|
|
intpow = e2->toInteger();
|
|
else if (e2->op == TOKfloat64 && (e2->toReal() == (sinteger_t)(e2->toReal())))
|
|
intpow = (sinteger_t)(e2->toReal());
|
|
|
|
// Deal with x^^2, x^^3 immediately, since they are of practical importance.
|
|
if (intpow == 2 || intpow == 3)
|
|
{
|
|
// Replace x^^2 with (tmp = x, tmp*tmp)
|
|
// Replace x^^3 with (tmp = x, tmp*tmp*tmp)
|
|
Identifier *idtmp = Lexer::uniqueId("__powtmp");
|
|
VarDeclaration *tmp = new VarDeclaration(loc, e1->type->toBasetype(), idtmp, new ExpInitializer(0, e1));
|
|
tmp->storage_class = STCctfe;
|
|
Expression *ve = new VarExp(loc, tmp);
|
|
Expression *ae = new DeclarationExp(loc, tmp);
|
|
/* Note that we're reusing ve. This should be ok.
|
|
*/
|
|
Expression *me = new MulExp(loc, ve, ve);
|
|
if (intpow == 3)
|
|
me = new MulExp(loc, me, ve);
|
|
e = new CommaExp(loc, ae, me);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
static int importMathChecked = 0;
|
|
if (!importMathChecked)
|
|
{
|
|
importMathChecked = 1;
|
|
for (size_t i = 0; i < Module::amodules.dim; i++)
|
|
{ Module *mi = Module::amodules.tdata()[i];
|
|
//printf("\t[%d] %s\n", i, mi->toChars());
|
|
if (mi->ident == Id::math &&
|
|
mi->parent->ident == Id::std &&
|
|
!mi->parent->parent)
|
|
goto L1;
|
|
}
|
|
error("must import std.math to use ^^ operator");
|
|
return new ErrorExp();
|
|
|
|
L1: ;
|
|
}
|
|
|
|
e = new IdentifierExp(loc, Id::empty);
|
|
e = new DotIdExp(loc, e, Id::std);
|
|
e = new DotIdExp(loc, e, Id::math);
|
|
if (e2->op == TOKfloat64 && e2->toReal() == 0.5)
|
|
{ // Replace e1 ^^ 0.5 with .std.math.sqrt(x)
|
|
e = new CallExp(loc, new DotIdExp(loc, e, Id::_sqrt), e1);
|
|
}
|
|
else
|
|
{
|
|
// Replace e1 ^^ e2 with .std.math.pow(e1, e2)
|
|
e = new CallExp(loc, new DotIdExp(loc, e, Id::_pow), e1, e2);
|
|
}
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
return incompatibleTypes();
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKshl, sizeof(ShlExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *ShlExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
//printf("ShlExp::semantic(), type = %p\n", type);
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
e1 = e1->checkIntegral();
|
|
e2 = e2->checkIntegral();
|
|
if (e1->type->toBasetype()->ty == Tvector ||
|
|
e2->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
e1 = e1->integralPromotions(sc);
|
|
e2 = e2->castTo(sc, Type::tshiftcnt);
|
|
type = e1->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKshr, sizeof(ShrExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *ShrExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
e1 = e1->checkIntegral();
|
|
e2 = e2->checkIntegral();
|
|
if (e1->type->toBasetype()->ty == Tvector ||
|
|
e2->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
e1 = e1->integralPromotions(sc);
|
|
e2 = e2->castTo(sc, Type::tshiftcnt);
|
|
type = e1->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKushr, sizeof(UshrExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *UshrExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
e1 = e1->checkIntegral();
|
|
e2 = e2->checkIntegral();
|
|
if (e1->type->toBasetype()->ty == Tvector ||
|
|
e2->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
e1 = e1->integralPromotions(sc);
|
|
e2 = e2->castTo(sc, Type::tshiftcnt);
|
|
type = e1->type;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
AndExp::AndExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKand, sizeof(AndExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *AndExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
if (e1->type->toBasetype()->ty == Tbool &&
|
|
e2->type->toBasetype()->ty == Tbool)
|
|
{
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkIntegral();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkIntegral();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
OrExp::OrExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKor, sizeof(OrExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *OrExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
if (e1->type->toBasetype()->ty == Tbool &&
|
|
e2->type->toBasetype()->ty == Tbool)
|
|
{
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkIntegral();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkIntegral();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
XorExp::XorExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKxor, sizeof(XorExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *XorExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (!type)
|
|
{ BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
if (e1->type->toBasetype()->ty == Tbool &&
|
|
e2->type->toBasetype()->ty == Tbool)
|
|
{
|
|
type = e1->type;
|
|
e = this;
|
|
}
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
if (!e1->isArrayOperand())
|
|
e1->checkIntegral();
|
|
if (!e2->isArrayOperand())
|
|
e2->checkIntegral();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
OrOrExp::OrOrExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKoror, sizeof(OrOrExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *OrOrExp::semantic(Scope *sc)
|
|
{
|
|
unsigned cs1;
|
|
|
|
// same as for AndAnd
|
|
e1 = e1->semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->checkToPointer();
|
|
e1 = e1->checkToBoolean(sc);
|
|
cs1 = sc->callSuper;
|
|
|
|
if (sc->flags & SCOPEstaticif)
|
|
{
|
|
/* If in static if, don't evaluate e2 if we don't have to.
|
|
*/
|
|
e1 = e1->optimize(WANTflags);
|
|
if (e1->isBool(TRUE))
|
|
{
|
|
return new IntegerExp(loc, 1, Type::tboolean);
|
|
}
|
|
}
|
|
|
|
e2 = e2->semantic(sc);
|
|
sc->mergeCallSuper(loc, cs1);
|
|
e2 = resolveProperties(sc, e2);
|
|
e2 = e2->checkToPointer();
|
|
|
|
if (e2->type->ty == Tvoid)
|
|
type = Type::tvoid;
|
|
else
|
|
{
|
|
e2 = e2->checkToBoolean(sc);
|
|
type = Type::tboolean;
|
|
}
|
|
if (e2->op == TOKtype || e2->op == TOKimport)
|
|
{ error("%s is not an expression", e2->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
if (e2->op == TOKerror)
|
|
return e2;
|
|
return this;
|
|
}
|
|
|
|
Expression *OrOrExp::checkToBoolean(Scope *sc)
|
|
{
|
|
e2 = e2->checkToBoolean(sc);
|
|
return this;
|
|
}
|
|
|
|
int OrOrExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
AndAndExp::AndAndExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKandand, sizeof(AndAndExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *AndAndExp::semantic(Scope *sc)
|
|
{
|
|
unsigned cs1;
|
|
|
|
// same as for OrOr
|
|
e1 = e1->semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
e1 = e1->checkToPointer();
|
|
e1 = e1->checkToBoolean(sc);
|
|
cs1 = sc->callSuper;
|
|
|
|
if (sc->flags & SCOPEstaticif)
|
|
{
|
|
/* If in static if, don't evaluate e2 if we don't have to.
|
|
*/
|
|
e1 = e1->optimize(WANTflags);
|
|
if (e1->isBool(FALSE))
|
|
{
|
|
return new IntegerExp(loc, 0, Type::tboolean);
|
|
}
|
|
}
|
|
|
|
e2 = e2->semantic(sc);
|
|
sc->mergeCallSuper(loc, cs1);
|
|
e2 = resolveProperties(sc, e2);
|
|
e2 = e2->checkToPointer();
|
|
|
|
if (e2->type->ty == Tvoid)
|
|
type = Type::tvoid;
|
|
else
|
|
{
|
|
e2 = e2->checkToBoolean(sc);
|
|
type = Type::tboolean;
|
|
}
|
|
if (e2->op == TOKtype || e2->op == TOKimport)
|
|
{ error("%s is not an expression", e2->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (e1->op == TOKerror)
|
|
return e1;
|
|
if (e2->op == TOKerror)
|
|
return e2;
|
|
return this;
|
|
}
|
|
|
|
Expression *AndAndExp::checkToBoolean(Scope *sc)
|
|
{
|
|
e2 = e2->checkToBoolean(sc);
|
|
return this;
|
|
}
|
|
|
|
int AndAndExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
InExp::InExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKin, sizeof(InExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *InExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
e = op_overload(sc);
|
|
if (e)
|
|
return e;
|
|
|
|
//type = Type::tboolean;
|
|
Type *t2b = e2->type->toBasetype();
|
|
switch (t2b->ty)
|
|
{
|
|
case Taarray:
|
|
{
|
|
TypeAArray *ta = (TypeAArray *)t2b;
|
|
|
|
#if DMDV2
|
|
// Special handling for array keys
|
|
if (!arrayTypeCompatible(e1->loc, e1->type, ta->index))
|
|
#endif
|
|
{
|
|
// Convert key to type of key
|
|
e1 = e1->implicitCastTo(sc, ta->index);
|
|
}
|
|
|
|
// Return type is pointer to value
|
|
type = ta->nextOf()->pointerTo();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
error("rvalue of in expression must be an associative array, not %s", e2->type->toChars());
|
|
case Terror:
|
|
return new ErrorExp();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
int InExp::isBit()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
/* This deletes the key e1 from the associative array e2
|
|
*/
|
|
|
|
RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKremove, sizeof(RemoveExp), e1, e2)
|
|
{
|
|
type = Type::tboolean;
|
|
}
|
|
|
|
void RemoveExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, e1, PREC_primary);
|
|
buf->writestring(".remove(");
|
|
expToCBuffer(buf, hgs, e2, PREC_assign);
|
|
buf->writestring(")");
|
|
}
|
|
|
|
/************************************************************/
|
|
|
|
CmpExp::CmpExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, op, sizeof(CmpExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *CmpExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("CmpExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
|
|
Type *t1 = e1->type->toBasetype();
|
|
Type *t2 = e2->type->toBasetype();
|
|
if (t1->ty == Tclass && e2->op == TOKnull ||
|
|
t2->ty == Tclass && e1->op == TOKnull)
|
|
{
|
|
error("do not use null when comparing class types");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
e = op_overload(sc);
|
|
if (e)
|
|
{
|
|
if (!e->type->isscalar() && e->type->equals(e1->type))
|
|
{
|
|
error("recursive opCmp expansion");
|
|
e = new ErrorExp();
|
|
}
|
|
else if (e->op == TOKcall)
|
|
{ e = new CmpExp(op, loc, e, new IntegerExp(loc, 0, Type::tint32));
|
|
e = e->semantic(sc);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/* Disallow comparing T[]==T and T==T[]
|
|
*/
|
|
if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) ||
|
|
e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf()))
|
|
{
|
|
incompatibleTypes();
|
|
return new ErrorExp();
|
|
}
|
|
|
|
Expression *eb1 = e1;
|
|
Expression *eb2 = e2;
|
|
|
|
e = typeCombine(sc);
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
|
|
#if 0
|
|
// For integer comparisons, ensure the combined type can hold both arguments.
|
|
if (type && type->isintegral() && (op == TOKlt || op == TOKle ||
|
|
op == TOKgt || op == TOKge))
|
|
{
|
|
IntRange trange = IntRange::fromType(type);
|
|
|
|
Expression *errorexp = 0;
|
|
if (!trange.contains(eb1->getIntRange()))
|
|
errorexp = eb1;
|
|
if (!trange.contains(eb2->getIntRange()))
|
|
errorexp = eb2;
|
|
|
|
if (errorexp)
|
|
{
|
|
error("implicit conversion of '%s' to '%s' is unsafe in '(%s) %s (%s)'",
|
|
errorexp->toChars(), type->toChars(), eb1->toChars(), Token::toChars(op), eb2->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
type = Type::tboolean;
|
|
|
|
// Special handling for array comparisons
|
|
t1 = e1->type->toBasetype();
|
|
t2 = e2->type->toBasetype();
|
|
if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) &&
|
|
(t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer))
|
|
{
|
|
if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst &&
|
|
t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst &&
|
|
(t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid))
|
|
error("array comparison type mismatch, %s vs %s", t1->nextOf()->toChars(), t2->nextOf()->toChars());
|
|
e = this;
|
|
}
|
|
else if (t1->ty == Tstruct || t2->ty == Tstruct ||
|
|
(t1->ty == Tclass && t2->ty == Tclass))
|
|
{
|
|
if (t2->ty == Tstruct)
|
|
error("need member function opCmp() for %s %s to compare", t2->toDsymbol(sc)->kind(), t2->toChars());
|
|
else
|
|
error("need member function opCmp() for %s %s to compare", t1->toDsymbol(sc)->kind(), t1->toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
#if 1
|
|
else if (t1->iscomplex() || t2->iscomplex())
|
|
{
|
|
error("compare not defined for complex operands");
|
|
e = new ErrorExp();
|
|
}
|
|
#endif
|
|
else if (t1->ty == Tvector)
|
|
return incompatibleTypes();
|
|
else
|
|
{ if (!e1->rvalue() || !e2->rvalue())
|
|
return new ErrorExp();
|
|
e = this;
|
|
}
|
|
//printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars());
|
|
return e;
|
|
}
|
|
|
|
int CmpExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/************************************************************/
|
|
|
|
EqualExp::EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, op, sizeof(EqualExp), e1, e2)
|
|
{
|
|
assert(op == TOKequal || op == TOKnotequal);
|
|
}
|
|
|
|
int needDirectEq(Type *t1, Type *t2)
|
|
{
|
|
assert(t1->ty == Tarray || t1->ty == Tsarray);
|
|
assert(t2->ty == Tarray || t2->ty == Tsarray);
|
|
|
|
Type *t1n = t1->nextOf()->toBasetype();
|
|
Type *t2n = t2->nextOf()->toBasetype();
|
|
|
|
if (((t1n->ty == Tchar || t1n->ty == Twchar || t1n->ty == Tdchar) &&
|
|
(t2n->ty == Tchar || t2n->ty == Twchar || t2n->ty == Tdchar)) ||
|
|
(t1n->ty == Tvoid || t2n->ty == Tvoid))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (t1n->constOf() != t2n->constOf())
|
|
return TRUE;
|
|
|
|
Type *t = t1n;
|
|
while (t->toBasetype()->nextOf())
|
|
t = t->nextOf()->toBasetype();
|
|
if (t->ty != Tstruct)
|
|
return FALSE;
|
|
|
|
return ((TypeStruct *)t)->sym->xeq == StructDeclaration::xerreq;
|
|
}
|
|
|
|
Expression *EqualExp::semantic(Scope *sc)
|
|
{ Expression *e;
|
|
|
|
//printf("EqualExp::semantic('%s')\n", toChars());
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
|
|
/* Before checking for operator overloading, check to see if we're
|
|
* comparing the addresses of two statics. If so, we can just see
|
|
* if they are the same symbol.
|
|
*/
|
|
if (e1->op == TOKaddress && e2->op == TOKaddress)
|
|
{ AddrExp *ae1 = (AddrExp *)e1;
|
|
AddrExp *ae2 = (AddrExp *)e2;
|
|
|
|
if (ae1->e1->op == TOKvar && ae2->e1->op == TOKvar)
|
|
{ VarExp *ve1 = (VarExp *)ae1->e1;
|
|
VarExp *ve2 = (VarExp *)ae2->e1;
|
|
|
|
if (ve1->var == ve2->var /*|| ve1->var->toSymbol() == ve2->var->toSymbol()*/)
|
|
{
|
|
// They are the same, result is 'true' for ==, 'false' for !=
|
|
e = new IntegerExp(loc, (op == TOKequal), Type::tboolean);
|
|
return e;
|
|
}
|
|
}
|
|
}
|
|
|
|
Type *t1 = e1->type->toBasetype();
|
|
Type *t2 = e2->type->toBasetype();
|
|
if (t1->ty == Tclass && e2->op == TOKnull ||
|
|
t2->ty == Tclass && e1->op == TOKnull)
|
|
{
|
|
error("use '%s' instead of '%s' when comparing with null",
|
|
Token::toChars(op == TOKequal ? TOKidentity : TOKnotidentity),
|
|
Token::toChars(op));
|
|
return new ErrorExp();
|
|
}
|
|
|
|
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
|
|
(t2->ty == Tarray || t2->ty == Tsarray))
|
|
{
|
|
if (needDirectEq(t1, t2))
|
|
{ /* Rewrite as:
|
|
* _ArrayEq(e1, e2)
|
|
*/
|
|
Expression *eq = new IdentifierExp(loc, Id::_ArrayEq);
|
|
Expressions *args = new Expressions();
|
|
args->push(e1);
|
|
args->push(e2);
|
|
e = new CallExp(loc, eq, args);
|
|
if (op == TOKnotequal)
|
|
e = new NotExp(loc, e);
|
|
e = e->trySemantic(sc); // for better error message
|
|
if (!e)
|
|
{ error("cannot compare %s and %s", t1->toChars(), t2->toChars());
|
|
return new ErrorExp();
|
|
}
|
|
return e;
|
|
}
|
|
}
|
|
|
|
//if (e2->op != TOKnull)
|
|
{
|
|
e = op_overload(sc);
|
|
if (e)
|
|
{
|
|
if (e->op == TOKcall && op == TOKnotequal)
|
|
{
|
|
e = new NotExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
}
|
|
return e;
|
|
}
|
|
}
|
|
|
|
/* Disallow comparing T[]==T and T==T[]
|
|
*/
|
|
if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) ||
|
|
e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf()))
|
|
{
|
|
incompatibleTypes();
|
|
return new ErrorExp();
|
|
}
|
|
|
|
e = typeCombine(sc);
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
|
|
type = Type::tboolean;
|
|
|
|
// Special handling for array comparisons
|
|
if (!arrayTypeCompatible(loc, e1->type, e2->type))
|
|
{
|
|
if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating())
|
|
{
|
|
// Cast both to complex
|
|
e1 = e1->castTo(sc, Type::tcomplex80);
|
|
e2 = e2->castTo(sc, Type::tcomplex80);
|
|
}
|
|
}
|
|
|
|
if (e1->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
|
|
return e;
|
|
}
|
|
|
|
int EqualExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************/
|
|
|
|
IdentityExp::IdentityExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
|
|
: BinExp(loc, op, sizeof(IdentityExp), e1, e2)
|
|
{
|
|
}
|
|
|
|
Expression *IdentityExp::semantic(Scope *sc)
|
|
{
|
|
if (type)
|
|
return this;
|
|
|
|
BinExp::semanticp(sc);
|
|
type = Type::tboolean;
|
|
|
|
Expression *e = typeCombine(sc);
|
|
if (e->op == TOKerror)
|
|
return e;
|
|
|
|
if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating())
|
|
{
|
|
// Cast both to complex
|
|
e1 = e1->castTo(sc, Type::tcomplex80);
|
|
e2 = e2->castTo(sc, Type::tcomplex80);
|
|
}
|
|
|
|
if (e1->type->toBasetype()->ty == Tvector)
|
|
return incompatibleTypes();
|
|
|
|
return this;
|
|
}
|
|
|
|
int IdentityExp::isBit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
CondExp::CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2)
|
|
: BinExp(loc, TOKquestion, sizeof(CondExp), e1, e2)
|
|
{
|
|
this->econd = econd;
|
|
}
|
|
|
|
Expression *CondExp::syntaxCopy()
|
|
{
|
|
return new CondExp(loc, econd->syntaxCopy(), e1->syntaxCopy(), e2->syntaxCopy());
|
|
}
|
|
|
|
|
|
Expression *CondExp::semantic(Scope *sc)
|
|
{ Type *t1;
|
|
Type *t2;
|
|
unsigned cs0;
|
|
unsigned cs1;
|
|
|
|
#if LOGSEMANTIC
|
|
printf("CondExp::semantic('%s')\n", toChars());
|
|
#endif
|
|
if (type)
|
|
return this;
|
|
|
|
econd = econd->semantic(sc);
|
|
econd = resolveProperties(sc, econd);
|
|
econd = econd->checkToPointer();
|
|
econd = econd->checkToBoolean(sc);
|
|
|
|
#if 0 /* this cannot work right because the types of e1 and e2
|
|
* both contribute to the type of the result.
|
|
*/
|
|
if (sc->flags & SCOPEstaticif)
|
|
{
|
|
/* If in static if, don't evaluate what we don't have to.
|
|
*/
|
|
econd = econd->optimize(WANTflags);
|
|
if (econd->isBool(TRUE))
|
|
{
|
|
e1 = e1->semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
return e1;
|
|
}
|
|
else if (econd->isBool(FALSE))
|
|
{
|
|
e2 = e2->semantic(sc);
|
|
e2 = resolveProperties(sc, e2);
|
|
return e2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
cs0 = sc->callSuper;
|
|
e1 = e1->semantic(sc);
|
|
e1 = resolveProperties(sc, e1);
|
|
cs1 = sc->callSuper;
|
|
sc->callSuper = cs0;
|
|
e2 = e2->semantic(sc);
|
|
e2 = resolveProperties(sc, e2);
|
|
sc->mergeCallSuper(loc, cs1);
|
|
|
|
|
|
// If either operand is void, the result is void
|
|
t1 = e1->type;
|
|
t2 = e2->type;
|
|
if (t1->ty == Tvoid || t2->ty == Tvoid)
|
|
type = Type::tvoid;
|
|
else if (t1 == t2)
|
|
type = t1;
|
|
else
|
|
{
|
|
typeCombine(sc);
|
|
switch (e1->type->toBasetype()->ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
e2 = e2->castTo(sc, e1->type);
|
|
break;
|
|
}
|
|
switch (e2->type->toBasetype()->ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
e1 = e1->castTo(sc, e2->type);
|
|
break;
|
|
}
|
|
if (type->toBasetype()->ty == Tarray)
|
|
{
|
|
e1 = e1->castTo(sc, type);
|
|
e2 = e2->castTo(sc, type);
|
|
}
|
|
}
|
|
#if 0
|
|
printf("res: %s\n", type->toChars());
|
|
printf("e1 : %s\n", e1->type->toChars());
|
|
printf("e2 : %s\n", e2->type->toChars());
|
|
#endif
|
|
return this;
|
|
}
|
|
|
|
#if DMDV2
|
|
int CondExp::isLvalue()
|
|
{
|
|
return e1->isLvalue() && e2->isLvalue();
|
|
}
|
|
#endif
|
|
|
|
Expression *CondExp::toLvalue(Scope *sc, Expression *ex)
|
|
{
|
|
PtrExp *e;
|
|
|
|
// convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
|
|
e = new PtrExp(loc, this, type);
|
|
|
|
e1 = e1->addressOf(sc);
|
|
e2 = e2->addressOf(sc);
|
|
|
|
typeCombine(sc);
|
|
|
|
type = e2->type;
|
|
return e;
|
|
}
|
|
|
|
Expression *CondExp::modifiableLvalue(Scope *sc, Expression *e)
|
|
{
|
|
//error("conditional expression %s is not a modifiable lvalue", toChars());
|
|
e1 = e1->modifiableLvalue(sc, e1);
|
|
e2 = e2->modifiableLvalue(sc, e1);
|
|
return toLvalue(sc, this);
|
|
}
|
|
|
|
void CondExp::checkEscape()
|
|
{
|
|
e1->checkEscape();
|
|
e2->checkEscape();
|
|
}
|
|
|
|
void CondExp::checkEscapeRef()
|
|
{
|
|
e1->checkEscapeRef();
|
|
e2->checkEscapeRef();
|
|
}
|
|
|
|
|
|
Expression *CondExp::checkToBoolean(Scope *sc)
|
|
{
|
|
e1 = e1->checkToBoolean(sc);
|
|
e2 = e2->checkToBoolean(sc);
|
|
return this;
|
|
}
|
|
|
|
|
|
void CondExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
expToCBuffer(buf, hgs, econd, PREC_oror);
|
|
buf->writestring(" ? ");
|
|
expToCBuffer(buf, hgs, e1, PREC_expr);
|
|
buf->writestring(" : ");
|
|
expToCBuffer(buf, hgs, e2, PREC_cond);
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
DefaultInitExp::DefaultInitExp(Loc loc, enum TOK subop, int size)
|
|
: Expression(loc, TOKdefault, size)
|
|
{
|
|
this->subop = subop;
|
|
}
|
|
|
|
void DefaultInitExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
buf->writestring(Token::toChars(subop));
|
|
}
|
|
|
|
/****************************************************************/
|
|
|
|
FileInitExp::FileInitExp(Loc loc)
|
|
: DefaultInitExp(loc, TOKfile, sizeof(FileInitExp))
|
|
{
|
|
}
|
|
|
|
Expression *FileInitExp::semantic(Scope *sc)
|
|
{
|
|
//printf("FileInitExp::semantic()\n");
|
|
type = Type::tchar->invariantOf()->arrayOf();
|
|
return this;
|
|
}
|
|
|
|
Expression *FileInitExp::resolveLoc(Loc loc, Scope *sc)
|
|
{
|
|
//printf("FileInitExp::resolve() %s\n", toChars());
|
|
const char *s = loc.filename ? loc.filename : sc->module->ident->toChars();
|
|
Expression *e = new StringExp(loc, (char *)s);
|
|
e = e->semantic(sc);
|
|
e = e->castTo(sc, type);
|
|
return e;
|
|
}
|
|
|
|
/****************************************************************/
|
|
|
|
LineInitExp::LineInitExp(Loc loc)
|
|
: DefaultInitExp(loc, TOKline, sizeof(LineInitExp))
|
|
{
|
|
}
|
|
|
|
Expression *LineInitExp::semantic(Scope *sc)
|
|
{
|
|
type = Type::tint32;
|
|
return this;
|
|
}
|
|
|
|
Expression *LineInitExp::resolveLoc(Loc loc, Scope *sc)
|
|
{
|
|
Expression *e = new IntegerExp(loc, loc.linnum, Type::tint32);
|
|
e = e->castTo(sc, type);
|
|
return e;
|
|
}
|
|
|
|
|