2 * Checks whether member access or array casting is allowed in `@safe` code.
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#function-safety, Function Safety)
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/safe.d, _safe.d)
10 * Documentation: https://dlang.org/phobos/dmd_safe.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/safe.d
16 import core
.stdc
.stdio
;
21 import dmd
.declaration
;
23 import dmd
.expression
;
25 import dmd
.identifier
;
31 /*************************************************************
32 * Check for unsafe access in @safe code:
33 * 1. read overlapped pointers
34 * 2. write misaligned pointers
35 * 3. write overlapped storage classes
36 * Print error if unsafe.
39 * e = expression to check
40 * readonly = if access is read-only
41 * printmsg = print error message if true
46 bool checkUnsafeAccess(Scope
* sc
, Expression e
, bool readonly
, bool printmsg
)
48 //printf("checkUnsafeAccess(e: '%s', readonly: %d, printmsg: %d)\n", e.toChars(), readonly, printmsg);
49 if (e
.op
!= TOK
.dotVariable
)
51 DotVarExp dve
= cast(DotVarExp
)e
;
52 if (VarDeclaration v
= dve
.var
.isVarDeclaration())
54 if (sc
.intypeof ||
!sc
.func ||
!sc
.func
.isSafeBypassingInference())
56 auto ad
= v
.toParent2().isAggregateDeclaration();
60 // needed to set v.overlapped and v.overlapUnsafe
61 if (ad
.sizeok
!= Sizeok
.done
)
62 ad
.determineSize(ad
.loc
);
64 const hasPointers
= v
.type
.hasPointers();
67 if (v
.overlapped
&& sc
.func
.setUnsafe())
70 e
.error("field `%s.%s` cannot access pointers in `@safe` code that overlap other fields",
71 ad
.toChars(), v
.toChars());
76 if (v
.type
.hasInvariant())
78 if (v
.overlapped
&& sc
.func
.setUnsafe())
81 e
.error("field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
82 ad
.toChars(), v
.toChars());
87 if (readonly ||
!e
.type
.isMutable())
90 if (hasPointers
&& v
.type
.toBasetype().ty
!= Tstruct
)
92 if ((!ad
.type
.alignment
.isDefault() && ad
.type
.alignment
.get() < target
.ptrsize ||
93 (v
.offset
& (target
.ptrsize
- 1))) &&
97 e
.error("field `%s.%s` cannot modify misaligned pointers in `@safe` code",
98 ad
.toChars(), v
.toChars());
103 if (v
.overlapUnsafe
&& sc
.func
.setUnsafe())
106 e
.error("field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
107 ad
.toChars(), v
.toChars());
115 /**********************************************
116 * Determine if it is @safe to cast e from tfrom to tto.
118 * e = expression to be cast
120 * tto = type to cast e to
124 bool isSafeCast(Expression e
, Type tfrom
, Type tto
)
126 // Implicit conversions are always safe
127 if (tfrom
.implicitConvTo(tto
))
130 if (!tto
.hasPointers())
133 auto tfromb
= tfrom
.toBasetype();
134 auto ttob
= tto
.toBasetype();
136 if (ttob
.ty
== Tclass
&& tfromb
.ty
== Tclass
)
138 ClassDeclaration cdfrom
= tfromb
.isClassHandle();
139 ClassDeclaration cdto
= ttob
.isClassHandle();
142 if (!cdfrom
.isBaseOf(cdto
, &offset
) &&
143 !((cdfrom
.isInterfaceDeclaration() || cdto
.isInterfaceDeclaration())
144 && cdfrom
.classKind
== ClassKind
.d
&& cdto
.classKind
== ClassKind
.d
))
147 if (cdfrom
.isCPPinterface() || cdto
.isCPPinterface())
150 if (!MODimplicitConv(tfromb
.mod
, ttob
.mod
))
155 if (ttob
.ty
== Tarray
&& tfromb
.ty
== Tsarray
) // https://issues.dlang.org/show_bug.cgi?id=12502
156 tfromb
= tfromb
.nextOf().arrayOf();
158 if (ttob
.ty
== Tarray
&& tfromb
.ty
== Tarray ||
159 ttob
.ty
== Tpointer
&& tfromb
.ty
== Tpointer
)
161 Type ttobn
= ttob
.nextOf().toBasetype();
162 Type tfromn
= tfromb
.nextOf().toBasetype();
164 /* From void[] to anything mutable is unsafe because:
167 * int[] ai = cast(int[]) av;
171 if (tfromn
.ty
== Tvoid
&& ttobn
.isMutable())
173 if (ttob
.ty
== Tarray
&& e
.op
== TOK
.arrayLiteral
)
178 // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
179 if (ttobn
.ty
== Tstruct
&& !(cast(TypeStruct
)ttobn
).sym
.members ||
180 tfromn
.ty
== Tstruct
&& !(cast(TypeStruct
)tfromn
).sym
.members
)
183 const frompointers
= tfromn
.hasPointers();
184 const topointers
= ttobn
.hasPointers();
186 if (frompointers
&& !topointers
&& ttobn
.isMutable())
189 if (!frompointers
&& topointers
)
193 ttobn
.ty
!= Tfunction
&& tfromn
.ty
!= Tfunction
&&
194 (ttob
.ty
== Tarray || ttobn
.size() <= tfromn
.size()) &&
195 MODimplicitConv(tfromn
.mod
, ttobn
.mod
))
203 /*************************************************
204 * Check for unsafe use of `.ptr` or `.funcptr`
207 * e = expression for error messages
208 * id = `ptr` or `funcptr`
213 bool checkUnsafeDotExp(Scope
* sc
, Expression e
, Identifier id
, int flag
)
215 if (!(flag
& DotExpFlag
.noDeref
) && // this use is attempting a dereference
216 sc
.func
&& // inside a function
217 !sc
.intypeof
&& // allow unsafe code in typeof expressions
218 !(sc
.flags
& SCOPE
.debug_
) && // allow unsafe code in debug statements
219 sc
.func
.setUnsafe()) // infer this function to be unsafe
222 e
.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e
.toChars(), e
.toChars());
224 e
.error("`%s.%s` cannot be used in `@safe` code", e
.toChars(), id
.toChars());