| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/safe.c |
| */ |
| |
| #include "mars.h" |
| #include "expression.h" |
| #include "scope.h" |
| #include "aggregate.h" |
| #include "target.h" |
| |
| bool MODimplicitConv(MOD modfrom, MOD modto); |
| |
| /************************************************************* |
| * Check for unsafe access in @safe code: |
| * 1. read overlapped pointers |
| * 2. write misaligned pointers |
| * 3. write overlapped storage classes |
| * Print error if unsafe. |
| * Params: |
| * sc = scope |
| * e = expression to check |
| * readonly = if access is read-only |
| * printmsg = print error message if true |
| * Returns: |
| * true if error |
| */ |
| |
| bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg) |
| { |
| if (e->op != TOKdotvar) |
| return false; |
| DotVarExp *dve = (DotVarExp *)e; |
| if (VarDeclaration *v = dve->var->isVarDeclaration()) |
| { |
| if (sc->intypeof || !sc->func || !sc->func->isSafeBypassingInference()) |
| return false; |
| |
| AggregateDeclaration *ad = v->toParent2()->isAggregateDeclaration(); |
| if (!ad) |
| return false; |
| |
| if (v->overlapped && v->type->hasPointers() && sc->func->setUnsafe()) |
| { |
| if (printmsg) |
| e->error("field %s.%s cannot access pointers in @safe code that overlap other fields", |
| ad->toChars(), v->toChars()); |
| return true; |
| } |
| |
| if (readonly || !e->type->isMutable()) |
| return false; |
| |
| if (v->type->hasPointers() && v->type->toBasetype()->ty != Tstruct) |
| { |
| if ((ad->type->alignment() < (unsigned)Target::ptrsize || |
| (v->offset & (Target::ptrsize - 1))) && |
| sc->func->setUnsafe()) |
| { |
| if (printmsg) |
| e->error("field %s.%s cannot modify misaligned pointers in @safe code", |
| ad->toChars(), v->toChars()); |
| return true; |
| } |
| } |
| |
| if (v->overlapUnsafe && sc->func->setUnsafe()) |
| { |
| if (printmsg) |
| e->error("field %s.%s cannot modify fields in @safe code that overlap fields with other storage classes", |
| ad->toChars(), v->toChars()); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| /********************************************** |
| * Determine if it is @safe to cast e from tfrom to tto. |
| * Params: |
| * e = expression to be cast |
| * tfrom = type of e |
| * tto = type to cast e to |
| * Returns: |
| * true if @safe |
| */ |
| bool isSafeCast(Expression *e, Type *tfrom, Type *tto) |
| { |
| // Implicit conversions are always safe |
| if (tfrom->implicitConvTo(tto)) |
| return true; |
| |
| if (!tto->hasPointers()) |
| return true; |
| |
| Type *ttob = tto->toBasetype(); |
| |
| if (ttob->ty == Tclass && tfrom->ty == Tclass) |
| { |
| ClassDeclaration *cdfrom = tfrom->isClassHandle(); |
| ClassDeclaration *cdto = ttob->isClassHandle(); |
| |
| int offset; |
| if (!cdfrom->isBaseOf(cdto, &offset)) |
| return false; |
| |
| if (cdfrom->isCPPinterface() || cdto->isCPPinterface()) |
| return false; |
| |
| if (!MODimplicitConv(tfrom->mod, ttob->mod)) |
| return false; |
| return true; |
| } |
| |
| if (ttob->ty == Tarray && tfrom->ty == Tsarray) // Bugzilla 12502 |
| tfrom = tfrom->nextOf()->arrayOf(); |
| |
| if ((ttob->ty == Tarray && tfrom->ty == Tarray) || |
| (ttob->ty == Tpointer && tfrom->ty == Tpointer)) |
| { |
| Type *ttobn = ttob->nextOf()->toBasetype(); |
| Type *tfromn = tfrom->nextOf()->toBasetype(); |
| |
| /* From void[] to anything mutable is unsafe because: |
| * int*[] api; |
| * void[] av = api; |
| * int[] ai = cast(int[]) av; |
| * ai[0] = 7; |
| * *api[0] crash! |
| */ |
| if (tfromn->ty == Tvoid && ttobn->isMutable()) |
| { |
| if (ttob->ty == Tarray && e->op == TOKarrayliteral) |
| return true; |
| return false; |
| } |
| |
| // If the struct is opaque we don't know about the struct members then the cast becomes unsafe |
| if ((ttobn->ty == Tstruct && !((TypeStruct *)ttobn)->sym->members) || |
| (tfromn->ty == Tstruct && !((TypeStruct *)tfromn)->sym->members)) |
| return false; |
| |
| const bool frompointers = tfromn->hasPointers(); |
| const bool topointers = ttobn->hasPointers(); |
| |
| if (frompointers && !topointers && ttobn->isMutable()) |
| return false; |
| |
| if (!frompointers && topointers) |
| return false; |
| |
| if (!topointers && |
| ttobn->ty != Tfunction && tfromn->ty != Tfunction && |
| (ttob->ty == Tarray || ttobn->size() <= tfromn->size()) && |
| MODimplicitConv(tfromn->mod, ttobn->mod)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |