blob: 9a967497b122d5f4adf343a47401682d30b95958 [file] [log] [blame]
//===--- LangOptions.cpp - Language & configuration options ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the LangOptions class, which provides various
// language and configuration flags.
//
//===----------------------------------------------------------------------===//
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Config.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include <limits.h>
using namespace swift;
static const StringRef SupportedConditionalCompilationOSs[] = {
"OSX",
"macOS",
"tvOS",
"watchOS",
"iOS",
"Linux",
"FreeBSD",
"Windows",
"Android",
"PS4",
"Cygwin",
"Haiku",
};
static const StringRef SupportedConditionalCompilationArches[] = {
"arm",
"arm64",
"i386",
"x86_64",
"powerpc64",
"powerpc64le",
"s390x"
};
static const StringRef SupportedConditionalCompilationEndianness[] = {
"little",
"big"
};
static const StringRef SupportedConditionalCompilationRuntimes[] = {
"_ObjC",
"_Native",
};
static const StringRef SupportedConditionalCompilationTargetEnvironments[] = {
"simulator",
};
static const PlatformConditionKind AllPublicPlatformConditionKinds[] = {
#define PLATFORM_CONDITION(LABEL, IDENTIFIER) PlatformConditionKind::LABEL,
#define PLATFORM_CONDITION_(LABEL, IDENTIFIER)
#include "swift/AST/PlatformConditionKinds.def"
};
ArrayRef<StringRef> getSupportedConditionalCompilationValues(const PlatformConditionKind &Kind) {
switch (Kind) {
case PlatformConditionKind::OS:
return SupportedConditionalCompilationOSs;
case PlatformConditionKind::Arch:
return SupportedConditionalCompilationArches;
case PlatformConditionKind::Endianness:
return SupportedConditionalCompilationEndianness;
case PlatformConditionKind::Runtime:
return SupportedConditionalCompilationRuntimes;
case PlatformConditionKind::CanImport:
return { };
case PlatformConditionKind::TargetEnvironment:
return SupportedConditionalCompilationTargetEnvironments;
}
llvm_unreachable("Unhandled PlatformConditionKind in switch");
}
PlatformConditionKind suggestedPlatformConditionKind(PlatformConditionKind Kind, const StringRef &V,
std::vector<StringRef> &suggestedValues) {
std::string lower = V.lower();
for (const PlatformConditionKind& candidateKind : AllPublicPlatformConditionKinds) {
if (candidateKind != Kind) {
auto supportedValues = getSupportedConditionalCompilationValues(candidateKind);
for (const StringRef& candidateValue : supportedValues) {
if (candidateValue.lower() == lower) {
suggestedValues.clear();
if (candidateValue != V) {
suggestedValues.emplace_back(candidateValue);
}
return candidateKind;
}
}
}
}
return Kind;
}
bool isMatching(PlatformConditionKind Kind, const StringRef &V,
PlatformConditionKind &suggestedKind, std::vector<StringRef> &suggestions) {
// Compare against known values, ignoring case to avoid penalizing
// characters with incorrect case.
unsigned minDistance = std::numeric_limits<unsigned>::max();
std::string lower = V.lower();
auto supportedValues = getSupportedConditionalCompilationValues(Kind);
for (const StringRef& candidate : supportedValues) {
if (candidate == V) {
suggestedKind = Kind;
suggestions.clear();
return true;
}
unsigned distance = StringRef(lower).edit_distance(candidate.lower());
if (distance < minDistance) {
suggestions.clear();
minDistance = distance;
}
if (distance == minDistance)
suggestions.emplace_back(candidate);
}
suggestedKind = suggestedPlatformConditionKind(Kind, V, suggestions);
return false;
}
bool LangOptions::
checkPlatformConditionSupported(PlatformConditionKind Kind, StringRef Value,
PlatformConditionKind &suggestedKind,
std::vector<StringRef> &suggestedValues) {
switch (Kind) {
case PlatformConditionKind::OS:
case PlatformConditionKind::Arch:
case PlatformConditionKind::Endianness:
case PlatformConditionKind::Runtime:
case PlatformConditionKind::TargetEnvironment:
return isMatching(Kind, Value, suggestedKind, suggestedValues);
case PlatformConditionKind::CanImport:
// All importable names are valid.
// FIXME: Perform some kind of validation of the string?
return true;
}
llvm_unreachable("Unhandled enum value");
}
StringRef
LangOptions::getPlatformConditionValue(PlatformConditionKind Kind) const {
// Last one wins.
for (auto &Opt : llvm::reverse(PlatformConditionValues)) {
if (Opt.first == Kind)
return Opt.second;
}
return StringRef();
}
bool LangOptions::
checkPlatformCondition(PlatformConditionKind Kind, StringRef Value) const {
// Check a special case that "macOS" is an alias of "OSX".
if (Kind == PlatformConditionKind::OS && Value == "macOS")
return checkPlatformCondition(Kind, "OSX");
for (auto &Opt : llvm::reverse(PlatformConditionValues)) {
if (Opt.first == Kind)
if (Opt.second == Value)
return true;
}
return false;
}
bool LangOptions::isCustomConditionalCompilationFlagSet(StringRef Name) const {
return std::find(CustomConditionalCompilationFlags.begin(),
CustomConditionalCompilationFlags.end(), Name)
!= CustomConditionalCompilationFlags.end();
}
std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {
clearAllPlatformConditionValues();
if (triple.getOS() == llvm::Triple::Darwin &&
triple.getVendor() == llvm::Triple::Apple) {
// Rewrite darwinX.Y triples to macosx10.X'.Y ones.
// It affects code generation on our platform.
llvm::SmallString<16> osxBuf;
llvm::raw_svector_ostream osx(osxBuf);
osx << llvm::Triple::getOSTypeName(llvm::Triple::MacOSX);
unsigned major, minor, micro;
triple.getMacOSXVersion(major, minor, micro);
osx << major << "." << minor;
if (micro != 0)
osx << "." << micro;
triple.setOSName(osx.str());
}
Target = std::move(triple);
bool UnsupportedOS = false;
// Set the "os" platform condition.
if (Target.isMacOSX())
addPlatformConditionValue(PlatformConditionKind::OS, "OSX");
else if (Target.isTvOS())
addPlatformConditionValue(PlatformConditionKind::OS, "tvOS");
else if (Target.isWatchOS())
addPlatformConditionValue(PlatformConditionKind::OS, "watchOS");
else if (Target.isiOS())
addPlatformConditionValue(PlatformConditionKind::OS, "iOS");
else if (Target.isAndroid())
addPlatformConditionValue(PlatformConditionKind::OS, "Android");
else if (Target.isOSLinux())
addPlatformConditionValue(PlatformConditionKind::OS, "Linux");
else if (Target.isOSFreeBSD())
addPlatformConditionValue(PlatformConditionKind::OS, "FreeBSD");
else if (Target.isWindowsCygwinEnvironment())
addPlatformConditionValue(PlatformConditionKind::OS, "Cygwin");
else if (Target.isOSWindows())
addPlatformConditionValue(PlatformConditionKind::OS, "Windows");
else if (Target.isPS4())
addPlatformConditionValue(PlatformConditionKind::OS, "PS4");
else if (Target.isOSHaiku())
addPlatformConditionValue(PlatformConditionKind::OS, "Haiku");
else
UnsupportedOS = true;
bool UnsupportedArch = false;
// Set the "arch" platform condition.
switch (Target.getArch()) {
case llvm::Triple::ArchType::arm:
case llvm::Triple::ArchType::thumb:
addPlatformConditionValue(PlatformConditionKind::Arch, "arm");
break;
case llvm::Triple::ArchType::aarch64:
addPlatformConditionValue(PlatformConditionKind::Arch, "arm64");
break;
case llvm::Triple::ArchType::ppc64:
addPlatformConditionValue(PlatformConditionKind::Arch, "powerpc64");
break;
case llvm::Triple::ArchType::ppc64le:
addPlatformConditionValue(PlatformConditionKind::Arch, "powerpc64le");
break;
case llvm::Triple::ArchType::x86:
addPlatformConditionValue(PlatformConditionKind::Arch, "i386");
break;
case llvm::Triple::ArchType::x86_64:
addPlatformConditionValue(PlatformConditionKind::Arch, "x86_64");
break;
case llvm::Triple::ArchType::systemz:
addPlatformConditionValue(PlatformConditionKind::Arch, "s390x");
break;
default:
UnsupportedArch = true;
}
if (UnsupportedOS || UnsupportedArch)
return { UnsupportedOS, UnsupportedArch };
// Set the "_endian" platform condition.
switch (Target.getArch()) {
case llvm::Triple::ArchType::arm:
case llvm::Triple::ArchType::thumb:
addPlatformConditionValue(PlatformConditionKind::Endianness, "little");
break;
case llvm::Triple::ArchType::aarch64:
addPlatformConditionValue(PlatformConditionKind::Endianness, "little");
break;
case llvm::Triple::ArchType::ppc64:
addPlatformConditionValue(PlatformConditionKind::Endianness, "big");
break;
case llvm::Triple::ArchType::ppc64le:
addPlatformConditionValue(PlatformConditionKind::Endianness, "little");
break;
case llvm::Triple::ArchType::x86:
addPlatformConditionValue(PlatformConditionKind::Endianness, "little");
break;
case llvm::Triple::ArchType::x86_64:
addPlatformConditionValue(PlatformConditionKind::Endianness, "little");
break;
case llvm::Triple::ArchType::systemz:
addPlatformConditionValue(PlatformConditionKind::Endianness, "big");
break;
default:
llvm_unreachable("undefined architecture endianness");
}
// Set the "runtime" platform condition.
if (EnableObjCInterop)
addPlatformConditionValue(PlatformConditionKind::Runtime, "_ObjC");
else
addPlatformConditionValue(PlatformConditionKind::Runtime, "_Native");
// Set the "targetEnvironment" platform condition if targeting a simulator
// environment. Otherwise _no_ value is present for targetEnvironment; it's
// an optional disambiguating refinement of the triple.
if (swift::tripleIsAnySimulator(Target))
addPlatformConditionValue(PlatformConditionKind::TargetEnvironment,
"simulator");
// If you add anything to this list, change the default size of
// PlatformConditionValues to not require an extra allocation
// in the common case.
return { false, false };
}
bool LangOptions::doesTargetSupportObjCMetadataUpdateCallback() const {
if (Target.isMacOSX())
return !Target.isMacOSXVersionLT(10, 14, 4);
if (Target.isiOS()) // also returns true on tvOS
return !Target.isOSVersionLT(12, 2);
if (Target.isWatchOS())
return !Target.isOSVersionLT(5, 2);
// Don't assert if we're running on a non-Apple platform; we still
// want to allow running tests that -enable-objc-interop.
return false;
}
bool LangOptions::doesTargetSupportObjCGetClassHook() const {
return doesTargetSupportObjCMetadataUpdateCallback();
}
bool LangOptions::doesTargetSupportObjCClassStubs() const {
if (Target.isMacOSX())
return !Target.isMacOSXVersionLT(10, 15);
if (Target.isiOS()) // also returns true on tvOS
return !Target.isOSVersionLT(13);
if (Target.isWatchOS())
return !Target.isOSVersionLT(6);
// Don't assert if we're running on a non-Apple platform; we still
// want to allow running tests that -enable-objc-interop.
return false;
}