blob: 7d83dd170ade10f1fdd4ab5c4cbedfd609fe73ac [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 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() < 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;
}