Delete some more files.

This commit is contained in:
Roman Divacky 2009-10-15 17:07:18 +00:00
parent a505eb93f4
commit a5f348eb91
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/clang/dist/; revision=198143
11 changed files with 0 additions and 6467 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,322 +0,0 @@
//===--- ResolveLocation.cpp - Source location resolver ---------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines the ResolveLocationInAST function, which resolves a
// source location into a <Decl *, Stmt *> pair.
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/Utils.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/Lexer.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/Compiler.h"
using namespace clang;
namespace {
/// \brief Base for the LocResolver classes. Mostly does source range checking.
class VISIBILITY_HIDDEN LocResolverBase {
protected:
ASTContext &Ctx;
SourceLocation Loc;
Decl *Dcl;
Stmt *Stm;
bool PassedLoc;
/// \brief Checks whether Loc is in the source range of 'D'.
///
/// If it is, updates Dcl. If Loc is passed the source range, it sets
/// PassedLoc, otherwise it does nothing.
void CheckRange(Decl *D);
/// \brief Checks whether Loc is in the source range of 'Node'.
///
/// If it is, updates Stm. If Loc is passed the source range, it sets
/// PassedLoc, otherwise it does nothing.
void CheckRange(Stmt *Node);
/// \brief Updates the end source range to cover the full length of the token
/// positioned at the end of the source range.
///
/// e.g.,
/// @code
/// int foo
/// ^ ^
/// @endcode
/// will be updated to
/// @code
/// int foo
/// ^ ^
/// @endcode
void FixRange(SourceRange &Range);
public:
LocResolverBase(ASTContext &ctx, SourceLocation loc)
: Ctx(ctx), Loc(loc), Dcl(0), Stm(0), PassedLoc(0) {}
/// \brief We found a AST node that corresponds to the source location.
bool FoundIt() const { return Dcl != 0 || Stm != 0; }
/// \brief We either found a AST node or we passed the source location while
/// searching.
bool Finished() const { return FoundIt() || PassedLoc; }
Decl *getDecl() const { return Dcl; }
Stmt *getStmt() const { return Stm; }
std::pair<Decl *, Stmt *> getResult() const {
return std::make_pair(getDecl(), getStmt());
}
/// \brief Debugging output.
void print(Decl *D);
/// \brief Debugging output.
void print(Stmt *Node);
};
/// \brief Searches a statement for the AST node that corresponds to a source
/// location.
class VISIBILITY_HIDDEN StmtLocResolver : public LocResolverBase,
public StmtVisitor<StmtLocResolver> {
public:
StmtLocResolver(ASTContext &ctx, SourceLocation loc)
: LocResolverBase(ctx, loc) {}
void VisitDeclStmt(DeclStmt *Node);
void VisitStmt(Stmt *Node);
};
/// \brief Searches a declaration for the AST node that corresponds to a source
/// location.
class VISIBILITY_HIDDEN DeclLocResolver : public LocResolverBase,
public DeclVisitor<DeclLocResolver> {
public:
DeclLocResolver(ASTContext &ctx, SourceLocation loc)
: LocResolverBase(ctx, loc) {}
void VisitDeclContext(DeclContext *DC);
void VisitTranslationUnitDecl(TranslationUnitDecl *TU);
void VisitVarDecl(VarDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
void VisitDecl(Decl *D);
};
} // anonymous namespace
void StmtLocResolver::VisitDeclStmt(DeclStmt *Node) {
CheckRange(Node);
if (!FoundIt())
return;
assert(Stm == Node && "Result not updated ?");
// Search all declarations of this DeclStmt. If we found the one corresponding
// to the source location, update this StmtLocResolver's result.
DeclLocResolver DLR(Ctx, Loc);
for (DeclStmt::decl_iterator
I = Node->decl_begin(), E = Node->decl_end(); I != E; ++I) {
DLR.Visit(*I);
if (DLR.Finished()) {
if (DLR.FoundIt())
llvm::tie(Dcl, Stm) = DLR.getResult();
return;
}
}
}
void StmtLocResolver::VisitStmt(Stmt *Node) {
CheckRange(Node);
if (!FoundIt())
return;
assert(Stm == Node && "Result not updated ?");
// Search the child statements.
StmtLocResolver SLR(Ctx, Loc);
for (Stmt::child_iterator
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
SLR.Visit(*I);
if (!SLR.Finished())
continue;
// We either found it or we passed the source location.
if (SLR.FoundIt()) {
// Only update Dcl if we found another more immediate 'parent' Decl for
// the statement.
if (SLR.getDecl())
Dcl = SLR.getDecl();
Stm = SLR.getStmt();
}
return;
}
}
void DeclLocResolver::VisitDeclContext(DeclContext *DC) {
DeclLocResolver DLR(Ctx, Loc);
for (DeclContext::decl_iterator
I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) {
DLR.Visit(*I);
if (DLR.Finished()) {
if (DLR.FoundIt())
llvm::tie(Dcl, Stm) = DLR.getResult();
return;
}
}
}
void DeclLocResolver::VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
VisitDeclContext(TU);
}
void DeclLocResolver::VisitFunctionDecl(FunctionDecl *D) {
CheckRange(D);
if (!FoundIt())
return;
assert(Dcl == D && "Result not updated ?");
// First, search through the parameters of the function.
DeclLocResolver ParmRes(Ctx, Loc);
for (FunctionDecl::param_iterator
I = D->param_begin(), E = D->param_end(); I != E; ++I) {
ParmRes.Visit(*I);
if (ParmRes.Finished()) {
if (ParmRes.FoundIt())
llvm::tie(Dcl, Stm) = ParmRes.getResult();
return;
}
}
// We didn't found the location in the parameters and we didn't get passed it.
// Second, search through the declarations that are part of the function.
// If we find he location there, we won't have to search through its body.
DeclLocResolver DLR(Ctx, Loc);
DLR.VisitDeclContext(D);
if (DLR.FoundIt()) {
llvm::tie(Dcl, Stm) = DLR.getResult();
return;
}
// We didn't find a declaration that corresponds to the source location.
// Finally, search through the body of the function.
if (D->isThisDeclarationADefinition()) {
StmtLocResolver SLR(Ctx, Loc);
SLR.Visit(D->getBody());
if (SLR.FoundIt()) {
llvm::tie(Dcl, Stm) = SLR.getResult();
// If we didn't find a more immediate 'parent' declaration for the
// statement, set the function as the parent.
if (Dcl == 0)
Dcl = D;
}
}
}
void DeclLocResolver::VisitVarDecl(VarDecl *D) {
CheckRange(D);
if (!FoundIt())
return;
assert(Dcl == D && "Result not updated ?");
// Check whether the location points to the init expression.
if (D->getInit()) {
StmtLocResolver SLR(Ctx, Loc);
SLR.Visit(D->getInit());
Stm = SLR.getStmt();
}
}
void DeclLocResolver::VisitDecl(Decl *D) {
CheckRange(D);
}
void LocResolverBase::CheckRange(Decl *D) {
SourceRange Range = D->getSourceRange();
if (!Range.isValid())
return;
FixRange(Range);
SourceManager &SourceMgr = Ctx.getSourceManager();
if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc))
return;
if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin()))
PassedLoc = true;
else
Dcl = D;
}
void LocResolverBase::CheckRange(Stmt *Node) {
SourceRange Range = Node->getSourceRange();
if (!Range.isValid())
return;
FixRange(Range);
SourceManager &SourceMgr = Ctx.getSourceManager();
if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc))
return;
if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin()))
PassedLoc = true;
else
Stm = Node;
}
void LocResolverBase::FixRange(SourceRange &Range) {
if (!Range.isValid())
return;
unsigned TokSize = Lexer::MeasureTokenLength(Range.getEnd(),
Ctx.getSourceManager(),
Ctx.getLangOptions());
Range.setEnd(Range.getEnd().getFileLocWithOffset(TokSize-1));
}
void LocResolverBase::print(Decl *D) {
llvm::raw_ostream &OS = llvm::outs();
OS << "#### DECL ####\n";
D->print(OS);
OS << " <";
D->getLocStart().print(OS, Ctx.getSourceManager());
OS << " > - <";
D->getLocEnd().print(OS, Ctx.getSourceManager());
OS << ">\n\n";
OS.flush();
}
void LocResolverBase::print(Stmt *Node) {
llvm::raw_ostream &OS = llvm::outs();
OS << "#### STMT ####\n";
Node->printPretty(OS, Ctx, 0, PrintingPolicy(Ctx.getLangOptions()));
OS << " <";
Node->getLocStart().print(OS, Ctx.getSourceManager());
OS << " > - <";
Node->getLocEnd().print(OS, Ctx.getSourceManager());
OS << ">\n\n";
OS.flush();
}
/// \brief Returns the AST node that a source location points to.
///
std::pair<Decl *, Stmt *>
clang::ResolveLocationInAST(ASTContext &Ctx, SourceLocation Loc) {
if (Loc.isInvalid())
return std::make_pair((Decl*)0, (Stmt*)0);
DeclLocResolver DLR(Ctx, Loc);
DLR.Visit(Ctx.getTranslationUnitDecl());
return DLR.getResult();
}

View File

@ -1,350 +0,0 @@
//===---- SemaInherit.cpp - C++ Inheritance ---------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides Sema routines for C++ inheritance semantics,
// including searching the inheritance hierarchy.
//
//===----------------------------------------------------------------------===//
#include "SemaInherit.h"
#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include <algorithm>
#include <memory>
#include <set>
#include <string>
using namespace clang;
/// \brief Computes the set of declarations referenced by these base
/// paths.
void BasePaths::ComputeDeclsFound() {
assert(NumDeclsFound == 0 && !DeclsFound &&
"Already computed the set of declarations");
std::set<NamedDecl *> Decls;
for (BasePaths::paths_iterator Path = begin(), PathEnd = end();
Path != PathEnd; ++Path)
Decls.insert(*Path->Decls.first);
NumDeclsFound = Decls.size();
DeclsFound = new NamedDecl * [NumDeclsFound];
std::copy(Decls.begin(), Decls.end(), DeclsFound);
}
BasePaths::decl_iterator BasePaths::found_decls_begin() {
if (NumDeclsFound == 0)
ComputeDeclsFound();
return DeclsFound;
}
BasePaths::decl_iterator BasePaths::found_decls_end() {
if (NumDeclsFound == 0)
ComputeDeclsFound();
return DeclsFound + NumDeclsFound;
}
/// isAmbiguous - Determines whether the set of paths provided is
/// ambiguous, i.e., there are two or more paths that refer to
/// different base class subobjects of the same type. BaseType must be
/// an unqualified, canonical class type.
bool BasePaths::isAmbiguous(QualType BaseType) {
assert(BaseType->isCanonical() && "Base type must be the canonical type");
assert(BaseType.getCVRQualifiers() == 0 && "Base type must be unqualified");
std::pair<bool, unsigned>& Subobjects = ClassSubobjects[BaseType];
return Subobjects.second + (Subobjects.first? 1 : 0) > 1;
}
/// clear - Clear out all prior path information.
void BasePaths::clear() {
Paths.clear();
ClassSubobjects.clear();
ScratchPath.clear();
DetectedVirtual = 0;
}
/// @brief Swaps the contents of this BasePaths structure with the
/// contents of Other.
void BasePaths::swap(BasePaths &Other) {
std::swap(Origin, Other.Origin);
Paths.swap(Other.Paths);
ClassSubobjects.swap(Other.ClassSubobjects);
std::swap(FindAmbiguities, Other.FindAmbiguities);
std::swap(RecordPaths, Other.RecordPaths);
std::swap(DetectVirtual, Other.DetectVirtual);
std::swap(DetectedVirtual, Other.DetectedVirtual);
}
/// IsDerivedFrom - Determine whether the type Derived is derived from
/// the type Base, ignoring qualifiers on Base and Derived. This
/// routine does not assess whether an actual conversion from a
/// Derived* to a Base* is legal, because it does not account for
/// ambiguous conversions or conversions to private/protected bases.
bool Sema::IsDerivedFrom(QualType Derived, QualType Base) {
BasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,
/*DetectVirtual=*/false);
return IsDerivedFrom(Derived, Base, Paths);
}
/// IsDerivedFrom - Determine whether the type Derived is derived from
/// the type Base, ignoring qualifiers on Base and Derived. This
/// routine does not assess whether an actual conversion from a
/// Derived* to a Base* is legal, because it does not account for
/// ambiguous conversions or conversions to private/protected
/// bases. This routine will use Paths to determine if there are
/// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record
/// information about all of the paths (if @c Paths.isRecordingPaths()).
bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) {
Derived = Context.getCanonicalType(Derived).getUnqualifiedType();
Base = Context.getCanonicalType(Base).getUnqualifiedType();
if (!Derived->isRecordType() || !Base->isRecordType())
return false;
if (Derived == Base)
return false;
Paths.setOrigin(Derived);
return LookupInBases(cast<CXXRecordDecl>(Derived->getAsRecordType()->getDecl()),
MemberLookupCriteria(Base), Paths);
}
/// LookupInBases - Look for something that meets the specified
/// Criteria within the base classes of Class (or any of its base
/// classes, transitively). This routine populates BasePaths with the
/// list of paths that one can take to find the entity that meets the
/// search criteria, and returns true if any such entity is found. The
/// various options passed to the BasePath constructor will affect the
/// behavior of this lookup, e.g., whether it finds ambiguities,
/// records paths, or attempts to detect the use of virtual base
/// classes.
bool Sema::LookupInBases(CXXRecordDecl *Class,
const MemberLookupCriteria& Criteria,
BasePaths &Paths) {
bool FoundPath = false;
for (CXXRecordDecl::base_class_const_iterator BaseSpec = Class->bases_begin(),
BaseSpecEnd = Class->bases_end();
BaseSpec != BaseSpecEnd; ++BaseSpec) {
// Find the record of the base class subobjects for this type.
QualType BaseType = Context.getCanonicalType(BaseSpec->getType());
BaseType = BaseType.getUnqualifiedType();
// If a base class of the class template depends on a template-parameter,
// the base class scope is not examined during unqualified name lookup.
// [temp.dep]p3.
if (BaseType->isDependentType())
continue;
// Determine whether we need to visit this base class at all,
// updating the count of subobjects appropriately.
std::pair<bool, unsigned>& Subobjects = Paths.ClassSubobjects[BaseType];
bool VisitBase = true;
bool SetVirtual = false;
if (BaseSpec->isVirtual()) {
VisitBase = !Subobjects.first;
Subobjects.first = true;
if (Paths.isDetectingVirtual() && Paths.DetectedVirtual == 0) {
// If this is the first virtual we find, remember it. If it turns out
// there is no base path here, we'll reset it later.
Paths.DetectedVirtual = BaseType->getAsRecordType();
SetVirtual = true;
}
} else
++Subobjects.second;
if (Paths.isRecordingPaths()) {
// Add this base specifier to the current path.
BasePathElement Element;
Element.Base = &*BaseSpec;
Element.Class = Class;
if (BaseSpec->isVirtual())
Element.SubobjectNumber = 0;
else
Element.SubobjectNumber = Subobjects.second;
Paths.ScratchPath.push_back(Element);
}
CXXRecordDecl *BaseRecord
= cast<CXXRecordDecl>(BaseSpec->getType()->getAsRecordType()->getDecl());
// Either look at the base class type or look into the base class
// type to see if we've found a member that meets the search
// criteria.
bool FoundPathToThisBase = false;
switch (Criteria.Kind) {
case MemberLookupCriteria::LK_Base:
FoundPathToThisBase
= (Context.getCanonicalType(BaseSpec->getType()) == Criteria.Base);
break;
case MemberLookupCriteria::LK_NamedMember:
Paths.ScratchPath.Decls = BaseRecord->lookup(Criteria.Name);
while (Paths.ScratchPath.Decls.first != Paths.ScratchPath.Decls.second) {
if (isAcceptableLookupResult(*Paths.ScratchPath.Decls.first,
Criteria.NameKind, Criteria.IDNS)) {
FoundPathToThisBase = true;
break;
}
++Paths.ScratchPath.Decls.first;
}
break;
case MemberLookupCriteria::LK_OverriddenMember:
Paths.ScratchPath.Decls =
BaseRecord->lookup(Criteria.Method->getDeclName());
while (Paths.ScratchPath.Decls.first != Paths.ScratchPath.Decls.second) {
if (CXXMethodDecl *MD =
dyn_cast<CXXMethodDecl>(*Paths.ScratchPath.Decls.first)) {
OverloadedFunctionDecl::function_iterator MatchedDecl;
if (MD->isVirtual() &&
!IsOverload(Criteria.Method, MD, MatchedDecl)) {
FoundPathToThisBase = true;
break;
}
}
++Paths.ScratchPath.Decls.first;
}
break;
}
if (FoundPathToThisBase) {
// We've found a path that terminates that this base.
FoundPath = true;
if (Paths.isRecordingPaths()) {
// We have a path. Make a copy of it before moving on.
Paths.Paths.push_back(Paths.ScratchPath);
} else if (!Paths.isFindingAmbiguities()) {
// We found a path and we don't care about ambiguities;
// return immediately.
return FoundPath;
}
}
// C++ [class.member.lookup]p2:
// A member name f in one sub-object B hides a member name f in
// a sub-object A if A is a base class sub-object of B. Any
// declarations that are so hidden are eliminated from
// consideration.
else if (VisitBase && LookupInBases(BaseRecord, Criteria, Paths)) {
// There is a path to a base class that meets the criteria. If we're not
// collecting paths or finding ambiguities, we're done.
FoundPath = true;
if (!Paths.isFindingAmbiguities())
return FoundPath;
}
// Pop this base specifier off the current path (if we're
// collecting paths).
if (Paths.isRecordingPaths())
Paths.ScratchPath.pop_back();
// If we set a virtual earlier, and this isn't a path, forget it again.
if (SetVirtual && !FoundPath) {
Paths.DetectedVirtual = 0;
}
}
return FoundPath;
}
/// CheckDerivedToBaseConversion - Check whether the Derived-to-Base
/// conversion (where Derived and Base are class types) is
/// well-formed, meaning that the conversion is unambiguous (and
/// that all of the base classes are accessible). Returns true
/// and emits a diagnostic if the code is ill-formed, returns false
/// otherwise. Loc is the location where this routine should point to
/// if there is an error, and Range is the source range to highlight
/// if there is an error.
bool
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
unsigned AmbigiousBaseConvID,
SourceLocation Loc, SourceRange Range,
DeclarationName Name) {
// First, determine whether the path from Derived to Base is
// ambiguous. This is slightly more expensive than checking whether
// the Derived to Base conversion exists, because here we need to
// explore multiple paths to determine if there is an ambiguity.
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
/*DetectVirtual=*/false);
bool DerivationOkay = IsDerivedFrom(Derived, Base, Paths);
assert(DerivationOkay &&
"Can only be used with a derived-to-base conversion");
(void)DerivationOkay;
if (!Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) {
// Check that the base class can be accessed.
return CheckBaseClassAccess(Derived, Base, InaccessibleBaseID, Paths, Loc,
Name);
}
// We know that the derived-to-base conversion is ambiguous, and
// we're going to produce a diagnostic. Perform the derived-to-base
// search just one more time to compute all of the possible paths so
// that we can print them out. This is more expensive than any of
// the previous derived-to-base checks we've done, but at this point
// performance isn't as much of an issue.
Paths.clear();
Paths.setRecordingPaths(true);
bool StillOkay = IsDerivedFrom(Derived, Base, Paths);
assert(StillOkay && "Can only be used with a derived-to-base conversion");
(void)StillOkay;
// Build up a textual representation of the ambiguous paths, e.g.,
// D -> B -> A, that will be used to illustrate the ambiguous
// conversions in the diagnostic. We only print one of the paths
// to each base class subobject.
std::string PathDisplayStr = getAmbiguousPathsDisplayString(Paths);
Diag(Loc, AmbigiousBaseConvID)
<< Derived << Base << PathDisplayStr << Range << Name;
return true;
}
bool
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
SourceLocation Loc, SourceRange Range) {
return CheckDerivedToBaseConversion(Derived, Base,
diag::err_conv_to_inaccessible_base,
diag::err_ambiguous_derived_to_base_conv,
Loc, Range, DeclarationName());
}
/// @brief Builds a string representing ambiguous paths from a
/// specific derived class to different subobjects of the same base
/// class.
///
/// This function builds a string that can be used in error messages
/// to show the different paths that one can take through the
/// inheritance hierarchy to go from the derived class to different
/// subobjects of a base class. The result looks something like this:
/// @code
/// struct D -> struct B -> struct A
/// struct D -> struct C -> struct A
/// @endcode
std::string Sema::getAmbiguousPathsDisplayString(BasePaths &Paths) {
std::string PathDisplayStr;
std::set<unsigned> DisplayedPaths;
for (BasePaths::paths_iterator Path = Paths.begin();
Path != Paths.end(); ++Path) {
if (DisplayedPaths.insert(Path->back().SubobjectNumber).second) {
// We haven't displayed a path to this particular base
// class subobject yet.
PathDisplayStr += "\n ";
PathDisplayStr += Paths.getOrigin().getAsString();
for (BasePath::const_iterator Element = Path->begin();
Element != Path->end(); ++Element)
PathDisplayStr += " -> " + Element->Base->getType().getAsString();
}
}
return PathDisplayStr;
}

View File

@ -1,248 +0,0 @@
//===------ SemaInherit.h - C++ Inheritance ---------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides Sema data structures that help analyse C++
// inheritance semantics, including searching the inheritance
// hierarchy.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SEMA_INHERIT_H
#define LLVM_CLANG_SEMA_INHERIT_H
#include "Sema.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include "llvm/ADT/SmallVector.h"
#include <list>
#include <map>
namespace clang {
class CXXBaseSpecifier;
/// BasePathElement - An element in a path from a derived class to a
/// base class. Each step in the path references the link from a
/// derived class to one of its direct base classes, along with a
/// base "number" that identifies which base subobject of the
/// original derived class we are referencing.
struct BasePathElement {
/// Base - The base specifier that states the link from a derived
/// class to a base class, which will be followed by this base
/// path element.
const CXXBaseSpecifier *Base;
/// Class - The record decl of the class that the base is a base of.
const CXXRecordDecl *Class;
/// SubobjectNumber - Identifies which base class subobject (of type
/// @c Base->getType()) this base path element refers to. This
/// value is only valid if @c !Base->isVirtual(), because there
/// is no base numbering for the zero or one virtual bases of a
/// given type.
int SubobjectNumber;
};
/// BasePath - Represents a path from a specific derived class
/// (which is not represented as part of the path) to a particular
/// (direct or indirect) base class subobject that contains some
/// number of declarations with the same name. Individual elements
/// in the path are described by the BasePathElement structure,
/// which captures both the link from a derived class to one of its
/// direct bases and identification describing which base class
/// subobject is being used.
struct BasePath : public llvm::SmallVector<BasePathElement, 4> {
/// Decls - The set of declarations found inside this base class
/// subobject.
DeclContext::lookup_result Decls;
};
/// BasePaths - Represents the set of paths from a derived class to
/// one of its (direct or indirect) bases. For example, given the
/// following class hierachy:
///
/// @code
/// class A { };
/// class B : public A { };
/// class C : public A { };
/// class D : public B, public C{ };
/// @endcode
///
/// There are two potential BasePaths to represent paths from D to a
/// base subobject of type A. One path is (D,0) -> (B,0) -> (A,0)
/// and another is (D,0)->(C,0)->(A,1). These two paths actually
/// refer to two different base class subobjects of the same type,
/// so the BasePaths object refers to an ambiguous path. On the
/// other hand, consider the following class hierarchy:
///
/// @code
/// class A { };
/// class B : public virtual A { };
/// class C : public virtual A { };
/// class D : public B, public C{ };
/// @endcode
///
/// Here, there are two potential BasePaths again, (D, 0) -> (B, 0)
/// -> (A,v) and (D, 0) -> (C, 0) -> (A, v), but since both of them
/// refer to the same base class subobject of type A (the virtual
/// one), there is no ambiguity.
class BasePaths {
/// Origin - The type from which this search originated.
QualType Origin;
/// Paths - The actual set of paths that can be taken from the
/// derived class to the same base class.
std::list<BasePath> Paths;
/// ClassSubobjects - Records the class subobjects for each class
/// type that we've seen. The first element in the pair says
/// whether we found a path to a virtual base for that class type,
/// while the element contains the number of non-virtual base
/// class subobjects for that class type. The key of the map is
/// the cv-unqualified canonical type of the base class subobject.
std::map<QualType, std::pair<bool, unsigned>, QualTypeOrdering>
ClassSubobjects;
/// FindAmbiguities - Whether Sema::IsDerivedFrom should try find
/// ambiguous paths while it is looking for a path from a derived
/// type to a base type.
bool FindAmbiguities;
/// RecordPaths - Whether Sema::IsDerivedFrom should record paths
/// while it is determining whether there are paths from a derived
/// type to a base type.
bool RecordPaths;
/// DetectVirtual - Whether Sema::IsDerivedFrom should abort the search
/// if it finds a path that goes across a virtual base. The virtual class
/// is also recorded.
bool DetectVirtual;
/// ScratchPath - A BasePath that is used by Sema::IsDerivedFrom
/// to help build the set of paths.
BasePath ScratchPath;
/// DetectedVirtual - The base class that is virtual.
const RecordType *DetectedVirtual;
/// \brief Array of the declarations that have been found. This
/// array is constructed only if needed, e.g., to iterate over the
/// results within LookupResult.
NamedDecl **DeclsFound;
unsigned NumDeclsFound;
friend class Sema;
void ComputeDeclsFound();
public:
typedef std::list<BasePath>::const_iterator paths_iterator;
typedef NamedDecl **decl_iterator;
/// BasePaths - Construct a new BasePaths structure to record the
/// paths for a derived-to-base search.
explicit BasePaths(bool FindAmbiguities = true,
bool RecordPaths = true,
bool DetectVirtual = true)
: FindAmbiguities(FindAmbiguities), RecordPaths(RecordPaths),
DetectVirtual(DetectVirtual), DetectedVirtual(0), DeclsFound(0),
NumDeclsFound(0)
{}
~BasePaths() { delete [] DeclsFound; }
paths_iterator begin() const { return Paths.begin(); }
paths_iterator end() const { return Paths.end(); }
BasePath& front() { return Paths.front(); }
const BasePath& front() const { return Paths.front(); }
decl_iterator found_decls_begin();
decl_iterator found_decls_end();
bool isAmbiguous(QualType BaseType);
/// isFindingAmbiguities - Whether we are finding multiple paths
/// to detect ambiguities.
bool isFindingAmbiguities() const { return FindAmbiguities; }
/// isRecordingPaths - Whether we are recording paths.
bool isRecordingPaths() const { return RecordPaths; }
/// setRecordingPaths - Specify whether we should be recording
/// paths or not.
void setRecordingPaths(bool RP) { RecordPaths = RP; }
/// isDetectingVirtual - Whether we are detecting virtual bases.
bool isDetectingVirtual() const { return DetectVirtual; }
/// getDetectedVirtual - The virtual base discovered on the path.
const RecordType* getDetectedVirtual() const {
return DetectedVirtual;
}
/// @brief Retrieve the type from which this base-paths search
/// began
QualType getOrigin() const { return Origin; }
void setOrigin(QualType Type) { Origin = Type; }
void clear();
void swap(BasePaths &Other);
};
/// MemberLookupCriteria - Criteria for performing lookup of a
/// member of a C++ class. Objects of this type are used to direct
/// Sema::LookupCXXClassMember.
struct MemberLookupCriteria {
/// LookupKind - the kind of lookup we're doing.
enum LookupKind {
LK_Base,
LK_NamedMember,
LK_OverriddenMember
};
/// MemberLookupCriteria - Constructs member lookup criteria to
/// search for a base class of type Base.
explicit MemberLookupCriteria(QualType Base)
: Kind(LK_Base), Base(Base) { }
/// MemberLookupCriteria - Constructs member lookup criteria to
/// search for a class member with the given Name.
explicit MemberLookupCriteria(DeclarationName Name,
Sema::LookupNameKind NameKind,
unsigned IDNS)
: Kind(LK_NamedMember), Name(Name), NameKind(NameKind), IDNS(IDNS) { }
explicit MemberLookupCriteria(CXXMethodDecl *MD)
: Kind(LK_OverriddenMember), Method(MD) { }
/// Kind - The kind of lookup we're doing.
/// LK_Base if we are looking for a base class (whose
/// type is Base). LK_NamedMember if we are looking for a named member of
/// the class (with the name Name).
LookupKind Kind;
/// Base - The type of the base class we're searching for, if
/// LookupBase is true.
QualType Base;
/// Name - The name of the member we're searching for, if
/// LookupBase is false.
DeclarationName Name;
Sema::LookupNameKind NameKind;
unsigned IDNS;
CXXMethodDecl *Method;
};
}
#endif

View File

@ -1,932 +0,0 @@
//===--- SemaNamedCast.cpp - Semantic Analysis for Named Casts ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements semantic analysis for C++ named casts.
//
//===----------------------------------------------------------------------===//
#include "Sema.h"
#include "SemaInherit.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ASTContext.h"
#include "llvm/ADT/SmallVector.h"
#include <set>
using namespace clang;
enum TryStaticCastResult {
TSC_NotApplicable, ///< The cast method is not applicable.
TSC_Success, ///< The cast method is appropriate and successful.
TSC_Failed ///< The cast method is appropriate, but failed. A
///< diagnostic has been emitted.
};
static void CheckConstCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange,
const SourceRange &DestRange);
static void CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange,
const SourceRange &DestRange);
static void CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange);
static void CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange,
const SourceRange &DestRange);
static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType);
static TryStaticCastResult TryLValueToRValueCast(
Sema &Self, Expr *SrcExpr, QualType DestType, const SourceRange &OpRange);
static TryStaticCastResult TryStaticReferenceDowncast(
Sema &Self, Expr *SrcExpr, QualType DestType, const SourceRange &OpRange);
static TryStaticCastResult TryStaticPointerDowncast(
Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange);
static TryStaticCastResult TryStaticMemberPointerUpcast(
Sema &Self, QualType SrcType, QualType DestType, const SourceRange &OpRange);
static TryStaticCastResult TryStaticDowncast(Sema &Self, QualType SrcType,
QualType DestType,
const SourceRange &OpRange,
QualType OrigSrcType,
QualType OrigDestType);
static TryStaticCastResult TryStaticImplicitCast(Sema &Self, Expr *SrcExpr,
QualType DestType,
const SourceRange &OpRange);
/// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
Action::OwningExprResult
Sema::ActOnCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
SourceLocation LAngleBracketLoc, TypeTy *Ty,
SourceLocation RAngleBracketLoc,
SourceLocation LParenLoc, ExprArg E,
SourceLocation RParenLoc) {
Expr *Ex = E.takeAs<Expr>();
QualType DestType = QualType::getFromOpaquePtr(Ty);
SourceRange OpRange(OpLoc, RParenLoc);
SourceRange DestRange(LAngleBracketLoc, RAngleBracketLoc);
// If the type is dependent, we won't do the semantic analysis now.
// FIXME: should we check this in a more fine-grained manner?
bool TypeDependent = DestType->isDependentType() || Ex->isTypeDependent();
switch (Kind) {
default: assert(0 && "Unknown C++ cast!");
case tok::kw_const_cast:
if (!TypeDependent)
CheckConstCast(*this, Ex, DestType, OpRange, DestRange);
return Owned(new (Context) CXXConstCastExpr(DestType.getNonReferenceType(),
Ex, DestType, OpLoc));
case tok::kw_dynamic_cast:
if (!TypeDependent)
CheckDynamicCast(*this, Ex, DestType, OpRange, DestRange);
return Owned(new (Context)CXXDynamicCastExpr(DestType.getNonReferenceType(),
Ex, DestType, OpLoc));
case tok::kw_reinterpret_cast:
if (!TypeDependent)
CheckReinterpretCast(*this, Ex, DestType, OpRange, DestRange);
return Owned(new (Context) CXXReinterpretCastExpr(
DestType.getNonReferenceType(),
Ex, DestType, OpLoc));
case tok::kw_static_cast:
if (!TypeDependent)
CheckStaticCast(*this, Ex, DestType, OpRange);
return Owned(new (Context) CXXStaticCastExpr(DestType.getNonReferenceType(),
Ex, DestType, OpLoc));
}
return ExprError();
}
/// CheckConstCast - Check that a const_cast\<DestType\>(SrcExpr) is valid.
/// Refer to C++ 5.2.11 for details. const_cast is typically used in code
/// like this:
/// const char *str = "literal";
/// legacy_function(const_cast\<char*\>(str));
void
CheckConstCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange, const SourceRange &DestRange)
{
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
DestType = Self.Context.getCanonicalType(DestType);
QualType SrcType = SrcExpr->getType();
if (const LValueReferenceType *DestTypeTmp =
DestType->getAsLValueReferenceType()) {
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
// Cannot cast non-lvalue to lvalue reference type.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
<< "const_cast" << OrigDestType << SrcExpr->getSourceRange();
return;
}
// C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2
// [...] if a pointer to T1 can be [cast] to the type pointer to T2.
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
SrcType = Self.Context.getPointerType(SrcType);
} else {
// C++ 5.2.11p1: Otherwise, the result is an rvalue and the
// lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
// conversions are performed on the expression.
Self.DefaultFunctionArrayConversion(SrcExpr);
SrcType = SrcExpr->getType();
}
// C++ 5.2.11p5: For a const_cast involving pointers to data members [...]
// the rules for const_cast are the same as those used for pointers.
if (!DestType->isPointerType() && !DestType->isMemberPointerType()) {
// Cannot cast to non-pointer, non-reference type. Note that, if DestType
// was a reference type, we converted it to a pointer above.
// The status of rvalue references isn't entirely clear, but it looks like
// conversion to them is simply invalid.
// C++ 5.2.11p3: For two pointer types [...]
Self.Diag(OpRange.getBegin(), diag::err_bad_const_cast_dest)
<< OrigDestType << DestRange;
return;
}
if (DestType->isFunctionPointerType() ||
DestType->isMemberFunctionPointerType()) {
// Cannot cast direct function pointers.
// C++ 5.2.11p2: [...] where T is any object type or the void type [...]
// T is the ultimate pointee of source and target type.
Self.Diag(OpRange.getBegin(), diag::err_bad_const_cast_dest)
<< OrigDestType << DestRange;
return;
}
SrcType = Self.Context.getCanonicalType(SrcType);
// Unwrap the pointers. Ignore qualifiers. Terminate early if the types are
// completely equal.
// FIXME: const_cast should probably not be able to convert between pointers
// to different address spaces.
// C++ 5.2.11p3 describes the core semantics of const_cast. All cv specifiers
// in multi-level pointers may change, but the level count must be the same,
// as must be the final pointee type.
while (SrcType != DestType &&
Self.UnwrapSimilarPointerTypes(SrcType, DestType)) {
SrcType = SrcType.getUnqualifiedType();
DestType = DestType.getUnqualifiedType();
}
// Doug Gregor said to disallow this until users complain.
#if 0
// If we end up with constant arrays of equal size, unwrap those too. A cast
// from const int [N] to int (&)[N] is invalid by my reading of the
// standard, but g++ accepts it even with -ansi -pedantic.
// No more than one level, though, so don't embed this in the unwrap loop
// above.
const ConstantArrayType *SrcTypeArr, *DestTypeArr;
if ((SrcTypeArr = Self.Context.getAsConstantArrayType(SrcType)) &&
(DestTypeArr = Self.Context.getAsConstantArrayType(DestType)))
{
if (SrcTypeArr->getSize() != DestTypeArr->getSize()) {
// Different array sizes.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "const_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
SrcType = SrcTypeArr->getElementType().getUnqualifiedType();
DestType = DestTypeArr->getElementType().getUnqualifiedType();
}
#endif
// Since we're dealing in canonical types, the remainder must be the same.
if (SrcType != DestType) {
// Cast between unrelated types.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "const_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
}
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
/// valid.
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
/// like this:
/// char *bytes = reinterpret_cast\<char*\>(int_ptr);
void
CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange, const SourceRange &DestRange)
{
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
DestType = Self.Context.getCanonicalType(DestType);
QualType SrcType = SrcExpr->getType();
if (const LValueReferenceType *DestTypeTmp =
DestType->getAsLValueReferenceType()) {
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
// Cannot cast non-lvalue to reference type.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
<< "reinterpret_cast" << OrigDestType << SrcExpr->getSourceRange();
return;
}
// C++ 5.2.10p10: [...] a reference cast reinterpret_cast<T&>(x) has the
// same effect as the conversion *reinterpret_cast<T*>(&x) with the
// built-in & and * operators.
// This code does this transformation for the checked types.
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
SrcType = Self.Context.getPointerType(SrcType);
} else if (const RValueReferenceType *DestTypeTmp =
DestType->getAsRValueReferenceType()) {
// Both the reference conversion and the rvalue rules apply.
Self.DefaultFunctionArrayConversion(SrcExpr);
SrcType = SrcExpr->getType();
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
SrcType = Self.Context.getPointerType(SrcType);
} else {
// C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and
// function-to-pointer standard conversions are performed on the
// expression v.
Self.DefaultFunctionArrayConversion(SrcExpr);
SrcType = SrcExpr->getType();
}
// Canonicalize source for comparison.
SrcType = Self.Context.getCanonicalType(SrcType);
const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType(),
*SrcMemPtr = SrcType->getAsMemberPointerType();
if (DestMemPtr && SrcMemPtr) {
// C++ 5.2.10p9: An rvalue of type "pointer to member of X of type T1"
// can be explicitly converted to an rvalue of type "pointer to member
// of Y of type T2" if T1 and T2 are both function types or both object
// types.
if (DestMemPtr->getPointeeType()->isFunctionType() !=
SrcMemPtr->getPointeeType()->isFunctionType()) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
// C++ 5.2.10p2: The reinterpret_cast operator shall not cast away
// constness.
if (CastsAwayConstness(Self, SrcType, DestType)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
// A valid member pointer cast.
return;
}
// See below for the enumeral issue.
if (SrcType->isNullPtrType() && DestType->isIntegralType() &&
!DestType->isEnumeralType()) {
// C++0x 5.2.10p4: A pointer can be explicitly converted to any integral
// type large enough to hold it. A value of std::nullptr_t can be
// converted to an integral type; the conversion has the same meaning
// and validity as a conversion of (void*)0 to the integral type.
if (Self.Context.getTypeSize(SrcType) >
Self.Context.getTypeSize(DestType)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
<< OrigDestType << DestRange;
}
return;
}
bool destIsPtr = DestType->isPointerType();
bool srcIsPtr = SrcType->isPointerType();
if (!destIsPtr && !srcIsPtr) {
// Except for std::nullptr_t->integer and lvalue->reference, which are
// handled above, at least one of the two arguments must be a pointer.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
if (SrcType == DestType) {
// C++ 5.2.10p2 has a note that mentions that, subject to all other
// restrictions, a cast to the same type is allowed. The intent is not
// entirely clear here, since all other paragraphs explicitly forbid casts
// to the same type. However, the behavior of compilers is pretty consistent
// on this point: allow same-type conversion if the involved types are
// pointers, disallow otherwise.
return;
}
// Note: Clang treats enumeration types as integral types. If this is ever
// changed for C++, the additional check here will be redundant.
if (DestType->isIntegralType() && !DestType->isEnumeralType()) {
assert(srcIsPtr && "One type must be a pointer");
// C++ 5.2.10p4: A pointer can be explicitly converted to any integral
// type large enough to hold it.
if (Self.Context.getTypeSize(SrcType) >
Self.Context.getTypeSize(DestType)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
<< OrigDestType << DestRange;
}
return;
}
if (SrcType->isIntegralType() || SrcType->isEnumeralType()) {
assert(destIsPtr && "One type must be a pointer");
// C++ 5.2.10p5: A value of integral or enumeration type can be explicitly
// converted to a pointer.
return;
}
if (!destIsPtr || !srcIsPtr) {
// With the valid non-pointer conversions out of the way, we can be even
// more stringent.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
// C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
if (CastsAwayConstness(Self, SrcType, DestType)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
// Not casting away constness, so the only remaining check is for compatible
// pointer categories.
if (SrcType->isFunctionPointerType()) {
if (DestType->isFunctionPointerType()) {
// C++ 5.2.10p6: A pointer to a function can be explicitly converted to
// a pointer to a function of a different type.
return;
}
// C++0x 5.2.10p8: Converting a pointer to a function into a pointer to
// an object type or vice versa is conditionally-supported.
// Compilers support it in C++03 too, though, because it's necessary for
// casting the return value of dlsym() and GetProcAddress().
// FIXME: Conditionally-supported behavior should be configurable in the
// TargetInfo or similar.
if (!Self.getLangOptions().CPlusPlus0x) {
Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
<< OpRange;
}
return;
}
if (DestType->isFunctionPointerType()) {
// See above.
if (!Self.getLangOptions().CPlusPlus0x) {
Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
<< OpRange;
}
return;
}
// C++ 5.2.10p7: A pointer to an object can be explicitly converted to
// a pointer to an object of different type.
// Void pointers are not specified, but supported by every compiler out there.
// So we finish by allowing everything that remains - it's got to be two
// object pointers.
}
/// CastsAwayConstness - Check if the pointer conversion from SrcType to
/// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by
/// the cast checkers. Both arguments must denote pointer (possibly to member)
/// types.
bool
CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType)
{
// Casting away constness is defined in C++ 5.2.11p8 with reference to
// C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since
// the rules are non-trivial. So first we construct Tcv *...cv* as described
// in C++ 5.2.11p8.
assert((SrcType->isPointerType() || SrcType->isMemberPointerType()) &&
"Source type is not pointer or pointer to member.");
assert((DestType->isPointerType() || DestType->isMemberPointerType()) &&
"Destination type is not pointer or pointer to member.");
QualType UnwrappedSrcType = SrcType, UnwrappedDestType = DestType;
llvm::SmallVector<unsigned, 8> cv1, cv2;
// Find the qualifications.
while (Self.UnwrapSimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) {
cv1.push_back(UnwrappedSrcType.getCVRQualifiers());
cv2.push_back(UnwrappedDestType.getCVRQualifiers());
}
assert(cv1.size() > 0 && "Must have at least one pointer level.");
// Construct void pointers with those qualifiers (in reverse order of
// unwrapping, of course).
QualType SrcConstruct = Self.Context.VoidTy;
QualType DestConstruct = Self.Context.VoidTy;
for (llvm::SmallVector<unsigned, 8>::reverse_iterator i1 = cv1.rbegin(),
i2 = cv2.rbegin();
i1 != cv1.rend(); ++i1, ++i2)
{
SrcConstruct = Self.Context.getPointerType(
SrcConstruct.getQualifiedType(*i1));
DestConstruct = Self.Context.getPointerType(
DestConstruct.getQualifiedType(*i2));
}
// Test if they're compatible.
return SrcConstruct != DestConstruct &&
!Self.IsQualificationConversion(SrcConstruct, DestConstruct);
}
/// CheckStaticCast - Check that a static_cast\<DestType\>(SrcExpr) is valid.
/// Refer to C++ 5.2.9 for details. Static casts are mostly used for making
/// implicit conversions explicit and getting rid of data loss warnings.
void
CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange)
{
// The order the tests is not entirely arbitrary. There is one conversion
// that can be handled in two different ways. Given:
// struct A {};
// struct B : public A {
// B(); B(const A&);
// };
// const A &a = B();
// the cast static_cast<const B&>(a) could be seen as either a static
// reference downcast, or an explicit invocation of the user-defined
// conversion using B's conversion constructor.
// DR 427 specifies that the downcast is to be applied here.
// FIXME: With N2812, casts to rvalue refs will change.
// C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void".
if (DestType->isVoidType()) {
return;
}
// C++ 5.2.9p5, reference downcast.
// See the function for details.
// DR 427 specifies that this is to be applied before paragraph 2.
if (TryStaticReferenceDowncast(Self, SrcExpr, DestType, OpRange)
> TSC_NotApplicable) {
return;
}
// N2844 5.2.9p3: An lvalue of type "cv1 T1" can be cast to type "rvalue
// reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
if (TryLValueToRValueCast(Self, SrcExpr, DestType, OpRange) >
TSC_NotApplicable) {
return;
}
// C++ 5.2.9p2: An expression e can be explicitly converted to a type T
// [...] if the declaration "T t(e);" is well-formed, [...].
if (TryStaticImplicitCast(Self, SrcExpr, DestType, OpRange) >
TSC_NotApplicable) {
return;
}
// C++ 5.2.9p6: May apply the reverse of any standard conversion, except
// lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean
// conversions, subject to further restrictions.
// Also, C++ 5.2.9p1 forbids casting away constness, which makes reversal
// of qualification conversions impossible.
// The lvalue-to-rvalue, array-to-pointer and function-to-pointer conversions
// are applied to the expression.
QualType OrigSrcType = SrcExpr->getType();
Self.DefaultFunctionArrayConversion(SrcExpr);
QualType SrcType = Self.Context.getCanonicalType(SrcExpr->getType());
// Reverse integral promotion/conversion. All such conversions are themselves
// again integral promotions or conversions and are thus already handled by
// p2 (TryDirectInitialization above).
// (Note: any data loss warnings should be suppressed.)
// The exception is the reverse of enum->integer, i.e. integer->enum (and
// enum->enum). See also C++ 5.2.9p7.
// The same goes for reverse floating point promotion/conversion and
// floating-integral conversions. Again, only floating->enum is relevant.
if (DestType->isEnumeralType()) {
if (SrcType->isComplexType() || SrcType->isVectorType()) {
// Fall through - these cannot be converted.
} else if (SrcType->isArithmeticType() || SrcType->isEnumeralType()) {
return;
}
}
// Reverse pointer upcast. C++ 4.10p3 specifies pointer upcast.
// C++ 5.2.9p8 additionally disallows a cast path through virtual inheritance.
if (TryStaticPointerDowncast(Self, SrcType, DestType, OpRange)
> TSC_NotApplicable) {
return;
}
// Reverse member pointer conversion. C++ 4.11 specifies member pointer
// conversion. C++ 5.2.9p9 has additional information.
// DR54's access restrictions apply here also.
if (TryStaticMemberPointerUpcast(Self, SrcType, DestType, OpRange)
> TSC_NotApplicable) {
return;
}
// Reverse pointer conversion to void*. C++ 4.10.p2 specifies conversion to
// void*. C++ 5.2.9p10 specifies additional restrictions, which really is
// just the usual constness stuff.
if (const PointerType *SrcPointer = SrcType->getAsPointerType()) {
QualType SrcPointee = SrcPointer->getPointeeType();
if (SrcPointee->isVoidType()) {
if (const PointerType *DestPointer = DestType->getAsPointerType()) {
QualType DestPointee = DestPointer->getPointeeType();
if (DestPointee->isIncompleteOrObjectType()) {
// This is definitely the intended conversion, but it might fail due
// to a const violation.
if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
<< "static_cast" << DestType << OrigSrcType << OpRange;
}
return;
}
}
}
}
// We tried everything. Everything! Nothing works! :-(
// FIXME: Error reporting could be a lot better. Should store the reason why
// every substep failed and, at the end, select the most specific and report
// that.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "static_cast" << DestType << OrigSrcType
<< OpRange;
}
/// Tests whether a conversion according to N2844 is valid.
TryStaticCastResult
TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType,
const SourceRange &OpRange)
{
// N2844 5.2.9p3: An lvalue of type "cv1 T1" can be cast to type "rvalue
// reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
const RValueReferenceType *R = DestType->getAsRValueReferenceType();
if (!R)
return TSC_NotApplicable;
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid)
return TSC_NotApplicable;
// Because we try the reference downcast before this function, from now on
// this is the only cast possibility, so we issue an error if we fail now.
bool DerivedToBase;
if (Self.CompareReferenceRelationship(SrcExpr->getType(), R->getPointeeType(),
DerivedToBase) <
Sema::Ref_Compatible_With_Added_Qualification) {
Self.Diag(OpRange.getBegin(), diag::err_bad_lvalue_to_rvalue_cast)
<< SrcExpr->getType() << R->getPointeeType() << OpRange;
return TSC_Failed;
}
// FIXME: Similar to CheckReferenceInit, we actually need more AST annotation
// than nothing.
return TSC_Success;
}
/// Tests whether a conversion according to C++ 5.2.9p5 is valid.
TryStaticCastResult
TryStaticReferenceDowncast(Sema &Self, Expr *SrcExpr, QualType DestType,
const SourceRange &OpRange)
{
// C++ 5.2.9p5: An lvalue of type "cv1 B", where B is a class type, can be
// cast to type "reference to cv2 D", where D is a class derived from B,
// if a valid standard conversion from "pointer to D" to "pointer to B"
// exists, cv2 >= cv1, and B is not a virtual base class of D.
// In addition, DR54 clarifies that the base must be accessible in the
// current context. Although the wording of DR54 only applies to the pointer
// variant of this rule, the intent is clearly for it to apply to the this
// conversion as well.
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
return TSC_NotApplicable;
}
const ReferenceType *DestReference = DestType->getAsReferenceType();
if (!DestReference) {
return TSC_NotApplicable;
}
QualType DestPointee = DestReference->getPointeeType();
return TryStaticDowncast(Self, SrcExpr->getType(), DestPointee, OpRange,
SrcExpr->getType(), DestType);
}
/// Tests whether a conversion according to C++ 5.2.9p8 is valid.
TryStaticCastResult
TryStaticPointerDowncast(Sema &Self, QualType SrcType, QualType DestType,
const SourceRange &OpRange)
{
// C++ 5.2.9p8: An rvalue of type "pointer to cv1 B", where B is a class
// type, can be converted to an rvalue of type "pointer to cv2 D", where D
// is a class derived from B, if a valid standard conversion from "pointer
// to D" to "pointer to B" exists, cv2 >= cv1, and B is not a virtual base
// class of D.
// In addition, DR54 clarifies that the base must be accessible in the
// current context.
const PointerType *SrcPointer = SrcType->getAsPointerType();
if (!SrcPointer) {
return TSC_NotApplicable;
}
const PointerType *DestPointer = DestType->getAsPointerType();
if (!DestPointer) {
return TSC_NotApplicable;
}
return TryStaticDowncast(Self, SrcPointer->getPointeeType(),
DestPointer->getPointeeType(),
OpRange, SrcType, DestType);
}
/// TryStaticDowncast - Common functionality of TryStaticReferenceDowncast and
/// TryStaticPointerDowncast. Tests whether a static downcast from SrcType to
/// DestType, both of which must be canonical, is possible and allowed.
TryStaticCastResult
TryStaticDowncast(Sema &Self, QualType SrcType, QualType DestType,
const SourceRange &OpRange, QualType OrigSrcType,
QualType OrigDestType)
{
// Downcast can only happen in class hierarchies, so we need classes.
if (!DestType->isRecordType() || !SrcType->isRecordType()) {
return TSC_NotApplicable;
}
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
/*DetectVirtual=*/true);
if (!Self.IsDerivedFrom(DestType, SrcType, Paths)) {
return TSC_NotApplicable;
}
// Target type does derive from source type. Now we're serious. If an error
// appears now, it's not ignored.
// This may not be entirely in line with the standard. Take for example:
// struct A {};
// struct B : virtual A {
// B(A&);
// };
//
// void f()
// {
// (void)static_cast<const B&>(*((A*)0));
// }
// As far as the standard is concerned, p5 does not apply (A is virtual), so
// p2 should be used instead - "const B& t(*((A*)0));" is perfectly valid.
// However, both GCC and Comeau reject this example, and accepting it would
// mean more complex code if we're to preserve the nice error message.
// FIXME: Being 100% compliant here would be nice to have.
// Must preserve cv, as always.
if (!DestType.isAtLeastAsQualifiedAs(SrcType)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
<< "static_cast" << OrigDestType << OrigSrcType << OpRange;
return TSC_Failed;
}
if (Paths.isAmbiguous(SrcType.getUnqualifiedType())) {
// This code is analoguous to that in CheckDerivedToBaseConversion, except
// that it builds the paths in reverse order.
// To sum up: record all paths to the base and build a nice string from
// them. Use it to spice up the error message.
Paths.clear();
Paths.setRecordingPaths(true);
Self.IsDerivedFrom(DestType, SrcType, Paths);
std::string PathDisplayStr;
std::set<unsigned> DisplayedPaths;
for (BasePaths::paths_iterator Path = Paths.begin();
Path != Paths.end(); ++Path) {
if (DisplayedPaths.insert(Path->back().SubobjectNumber).second) {
// We haven't displayed a path to this particular base
// class subobject yet.
PathDisplayStr += "\n ";
for (BasePath::const_reverse_iterator Element = Path->rbegin();
Element != Path->rend(); ++Element)
PathDisplayStr += Element->Base->getType().getAsString() + " -> ";
PathDisplayStr += DestType.getAsString();
}
}
Self.Diag(OpRange.getBegin(), diag::err_ambiguous_base_to_derived_cast)
<< SrcType.getUnqualifiedType() << DestType.getUnqualifiedType()
<< PathDisplayStr << OpRange;
return TSC_Failed;
}
if (Paths.getDetectedVirtual() != 0) {
QualType VirtualBase(Paths.getDetectedVirtual(), 0);
Self.Diag(OpRange.getBegin(), diag::err_static_downcast_via_virtual)
<< OrigSrcType << OrigDestType << VirtualBase << OpRange;
return TSC_Failed;
}
// FIXME: Test accessibility.
return TSC_Success;
}
/// TryStaticMemberPointerUpcast - Tests whether a conversion according to
/// C++ 5.2.9p9 is valid:
///
/// An rvalue of type "pointer to member of D of type cv1 T" can be
/// converted to an rvalue of type "pointer to member of B of type cv2 T",
/// where B is a base class of D [...].
///
TryStaticCastResult
TryStaticMemberPointerUpcast(Sema &Self, QualType SrcType, QualType DestType,
const SourceRange &OpRange)
{
const MemberPointerType *SrcMemPtr = SrcType->getAsMemberPointerType();
if (!SrcMemPtr)
return TSC_NotApplicable;
const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType();
if (!DestMemPtr)
return TSC_NotApplicable;
// T == T, modulo cv
if (Self.Context.getCanonicalType(
SrcMemPtr->getPointeeType().getUnqualifiedType()) !=
Self.Context.getCanonicalType(DestMemPtr->getPointeeType().
getUnqualifiedType()))
return TSC_NotApplicable;
// B base of D
QualType SrcClass(SrcMemPtr->getClass(), 0);
QualType DestClass(DestMemPtr->getClass(), 0);
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
/*DetectVirtual=*/true);
if (!Self.IsDerivedFrom(SrcClass, DestClass, Paths)) {
return TSC_NotApplicable;
}
// B is a base of D. But is it an allowed base? If not, it's a hard error.
if (Paths.isAmbiguous(DestClass)) {
Paths.clear();
Paths.setRecordingPaths(true);
bool StillOkay = Self.IsDerivedFrom(SrcClass, DestClass, Paths);
assert(StillOkay);
StillOkay = StillOkay;
std::string PathDisplayStr = Self.getAmbiguousPathsDisplayString(Paths);
Self.Diag(OpRange.getBegin(), diag::err_ambiguous_memptr_conv)
<< 1 << SrcClass << DestClass << PathDisplayStr << OpRange;
return TSC_Failed;
}
if (const RecordType *VBase = Paths.getDetectedVirtual()) {
Self.Diag(OpRange.getBegin(), diag::err_memptr_conv_via_virtual)
<< SrcClass << DestClass << QualType(VBase, 0) << OpRange;
return TSC_Failed;
}
// FIXME: Test accessibility.
return TSC_Success;
}
/// TryStaticImplicitCast - Tests whether a conversion according to C++ 5.2.9p2
/// is valid:
///
/// An expression e can be explicitly converted to a type T using a
/// @c static_cast if the declaration "T t(e);" is well-formed [...].
TryStaticCastResult
TryStaticImplicitCast(Sema &Self, Expr *SrcExpr, QualType DestType,
const SourceRange &OpRange)
{
if (DestType->isReferenceType()) {
// At this point of CheckStaticCast, if the destination is a reference,
// this has to work. There is no other way that works.
return Self.CheckReferenceInit(SrcExpr, DestType) ?
TSC_Failed : TSC_Success;
}
if (DestType->isRecordType()) {
// FIXME: Use an implementation of C++ [over.match.ctor] for this.
return TSC_NotApplicable;
}
// FIXME: To get a proper error from invalid conversions here, we need to
// reimplement more of this.
ImplicitConversionSequence ICS = Self.TryImplicitConversion(
SrcExpr, DestType);
return ICS.ConversionKind == ImplicitConversionSequence::BadConversion ?
TSC_NotApplicable : TSC_Success;
}
/// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid.
/// Refer to C++ 5.2.7 for details. Dynamic casts are used mostly for runtime-
/// checked downcasts in class hierarchies.
void
CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const SourceRange &OpRange,
const SourceRange &DestRange)
{
QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();
DestType = Self.Context.getCanonicalType(DestType);
// C++ 5.2.7p1: T shall be a pointer or reference to a complete class type,
// or "pointer to cv void".
QualType DestPointee;
const PointerType *DestPointer = DestType->getAsPointerType();
const ReferenceType *DestReference = DestType->getAsReferenceType();
if (DestPointer) {
DestPointee = DestPointer->getPointeeType();
} else if (DestReference) {
DestPointee = DestReference->getPointeeType();
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ref_or_ptr)
<< OrigDestType << DestRange;
return;
}
const RecordType *DestRecord = DestPointee->getAsRecordType();
if (DestPointee->isVoidType()) {
assert(DestPointer && "Reference to void is not possible");
} else if (DestRecord) {
if (Self.RequireCompleteType(OpRange.getBegin(), DestPointee,
diag::err_bad_dynamic_cast_incomplete,
DestRange))
return;
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
<< DestPointee.getUnqualifiedType() << DestRange;
return;
}
// C++0x 5.2.7p2: If T is a pointer type, v shall be an rvalue of a pointer to
// complete class type, [...]. If T is an lvalue reference type, v shall be
// an lvalue of a complete class type, [...]. If T is an rvalue reference
// type, v shall be an expression having a complete effective class type,
// [...]
QualType SrcType = Self.Context.getCanonicalType(OrigSrcType);
QualType SrcPointee;
if (DestPointer) {
if (const PointerType *SrcPointer = SrcType->getAsPointerType()) {
SrcPointee = SrcPointer->getPointeeType();
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_ptr)
<< OrigSrcType << SrcExpr->getSourceRange();
return;
}
} else if (DestReference->isLValueReferenceType()) {
if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
<< "dynamic_cast" << OrigDestType << OpRange;
}
SrcPointee = SrcType;
} else {
SrcPointee = SrcType;
}
const RecordType *SrcRecord = SrcPointee->getAsRecordType();
if (SrcRecord) {
if (Self.RequireCompleteType(OpRange.getBegin(), SrcPointee,
diag::err_bad_dynamic_cast_incomplete,
SrcExpr->getSourceRange()))
return;
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
return;
}
assert((DestPointer || DestReference) &&
"Bad destination non-ptr/ref slipped through.");
assert((DestRecord || DestPointee->isVoidType()) &&
"Bad destination pointee slipped through.");
assert(SrcRecord && "Bad source pointee slipped through.");
// C++ 5.2.7p1: The dynamic_cast operator shall not cast away constness.
if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
<< "dynamic_cast" << OrigDestType << OrigSrcType << OpRange;
return;
}
// C++ 5.2.7p3: If the type of v is the same as the required result type,
// [except for cv].
if (DestRecord == SrcRecord) {
return;
}
// C++ 5.2.7p5
// Upcasts are resolved statically.
if (DestRecord && Self.IsDerivedFrom(SrcPointee, DestPointee)) {
Self.CheckDerivedToBaseConversion(SrcPointee, DestPointee,
OpRange.getBegin(), OpRange);
// Diagnostic already emitted on error.
return;
}
// C++ 5.2.7p6: Otherwise, v shall be [polymorphic].
const RecordDecl *SrcDecl = SrcRecord->getDecl()->getDefinition(Self.Context);
assert(SrcDecl && "Definition missing");
if (!cast<CXXRecordDecl>(SrcDecl)->isPolymorphic()) {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_polymorphic)
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
}
// Done. Everything else is run-time checks.
}

File diff suppressed because it is too large Load Diff

View File

@ -1,450 +0,0 @@
//===--- SemaTemplateInstantiateStmt.cpp - C++ Template Stmt Instantiation ===/
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===/
//
// This file implements C++ template instantiation for statements.
//
//===----------------------------------------------------------------------===/
#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Parse/DeclSpec.h"
#include "llvm/Support/Compiler.h"
using namespace clang;
namespace {
class VISIBILITY_HIDDEN TemplateStmtInstantiator
: public StmtVisitor<TemplateStmtInstantiator, Sema::OwningStmtResult> {
Sema &SemaRef;
const TemplateArgumentList &TemplateArgs;
template<typename T>
Sema::FullExprArg FullExpr(T &expr) {
return SemaRef.FullExpr(expr);
}
public:
typedef Sema::OwningExprResult OwningExprResult;
typedef Sema::OwningStmtResult OwningStmtResult;
TemplateStmtInstantiator(Sema &SemaRef,
const TemplateArgumentList &TemplateArgs)
: SemaRef(SemaRef), TemplateArgs(TemplateArgs) { }
// Declare VisitXXXStmt nodes for all of the statement kinds.
#define STMT(Type, Base) OwningStmtResult Visit##Type(Type *S);
#define EXPR(Type, Base)
#include "clang/AST/StmtNodes.def"
// Visit an expression (which will use the expression
// instantiator).
OwningStmtResult VisitExpr(Expr *E);
// Base case. I'm supposed to ignore this.
OwningStmtResult VisitStmt(Stmt *S) {
S->dump();
assert(false && "Cannot instantiate this kind of statement");
return SemaRef.StmtError();
}
};
}
//===----------------------------------------------------------------------===/
// Common/C statements
//===----------------------------------------------------------------------===/
Sema::OwningStmtResult TemplateStmtInstantiator::VisitDeclStmt(DeclStmt *S) {
llvm::SmallVector<Decl *, 4> Decls;
for (DeclStmt::decl_iterator D = S->decl_begin(), DEnd = S->decl_end();
D != DEnd; ++D) {
Decl *Instantiated = SemaRef.InstantiateDecl(*D, SemaRef.CurContext,
TemplateArgs);
if (!Instantiated)
return SemaRef.StmtError();
Decls.push_back(Instantiated);
SemaRef.CurrentInstantiationScope->InstantiatedLocal(*D, Instantiated);
}
return SemaRef.Owned(new (SemaRef.Context) DeclStmt(
DeclGroupRef::Create(SemaRef.Context,
&Decls[0],
Decls.size()),
S->getStartLoc(),
S->getEndLoc()));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitNullStmt(NullStmt *S) {
return SemaRef.Owned(S->Clone(SemaRef.Context));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitLabelStmt(LabelStmt *S) {
OwningStmtResult SubStmt = Visit(S->getSubStmt());
if (SubStmt.isInvalid())
return SemaRef.StmtError();
// FIXME: Pass the real colon loc in.
return SemaRef.ActOnLabelStmt(S->getIdentLoc(), S->getID(), SourceLocation(),
move(SubStmt));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitGotoStmt(GotoStmt *S) {
return SemaRef.ActOnGotoStmt(S->getGotoLoc(), S->getLabelLoc(),
S->getLabel()->getID());
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitIndirectGotoStmt(IndirectGotoStmt *S) {
OwningExprResult Target = SemaRef.InstantiateExpr(S->getTarget(),
TemplateArgs);
if (Target.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnIndirectGotoStmt(S->getGotoLoc(), S->getStarLoc(),
move(Target));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitBreakStmt(BreakStmt *S) {
return SemaRef.Owned(S->Clone(SemaRef.Context));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitContinueStmt(ContinueStmt *S) {
return SemaRef.Owned(S->Clone(SemaRef.Context));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitReturnStmt(ReturnStmt *S) {
Sema::OwningExprResult Result = SemaRef.ExprEmpty();
if (Expr *E = S->getRetValue()) {
Result = SemaRef.InstantiateExpr(E, TemplateArgs);
if (Result.isInvalid())
return SemaRef.StmtError();
}
return SemaRef.ActOnReturnStmt(S->getReturnLoc(), FullExpr(Result));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitCompoundStmt(CompoundStmt *S) {
return SemaRef.InstantiateCompoundStmt(S, TemplateArgs, false);
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitSwitchCase(SwitchCase *S) {
assert(false && "SwitchCase statements are never directly instantiated");
return SemaRef.StmtError();
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitCaseStmt(CaseStmt *S) {
// The case value expressions are not potentially evaluated.
EnterExpressionEvaluationContext Unevaluated(SemaRef, Action::Unevaluated);
// Instantiate left-hand case value.
OwningExprResult LHS = SemaRef.InstantiateExpr(S->getLHS(), TemplateArgs);
if (LHS.isInvalid())
return SemaRef.StmtError();
// Instantiate right-hand case value (for the GNU case-range extension).
OwningExprResult RHS = SemaRef.InstantiateExpr(S->getRHS(), TemplateArgs);
if (RHS.isInvalid())
return SemaRef.StmtError();
// Build the case statement.
OwningStmtResult Case = SemaRef.ActOnCaseStmt(S->getCaseLoc(),
move(LHS),
S->getEllipsisLoc(),
move(RHS),
S->getColonLoc());
if (Case.isInvalid())
return SemaRef.StmtError();
// Instantiate the statement following the case
OwningStmtResult SubStmt = SemaRef.InstantiateStmt(S->getSubStmt(),
TemplateArgs);
if (SubStmt.isInvalid())
return SemaRef.StmtError();
SemaRef.ActOnCaseStmtBody(Case.get(), move(SubStmt));
return move(Case);
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitDefaultStmt(DefaultStmt *S) {
// Instantiate the statement following the default case
OwningStmtResult SubStmt = SemaRef.InstantiateStmt(S->getSubStmt(),
TemplateArgs);
if (SubStmt.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnDefaultStmt(S->getDefaultLoc(),
S->getColonLoc(),
move(SubStmt),
/*CurScope=*/0);
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitIfStmt(IfStmt *S) {
// Instantiate the condition
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
if (Cond.isInvalid())
return SemaRef.StmtError();
Sema::FullExprArg FullCond(FullExpr(Cond));
// Instantiate the "then" branch.
OwningStmtResult Then = SemaRef.InstantiateStmt(S->getThen(), TemplateArgs);
if (Then.isInvalid())
return SemaRef.StmtError();
// Instantiate the "else" branch.
OwningStmtResult Else = SemaRef.InstantiateStmt(S->getElse(), TemplateArgs);
if (Else.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnIfStmt(S->getIfLoc(), FullCond, move(Then),
S->getElseLoc(), move(Else));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitSwitchStmt(SwitchStmt *S) {
// Instantiate the condition.
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
if (Cond.isInvalid())
return SemaRef.StmtError();
// Start the switch statement itself.
OwningStmtResult Switch = SemaRef.ActOnStartOfSwitchStmt(move(Cond));
if (Switch.isInvalid())
return SemaRef.StmtError();
// Instantiate the body of the switch statement.
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
if (Body.isInvalid())
return SemaRef.StmtError();
// Complete the switch statement.
return SemaRef.ActOnFinishSwitchStmt(S->getSwitchLoc(), move(Switch),
move(Body));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitWhileStmt(WhileStmt *S) {
// Instantiate the condition
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
if (Cond.isInvalid())
return SemaRef.StmtError();
Sema::FullExprArg FullCond(FullExpr(Cond));
// Instantiate the body
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
if (Body.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnWhileStmt(S->getWhileLoc(), FullCond, move(Body));
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitDoStmt(DoStmt *S) {
// Instantiate the condition
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
if (Cond.isInvalid())
return SemaRef.StmtError();
// Instantiate the body
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
if (Body.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnDoStmt(S->getDoLoc(), move(Body), S->getWhileLoc(),
SourceLocation(), move(Cond), S->getRParenLoc());
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitForStmt(ForStmt *S) {
// Instantiate the initialization statement
OwningStmtResult Init = SemaRef.InstantiateStmt(S->getInit(), TemplateArgs);
if (Init.isInvalid())
return SemaRef.StmtError();
// Instantiate the condition
OwningExprResult Cond = SemaRef.InstantiateExpr(S->getCond(), TemplateArgs);
if (Cond.isInvalid())
return SemaRef.StmtError();
// Instantiate the increment
OwningExprResult Inc = SemaRef.InstantiateExpr(S->getInc(), TemplateArgs);
if (Inc.isInvalid())
return SemaRef.StmtError();
// Instantiate the body
OwningStmtResult Body = SemaRef.InstantiateStmt(S->getBody(), TemplateArgs);
if (Body.isInvalid())
return SemaRef.StmtError();
return SemaRef.ActOnForStmt(S->getForLoc(), S->getLParenLoc(),
move(Init), move(Cond), move(Inc),
S->getRParenLoc(), move(Body));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitAsmStmt(AsmStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an 'asm' statement");
return SemaRef.StmtError();
}
//===----------------------------------------------------------------------===/
// C++ statements
//===----------------------------------------------------------------------===/
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitCXXTryStmt(CXXTryStmt *S) {
// Instantiate the try block itself.
OwningStmtResult TryBlock = VisitCompoundStmt(S->getTryBlock());
if (TryBlock.isInvalid())
return SemaRef.StmtError();
// Instantiate the handlers.
ASTOwningVector<&ActionBase::DeleteStmt> Handlers(SemaRef);
for (unsigned I = 0, N = S->getNumHandlers(); I != N; ++I) {
OwningStmtResult Handler = VisitCXXCatchStmt(S->getHandler(I));
if (Handler.isInvalid())
return SemaRef.StmtError();
Handlers.push_back(Handler.takeAs<Stmt>());
}
return SemaRef.ActOnCXXTryBlock(S->getTryLoc(), move(TryBlock),
move_arg(Handlers));
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitCXXCatchStmt(CXXCatchStmt *S) {
// Instantiate the exception declaration, if any.
VarDecl *Var = 0;
if (S->getExceptionDecl()) {
VarDecl *ExceptionDecl = S->getExceptionDecl();
QualType T = SemaRef.InstantiateType(ExceptionDecl->getType(),
TemplateArgs,
ExceptionDecl->getLocation(),
ExceptionDecl->getDeclName());
if (T.isNull())
return SemaRef.StmtError();
Var = SemaRef.BuildExceptionDeclaration(0, T,
ExceptionDecl->getIdentifier(),
ExceptionDecl->getLocation(),
/*FIXME: Inaccurate*/
SourceRange(ExceptionDecl->getLocation()));
if (Var->isInvalidDecl()) {
Var->Destroy(SemaRef.Context);
return SemaRef.StmtError();
}
// Introduce the exception declaration into scope.
SemaRef.CurrentInstantiationScope->InstantiatedLocal(ExceptionDecl, Var);
}
// Instantiate the actual exception handler.
OwningStmtResult Handler = Visit(S->getHandlerBlock());
if (Handler.isInvalid()) {
if (Var)
Var->Destroy(SemaRef.Context);
return SemaRef.StmtError();
}
return SemaRef.Owned(new (SemaRef.Context) CXXCatchStmt(S->getCatchLoc(),
Var,
Handler.takeAs<Stmt>()));
}
//===----------------------------------------------------------------------===/
// Objective-C statements
//===----------------------------------------------------------------------===/
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C @finally statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCAtSynchronizedStmt(
ObjCAtSynchronizedStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C @synchronized statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCAtTryStmt(ObjCAtTryStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C @try statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCForCollectionStmt(
ObjCForCollectionStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C \"for\" statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C @throw statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult
TemplateStmtInstantiator::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) {
// FIXME: Implement this
assert(false && "Cannot instantiate an Objective-C @catch statement");
return SemaRef.StmtError();
}
Sema::OwningStmtResult TemplateStmtInstantiator::VisitExpr(Expr *E) {
Sema::OwningExprResult Result = SemaRef.InstantiateExpr(E, TemplateArgs);
if (Result.isInvalid())
return SemaRef.StmtError();
return SemaRef.Owned(Result.takeAs<Stmt>());
}
Sema::OwningStmtResult
Sema::InstantiateStmt(Stmt *S, const TemplateArgumentList &TemplateArgs) {
if (!S)
return Owned((Stmt *)0);
TemplateStmtInstantiator Instantiator(*this, TemplateArgs);
return Instantiator.Visit(S);
}
Sema::OwningStmtResult
Sema::InstantiateCompoundStmt(CompoundStmt *S,
const TemplateArgumentList &TemplateArgs,
bool isStmtExpr) {
if (!S)
return Owned((Stmt *)0);
TemplateStmtInstantiator Instantiator(*this, TemplateArgs);
ASTOwningVector<&ActionBase::DeleteStmt> Statements(*this);
for (CompoundStmt::body_iterator B = S->body_begin(), BEnd = S->body_end();
B != BEnd; ++B) {
OwningStmtResult Result = Instantiator.Visit(*B);
if (Result.isInvalid())
return StmtError();
Statements.push_back(Result.takeAs<Stmt>());
}
return ActOnCompoundStmt(S->getLBracLoc(), S->getRBracLoc(),
move_arg(Statements), isStmtExpr);
}

View File

@ -1,21 +0,0 @@
LEVEL = ../../..
include $(LEVEL)/Makefile.common
# Test in all immediate subdirectories if unset.
TESTDIRS ?= $(shell echo $(PROJ_SRC_DIR)/*/)
ifndef TESTARGS
ifdef VERBOSE
TESTARGS = -v
else
TESTARGS = -s
endif
endif
all::
@ PATH=$(ToolDir):$(LLVM_SRC_ROOT)/test/Scripts:$$PATH VG=$(VG) ../utils/test/MultiTestRunner.py $(TESTARGS) $(TESTDIRS)
clean::
@ rm -rf Output/
.PHONY: all report clean

View File

@ -1,347 +0,0 @@
#!/usr/bin/python
"""
MultiTestRunner - Harness for running multiple tests in the simple clang style.
TODO
--
- Fix Ctrl-c issues
- Use a timeout
- Detect signalled failures (abort)
- Better support for finding tests
"""
# TOD
import os, sys, re, random, time
import threading
import ProgressBar
import TestRunner
from TestRunner import TestStatus
from Queue import Queue
kTestFileExtensions = set(['.mi','.i','.c','.cpp','.m','.mm','.ll'])
def getTests(inputs):
for path in inputs:
if not os.path.exists(path):
print >>sys.stderr,"WARNING: Invalid test \"%s\""%(path,)
continue
if os.path.isdir(path):
for dirpath,dirnames,filenames in os.walk(path):
dotTests = os.path.join(dirpath,'.tests')
if os.path.exists(dotTests):
for ln in open(dotTests):
if ln.strip():
yield os.path.join(dirpath,ln.strip())
else:
# FIXME: This doesn't belong here
if 'Output' in dirnames:
dirnames.remove('Output')
for f in filenames:
base,ext = os.path.splitext(f)
if ext in kTestFileExtensions:
yield os.path.join(dirpath,f)
else:
yield path
class TestingProgressDisplay:
def __init__(self, opts, numTests, progressBar=None):
self.opts = opts
self.numTests = numTests
self.digits = len(str(self.numTests))
self.current = None
self.lock = threading.Lock()
self.progressBar = progressBar
self.progress = 0.
def update(self, index, tr):
# Avoid locking overhead in quiet mode
if self.opts.quiet and not tr.failed():
return
# Output lock
self.lock.acquire()
try:
self.handleUpdate(index, tr)
finally:
self.lock.release()
def finish(self):
if self.progressBar:
self.progressBar.clear()
elif self.opts.succinct:
sys.stdout.write('\n')
def handleUpdate(self, index, tr):
if self.progressBar:
if tr.failed():
self.progressBar.clear()
else:
# Force monotonicity
self.progress = max(self.progress, float(index)/self.numTests)
self.progressBar.update(self.progress, tr.path)
return
elif self.opts.succinct:
if not tr.failed():
sys.stdout.write('.')
sys.stdout.flush()
return
else:
sys.stdout.write('\n')
extra = ''
if tr.code==TestStatus.Invalid:
extra = ' - (Invalid test)'
elif tr.code==TestStatus.NoRunLine:
extra = ' - (No RUN line)'
elif tr.failed():
extra = ' - %s'%(TestStatus.getName(tr.code).upper(),)
print '%*d/%*d - %s%s'%(self.digits, index+1, self.digits,
self.numTests, tr.path, extra)
if tr.failed() and self.opts.showOutput:
TestRunner.cat(tr.testResults, sys.stdout)
class TestResult:
def __init__(self, path, code, testResults):
self.path = path
self.code = code
self.testResults = testResults
def failed(self):
return self.code in (TestStatus.Fail,TestStatus.XPass)
class TestProvider:
def __init__(self, opts, tests, display):
self.opts = opts
self.tests = tests
self.index = 0
self.lock = threading.Lock()
self.results = [None]*len(self.tests)
self.startTime = time.time()
self.progress = display
def get(self):
self.lock.acquire()
try:
if self.opts.maxTime is not None:
if time.time() - self.startTime > self.opts.maxTime:
return None
if self.index >= len(self.tests):
return None
item = self.tests[self.index],self.index
self.index += 1
return item
finally:
self.lock.release()
def setResult(self, index, result):
self.results[index] = result
self.progress.update(index, result)
class Tester(threading.Thread):
def __init__(self, provider):
threading.Thread.__init__(self)
self.provider = provider
def run(self):
while 1:
item = self.provider.get()
if item is None:
break
self.runTest(item)
def runTest(self, (path,index)):
command = path
# Use hand concatentation here because we want to override
# absolute paths.
output = 'Output/' + path + '.out'
testname = path
testresults = 'Output/' + path + '.testresults'
TestRunner.mkdir_p(os.path.dirname(testresults))
numTests = len(self.provider.tests)
digits = len(str(numTests))
code = None
try:
opts = self.provider.opts
if opts.debugDoNotTest:
code = None
else:
code = TestRunner.runOneTest(path, command, output, testname,
opts.clang, opts.clangcc,
useValgrind=opts.useValgrind,
useDGCompat=opts.useDGCompat,
useScript=opts.testScript,
output=open(testresults,'w'))
except KeyboardInterrupt:
# This is a sad hack. Unfortunately subprocess goes
# bonkers with ctrl-c and we start forking merrily.
print 'Ctrl-C detected, goodbye.'
os.kill(0,9)
self.provider.setResult(index, TestResult(path, code, testresults))
def detectCPUs():
"""
Detects the number of CPUs on a system. Cribbed from pp.
"""
# Linux, Unix and MacOS:
if hasattr(os, "sysconf"):
if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
# Linux & Unix:
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
if isinstance(ncpus, int) and ncpus > 0:
return ncpus
else: # OSX:
return int(os.popen2("sysctl -n hw.ncpu")[1].read())
# Windows:
if os.environ.has_key("NUMBER_OF_PROCESSORS"):
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
if ncpus > 0:
return ncpus
return 1 # Default
def main():
global options
from optparse import OptionParser
parser = OptionParser("usage: %prog [options] {inputs}")
parser.add_option("-j", "--threads", dest="numThreads",
help="Number of testing threads",
type=int, action="store",
default=detectCPUs())
parser.add_option("", "--clang", dest="clang",
help="Program to use as \"clang\"",
action="store", default=None)
parser.add_option("", "--clang-cc", dest="clangcc",
help="Program to use as \"clang-cc\"",
action="store", default=None)
parser.add_option("", "--vg", dest="useValgrind",
help="Run tests under valgrind",
action="store_true", default=False)
parser.add_option("", "--dg", dest="useDGCompat",
help="Use llvm dejagnu compatibility mode",
action="store_true", default=False)
parser.add_option("", "--script", dest="testScript",
help="Default script to use",
action="store", default=None)
parser.add_option("-v", "--verbose", dest="showOutput",
help="Show all test output",
action="store_true", default=False)
parser.add_option("-q", "--quiet", dest="quiet",
help="Suppress no error output",
action="store_true", default=False)
parser.add_option("-s", "--succinct", dest="succinct",
help="Reduce amount of output",
action="store_true", default=False)
parser.add_option("", "--max-tests", dest="maxTests",
help="Maximum number of tests to run",
action="store", type=int, default=None)
parser.add_option("", "--max-time", dest="maxTime",
help="Maximum time to spend testing (in seconds)",
action="store", type=float, default=None)
parser.add_option("", "--shuffle", dest="shuffle",
help="Run tests in random order",
action="store_true", default=False)
parser.add_option("", "--seed", dest="seed",
help="Seed for random number generator (default: random)",
action="store", default=None)
parser.add_option("", "--no-progress-bar", dest="useProgressBar",
help="Do not use curses based progress bar",
action="store_false", default=True)
parser.add_option("", "--debug-do-not-test", dest="debugDoNotTest",
help="DEBUG: Skip running actual test script",
action="store_true", default=False)
parser.add_option("", "--path", dest="path",
help="Additional paths to add to testing environment",
action="store", type=str, default=None)
(opts, args) = parser.parse_args()
if not args:
parser.error('No inputs specified')
if opts.clang is None:
opts.clang = TestRunner.inferClang()
if opts.clangcc is None:
opts.clangcc = TestRunner.inferClangCC(opts.clang)
# FIXME: It could be worth loading these in parallel with testing.
allTests = list(getTests(args))
allTests.sort()
tests = allTests
if opts.seed is not None:
try:
seed = int(opts.seed)
except:
parser.error('--seed argument should be an integer')
random.seed(seed)
if opts.shuffle:
random.shuffle(tests)
if opts.maxTests is not None:
tests = tests[:opts.maxTests]
if opts.path is not None:
os.environ["PATH"] = opts.path + ":" + os.environ["PATH"];
extra = ''
if len(tests) != len(allTests):
extra = ' of %d'%(len(allTests),)
header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
opts.numThreads)
progressBar = None
if not opts.quiet:
if opts.useProgressBar:
try:
tc = ProgressBar.TerminalController()
progressBar = ProgressBar.ProgressBar(tc, header)
except ValueError:
pass
if not progressBar:
print header
display = TestingProgressDisplay(opts, len(tests), progressBar)
provider = TestProvider(opts, tests, display)
testers = [Tester(provider) for i in range(opts.numThreads)]
startTime = time.time()
for t in testers:
t.start()
try:
for t in testers:
t.join()
except KeyboardInterrupt:
sys.exit(1)
display.finish()
if not opts.quiet:
print 'Testing Time: %.2fs'%(time.time() - startTime)
# List test results organized organized by kind.
byCode = {}
for t in provider.results:
if t:
if t.code not in byCode:
byCode[t.code] = []
byCode[t.code].append(t)
for title,code in (('Expected Failures', TestStatus.XFail),
('Unexpected Passing Tests', TestStatus.XPass),
('Failing Tests', TestStatus.Fail)):
elts = byCode.get(code)
if not elts:
continue
print '*'*20
print '%s (%d):' % (title, len(elts))
for tr in elts:
print '\t%s'%(tr.path,)
numFailures = len(byCode.get(TestStatus.Fail,[]))
if numFailures:
print '\nFailures: %d' % (numFailures,)
sys.exit(1)
if __name__=='__main__':
main()

View File

@ -1,227 +0,0 @@
#!/usr/bin/python
# Source: http://code.activestate.com/recipes/475116/, with
# modifications by Daniel Dunbar.
import sys, re, time
class TerminalController:
"""
A class that can be used to portably generate formatted output to
a terminal.
`TerminalController` defines a set of instance variables whose
values are initialized to the control sequence necessary to
perform a given action. These can be simply included in normal
output to the terminal:
>>> term = TerminalController()
>>> print 'This is '+term.GREEN+'green'+term.NORMAL
Alternatively, the `render()` method can used, which replaces
'${action}' with the string required to perform 'action':
>>> term = TerminalController()
>>> print term.render('This is ${GREEN}green${NORMAL}')
If the terminal doesn't support a given action, then the value of
the corresponding instance variable will be set to ''. As a
result, the above code will still work on terminals that do not
support color, except that their output will not be colored.
Also, this means that you can test whether the terminal supports a
given action by simply testing the truth value of the
corresponding instance variable:
>>> term = TerminalController()
>>> if term.CLEAR_SCREEN:
... print 'This terminal supports clearning the screen.'
Finally, if the width and height of the terminal are known, then
they will be stored in the `COLS` and `LINES` attributes.
"""
# Cursor movement:
BOL = '' #: Move the cursor to the beginning of the line
UP = '' #: Move the cursor up one line
DOWN = '' #: Move the cursor down one line
LEFT = '' #: Move the cursor left one char
RIGHT = '' #: Move the cursor right one char
# Deletion:
CLEAR_SCREEN = '' #: Clear the screen and move to home position
CLEAR_EOL = '' #: Clear to the end of the line.
CLEAR_BOL = '' #: Clear to the beginning of the line.
CLEAR_EOS = '' #: Clear to the end of the screen
# Output modes:
BOLD = '' #: Turn on bold mode
BLINK = '' #: Turn on blink mode
DIM = '' #: Turn on half-bright mode
REVERSE = '' #: Turn on reverse-video mode
NORMAL = '' #: Turn off all modes
# Cursor display:
HIDE_CURSOR = '' #: Make the cursor invisible
SHOW_CURSOR = '' #: Make the cursor visible
# Terminal size:
COLS = None #: Width of the terminal (None for unknown)
LINES = None #: Height of the terminal (None for unknown)
# Foreground colors:
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
# Background colors:
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
_STRING_CAPABILITIES = """
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
def __init__(self, term_stream=sys.stdout):
"""
Create a `TerminalController` and initialize its attributes
with appropriate values for the current terminal.
`term_stream` is the stream that will be used for terminal
output; if this stream is not a tty, then the terminal is
assumed to be a dumb terminal (i.e., have no capabilities).
"""
# Curses isn't available on all platforms
try: import curses
except: return
# If the stream isn't a tty, then assume it has no capabilities.
if not term_stream.isatty(): return
# Check the terminal type. If we fail, then assume that the
# terminal has no capabilities.
try: curses.setupterm()
except: return
# Look up numeric capabilities.
self.COLS = curses.tigetnum('cols')
self.LINES = curses.tigetnum('lines')
# Look up string capabilities.
for capability in self._STRING_CAPABILITIES:
(attrib, cap_name) = capability.split('=')
setattr(self, attrib, self._tigetstr(cap_name) or '')
# Colors
set_fg = self._tigetstr('setf')
if set_fg:
for i,color in zip(range(len(self._COLORS)), self._COLORS):
setattr(self, color, curses.tparm(set_fg, i) or '')
set_fg_ansi = self._tigetstr('setaf')
if set_fg_ansi:
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
set_bg = self._tigetstr('setb')
if set_bg:
for i,color in zip(range(len(self._COLORS)), self._COLORS):
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
set_bg_ansi = self._tigetstr('setab')
if set_bg_ansi:
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
def _tigetstr(self, cap_name):
# String capabilities can include "delays" of the form "$<2>".
# For any modern terminal, we should be able to just ignore
# these, so strip them out.
import curses
cap = curses.tigetstr(cap_name) or ''
return re.sub(r'\$<\d+>[/*]?', '', cap)
def render(self, template):
"""
Replace each $-substitutions in the given template string with
the corresponding terminal control string (if it's defined) or
'' (if it's not).
"""
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
def _render_sub(self, match):
s = match.group()
if s == '$$': return s
else: return getattr(self, s[2:-1])
#######################################################################
# Example use case: progress bar
#######################################################################
class ProgressBar:
"""
A 3-line progress bar, which looks like::
Header
20% [===========----------------------------------]
progress message
The progress bar is colored, if the terminal supports color
output; and adjusts to the width of the terminal.
"""
BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s\n'
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
def __init__(self, term, header, useETA=True):
self.term = term
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
raise ValueError("Terminal isn't capable enough -- you "
"should use a simpler progress dispaly.")
self.width = self.term.COLS or 75
self.bar = term.render(self.BAR)
self.header = self.term.render(self.HEADER % header.center(self.width))
self.cleared = 1 #: true if we haven't drawn the bar yet.
self.useETA = useETA
if self.useETA:
self.startTime = time.time()
self.update(0, '')
def update(self, percent, message):
if self.cleared:
sys.stdout.write(self.header)
self.cleared = 0
prefix = '%3d%% ' % (percent*100,)
suffix = ''
if self.useETA:
elapsed = time.time() - self.startTime
if percent > .0001 and elapsed > 1:
total = elapsed / percent
eta = int(total - elapsed)
h = eta//3600.
m = (eta//60) % 60
s = eta % 60
suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
barWidth = self.width - len(prefix) - len(suffix) - 2
n = int(barWidth*percent)
if len(message) < self.width:
message = message + ' '*(self.width - len(message))
else:
message = '... ' + message[-(self.width-4):]
sys.stdout.write(
self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
(self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
self.term.CLEAR_EOL + message)
def clear(self):
if not self.cleared:
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL)
self.cleared = 1
def test():
import time
tc = TerminalController()
p = ProgressBar(tc, 'Tests')
for i in range(101):
p.update(i/100., str(i))
time.sleep(.3)
if __name__=='__main__':
test()

View File

@ -1,296 +0,0 @@
#!/usr/bin/python
#
# TestRunner.py - This script is used to run arbitrary unit tests. Unit
# tests must contain the command used to run them in the input file, starting
# immediately after a "RUN:" string.
#
# This runner recognizes and replaces the following strings in the command:
#
# %s - Replaced with the input name of the program, or the program to
# execute, as appropriate.
# %S - Replaced with the directory where the input resides.
# %llvmgcc - llvm-gcc command
# %llvmgxx - llvm-g++ command
# %prcontext - prcontext.tcl script
# %t - temporary file name (derived from testcase name)
#
import errno
import os
import re
import signal
import subprocess
import sys
# Increase determinism for things that use the terminal width.
#
# FIXME: Find a better place for this hack.
os.environ['COLUMNS'] = '0'
class TestStatus:
Pass = 0
XFail = 1
Fail = 2
XPass = 3
NoRunLine = 4
Invalid = 5
kNames = ['Pass','XFail','Fail','XPass','NoRunLine','Invalid']
@staticmethod
def getName(code):
return TestStatus.kNames[code]
def mkdir_p(path):
if not path:
pass
elif os.path.exists(path):
pass
else:
parent = os.path.dirname(path)
if parent != path:
mkdir_p(parent)
try:
os.mkdir(path)
except OSError,e:
if e.errno != errno.EEXIST:
raise
def remove(path):
try:
os.remove(path)
except OSError:
pass
def cat(path, output):
f = open(path)
output.writelines(f)
f.close()
def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG, CLANGCC,
useValgrind=False,
useDGCompat=False,
useScript=None,
output=sys.stdout):
if useValgrind:
VG_OUTPUT = '%s.vg'%(OUTPUT,)
if os.path.exists:
remove(VG_OUTPUT)
CLANG = 'valgrind --leak-check=full --quiet --log-file=%s %s'%(VG_OUTPUT, CLANG)
# Create the output directory if it does not already exist.
mkdir_p(os.path.dirname(OUTPUT))
# FIXME
#ulimit -t 40
# FIXME: Load script once
# FIXME: Support "short" script syntax
if useScript:
scriptFile = useScript
else:
# See if we have a per-dir test script.
dirScriptFile = os.path.join(os.path.dirname(FILENAME), 'test.script')
if os.path.exists(dirScriptFile):
scriptFile = dirScriptFile
else:
scriptFile = FILENAME
# Verify the script contains a run line.
for ln in open(scriptFile):
if 'RUN:' in ln:
break
else:
print >>output, "******************** TEST '%s' HAS NO RUN LINE! ********************"%(TESTNAME,)
output.flush()
return TestStatus.NoRunLine
OUTPUT = os.path.abspath(OUTPUT)
FILENAME = os.path.abspath(FILENAME)
SCRIPT = OUTPUT + '.script'
TEMPOUTPUT = OUTPUT + '.tmp'
substitutions = [('%s',SUBST),
('%S',os.path.dirname(SUBST)),
('%llvmgcc','llvm-gcc -emit-llvm -w'),
('%llvmgxx','llvm-g++ -emit-llvm -w'),
('%prcontext','prcontext.tcl'),
('%t',TEMPOUTPUT),
(' clang ', ' ' + CLANG + ' '),
(' clang-cc ', ' ' + CLANGCC + ' ')]
scriptLines = []
xfailLines = []
for ln in open(scriptFile):
if 'RUN:' in ln:
# Isolate run parameters
index = ln.index('RUN:')
ln = ln[index+4:]
# Apply substitutions
for a,b in substitutions:
ln = ln.replace(a,b)
if useDGCompat:
ln = re.sub(r'\{(.*)\}', r'"\1"', ln)
scriptLines.append(ln)
elif 'XFAIL' in ln:
xfailLines.append(ln)
if xfailLines:
print >>output, "XFAILED '%s':"%(TESTNAME,)
output.writelines(xfailLines)
# Write script file
f = open(SCRIPT,'w')
f.write(''.join(scriptLines))
f.close()
outputFile = open(OUTPUT,'w')
p = None
try:
p = subprocess.Popen(["/bin/sh",SCRIPT],
cwd=os.path.dirname(FILENAME),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out,err = p.communicate()
outputFile.write(out)
outputFile.write(err)
SCRIPT_STATUS = p.wait()
# Detect Ctrl-C in subprocess.
if SCRIPT_STATUS == -signal.SIGINT:
raise KeyboardInterrupt
except KeyboardInterrupt:
raise
outputFile.close()
if xfailLines:
SCRIPT_STATUS = not SCRIPT_STATUS
if useValgrind:
VG_STATUS = len(list(open(VG_OUTPUT)))
else:
VG_STATUS = 0
if SCRIPT_STATUS or VG_STATUS:
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
print >>output, "Command: "
output.writelines(scriptLines)
if not SCRIPT_STATUS:
print >>output, "Output:"
else:
print >>output, "Incorrect Output:"
cat(OUTPUT, output)
if VG_STATUS:
print >>output, "Valgrind Output:"
cat(VG_OUTPUT, output)
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
output.flush()
if xfailLines:
return TestStatus.XPass
else:
return TestStatus.Fail
if xfailLines:
return TestStatus.XFail
else:
return TestStatus.Pass
def capture(args):
p = subprocess.Popen(args, stdout=subprocess.PIPE)
out,_ = p.communicate()
return out
def which(command):
# Would be nice if Python had a lib function for this.
res = capture(['which',command])
res = res.strip()
if res and os.path.exists(res):
return res
return None
def inferClang():
# Determine which clang to use.
clang = os.getenv('CLANG')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clang:
return clang
# Otherwise look in the path.
clang = which('clang')
if not clang:
print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
sys.exit(1)
return clang
def inferClangCC(clang):
clangcc = os.getenv('CLANGCC')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clangcc:
return clangcc
# Otherwise try adding -cc since we expect to be looking in a build
# directory.
clangcc = which(clang + '-cc')
if not clangcc:
# Otherwise ask clang.
res = capture([clang, '-print-prog-name=clang-cc'])
res = res.strip()
if res and os.path.exists(res):
clangcc = res
if not clangcc:
print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
sys.exit(1)
return clangcc
def main():
global options
from optparse import OptionParser
parser = OptionParser("usage: %prog [options] {tests}")
parser.add_option("", "--clang", dest="clang",
help="Program to use as \"clang\"",
action="store", default=None)
parser.add_option("", "--clang-cc", dest="clangcc",
help="Program to use as \"clang-cc\"",
action="store", default=None)
parser.add_option("", "--vg", dest="useValgrind",
help="Run tests under valgrind",
action="store_true", default=False)
parser.add_option("", "--dg", dest="useDGCompat",
help="Use llvm dejagnu compatibility mode",
action="store_true", default=False)
(opts, args) = parser.parse_args()
if not args:
parser.error('No tests specified')
if opts.clang is None:
opts.clang = inferClang()
if opts.clangcc is None:
opts.clangcc = inferClangCC(opts.clang)
for path in args:
command = path
# Use hand concatentation here because we want to override
# absolute paths.
output = 'Output/' + path + '.out'
testname = path
res = runOneTest(path, command, output, testname,
opts.clang, opts.clangcc,
useValgrind=opts.useValgrind,
useDGCompat=opts.useDGCompat,
useScript=os.getenv("TEST_SCRIPT"))
sys.exit(res == TestStatus.Fail or res == TestStatus.XPass)
if __name__=='__main__':
main()