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-2024 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://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
;
29 import dmd
.func
: setUnsafe
, setUnsafePreview
;
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
!= EXP
.dotVariable
)
51 DotVarExp dve
= cast(DotVarExp
)e
;
52 if (VarDeclaration v
= dve
.var
.isVarDeclaration())
56 auto ad
= v
.isMember2();
60 import dmd
.globals
: global
;
63 if (sc
.setUnsafePreview(global
.params
.systemVariables
, !printmsg
, e
.loc
,
64 "cannot access `@system` field `%s.%s` in `@safe` code", ad
, v
))
68 // This branch shouldn't be here, but unfortunately calling `ad.determineSize`
69 // breaks code with circular reference errors. Specifically, test23589.d fails
70 if (ad
.sizeok
!= Sizeok
.done
&& !sc
.func
.isSafeBypassingInference())
73 // needed to set v.overlapped and v.overlapUnsafe
74 if (ad
.sizeok
!= Sizeok
.done
)
75 ad
.determineSize(ad
.loc
);
77 const hasPointers
= v
.type
.hasPointers();
82 if (sc
.func
.isSafeBypassingInference() && sc
.setUnsafe(!printmsg
, e
.loc
,
83 "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad
, v
))
89 import dmd
.globals
: FeatureState
;
90 // @@@DEPRECATED_2.116@@@
91 // https://issues.dlang.org/show_bug.cgi?id=20655
92 // Inferring `@system` because of union access breaks code,
93 // so make it a deprecation safety violation as of 2.106
94 // To turn into an error, remove `isSafeBypassingInference` check in the
95 // above if statement and remove the else branch
96 sc
.setUnsafePreview(FeatureState
.default_
, !printmsg
, e
.loc
,
97 "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad
, v
);
102 if (v
.type
.hasInvariant())
106 if (sc
.setUnsafe(!printmsg
, e
.loc
,
107 "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
113 if (readonly ||
!e
.type
.isMutable())
116 if (hasPointers
&& v
.type
.toBasetype().ty
!= Tstruct
)
118 if ((!ad
.type
.alignment
.isDefault() && ad
.type
.alignment
.get() < target
.ptrsize ||
119 (v
.offset
& (target
.ptrsize
- 1))))
121 if (sc
.setUnsafe(!printmsg
, e
.loc
,
122 "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad
, v
))
129 if (sc
.setUnsafe(!printmsg
, e
.loc
,
130 "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
141 /**********************************************
142 * Determine if it is @safe to cast e from tfrom to tto.
144 * e = expression to be cast
146 * tto = type to cast e to
150 bool isSafeCast(Expression e
, Type tfrom
, Type tto
)
152 // Implicit conversions are always safe
153 if (tfrom
.implicitConvTo(tto
))
156 if (!tto
.hasPointers())
159 auto tfromb
= tfrom
.toBasetype();
160 auto ttob
= tto
.toBasetype();
162 if (ttob
.ty
== Tclass
&& tfromb
.ty
== Tclass
)
164 ClassDeclaration cdfrom
= tfromb
.isClassHandle();
165 ClassDeclaration cdto
= ttob
.isClassHandle();
168 if (!cdfrom
.isBaseOf(cdto
, &offset
) &&
169 !((cdfrom
.isInterfaceDeclaration() || cdto
.isInterfaceDeclaration())
170 && cdfrom
.classKind
== ClassKind
.d
&& cdto
.classKind
== ClassKind
.d
))
173 if (cdfrom
.isCPPinterface() || cdto
.isCPPinterface())
176 if (!MODimplicitConv(tfromb
.mod
, ttob
.mod
))
181 if (ttob
.ty
== Tarray
&& tfromb
.ty
== Tsarray
) // https://issues.dlang.org/show_bug.cgi?id=12502
182 tfromb
= tfromb
.nextOf().arrayOf();
184 if (ttob
.ty
== Tarray
&& tfromb
.ty
== Tarray ||
185 ttob
.ty
== Tpointer
&& tfromb
.ty
== Tpointer
)
187 Type ttobn
= ttob
.nextOf().toBasetype();
188 Type tfromn
= tfromb
.nextOf().toBasetype();
190 /* From void[] to anything mutable is unsafe because:
193 * int[] ai = cast(int[]) av;
197 if (tfromn
.ty
== Tvoid
&& ttobn
.isMutable())
199 if (ttob
.ty
== Tarray
&& e
.op
== EXP
.arrayLiteral
)
204 // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
205 if (ttobn
.ty
== Tstruct
&& !(cast(TypeStruct
)ttobn
).sym
.members ||
206 tfromn
.ty
== Tstruct
&& !(cast(TypeStruct
)tfromn
).sym
.members
)
209 const frompointers
= tfromn
.hasPointers();
210 const topointers
= ttobn
.hasPointers();
212 if (frompointers
&& !topointers
&& ttobn
.isMutable())
215 if (!frompointers
&& topointers
)
219 ttobn
.ty
!= Tfunction
&& tfromn
.ty
!= Tfunction
&&
220 (ttob
.ty
== Tarray || ttobn
.size() <= tfromn
.size()) &&
221 MODimplicitConv(tfromn
.mod
, ttobn
.mod
))
229 /*************************************************
230 * Check for unsafe use of `.ptr` or `.funcptr`
233 * e = expression for error messages
234 * id = `ptr` or `funcptr`
239 bool checkUnsafeDotExp(Scope
* sc
, Expression e
, Identifier id
, int flag
)
241 if (!(flag
& DotExpFlag
.noDeref
)) // this use is attempting a dereference
244 return sc
.setUnsafe(false, e
.loc
, "`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e
, e
);
246 return sc
.setUnsafe(false, e
.loc
, "`%s.%s` cannot be used in `@safe` code", e
, id
);