mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
ClangImporter: enhance the importer to alias declarations
Import simple CPP macro aliases as aliases in Swift. Extend the macro
importer to import the following construct:
```
#define alias aliasee
```
as the following Swift construct:
```
@_transparent @inline(__always)
var alias: type(of: aliasee) {
aliasee
}
```
This improves the QoI for Windows where there is a universal define
(`UNICODE`) which normally is used for translating APIs between ANSI and
Unicode variants, e.g.:
```
#if defined(UNICODE)
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
```
Global variables which are non-const also have a setter synthesized:
```
@_transparent @inline(__always)
var alias: type(of: aliasee) {
get { return aliasee }
set { aliasee = newValue }
}
```
This commit is contained in:
committed by
Saleem Abdulrasool
parent
6c9a8b4033
commit
c66d43e1c5
@@ -20,6 +20,7 @@
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/DiagnosticsClangImporter.h"
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/ParameterList.h"
|
||||
#include "swift/AST/Stmt.h"
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/Basic/Assertions.h"
|
||||
@@ -31,6 +32,7 @@
|
||||
#include "clang/Lex/MacroInfo.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Sema/DelayedDiagnostic.h"
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
@@ -371,6 +373,104 @@ getIntegerConstantForMacroToken(ClangImporter::Implementation &impl,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
namespace {
|
||||
ValueDecl *importDeclAlias(ClangImporter::Implementation &clang,
|
||||
swift::DeclContext *DC, const clang::ValueDecl *D,
|
||||
Identifier alias) {
|
||||
// Variadic functions cannot be imported into Swift.
|
||||
// FIXME(compnerd) emit a diagnostic for the missing diagnostic.
|
||||
if (const auto *FD = dyn_cast<clang::FunctionDecl>(D))
|
||||
if (FD->isVariadic())
|
||||
return nullptr;
|
||||
|
||||
// Ignore self-referential macros.
|
||||
if (D->getName() == alias.str())
|
||||
return nullptr;
|
||||
|
||||
swift::ValueDecl *VD =
|
||||
dyn_cast_or_null<ValueDecl>(clang.importDecl(D, clang.CurrentVersion));
|
||||
if (VD == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// If the imported decl is named identically, avoid the aliasing.
|
||||
if (VD->getBaseIdentifier().str() == alias.str())
|
||||
return nullptr;
|
||||
|
||||
swift::ASTContext &Ctx = DC->getASTContext();
|
||||
ImportedType Ty =
|
||||
clang.importType(D->getType(), ImportTypeKind::Abstract,
|
||||
[&clang, &D](Diagnostic &&Diag) {
|
||||
clang.addImportDiagnostic(D, std::move(Diag),
|
||||
D->getLocation());
|
||||
}, /*AllowsNSUIntegerAsInt*/true,
|
||||
Bridgeability::None, { });
|
||||
swift::Type GetterTy = FunctionType::get({}, Ty.getType(), ASTExtInfo{});
|
||||
swift::Type SetterTy =
|
||||
FunctionType::get({AnyFunctionType::Param(Ty.getType())},
|
||||
Ctx.TheEmptyTupleType, ASTExtInfo{});
|
||||
|
||||
/* Storage */
|
||||
swift::VarDecl *V =
|
||||
new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var,
|
||||
SourceLoc(), alias, DC);
|
||||
V->setAccess(swift::AccessLevel::Public);
|
||||
V->setInterfaceType(Ty.getType());
|
||||
V->getAttrs().add(new (Ctx) TransparentAttr(/*Implicit*/true));
|
||||
V->getAttrs().add(new (Ctx) InlineAttr(InlineKind::Always));
|
||||
|
||||
/* Accessor */
|
||||
swift::AccessorDecl *G = nullptr;
|
||||
{
|
||||
G = AccessorDecl::createImplicit(Ctx, AccessorKind::Get, V, false, false,
|
||||
TypeLoc(), GetterTy, DC);
|
||||
G->setAccess(swift::AccessLevel::Public);
|
||||
G->setInterfaceType(GetterTy);
|
||||
G->setIsTransparent(true);
|
||||
G->setParameters(ParameterList::createEmpty(Ctx));
|
||||
|
||||
DeclRefExpr *DRE =
|
||||
new (Ctx) DeclRefExpr(ConcreteDeclRef(VD), {}, /*Implicit*/true,
|
||||
AccessSemantics::Ordinary, Ty.getType());
|
||||
ReturnStmt *RS = ReturnStmt::createImplicit(Ctx, DRE);
|
||||
|
||||
G->setBody(BraceStmt::createImplicit(Ctx, {RS}),
|
||||
AbstractFunctionDecl::BodyKind::TypeChecked);
|
||||
}
|
||||
|
||||
swift::AccessorDecl *S = nullptr;
|
||||
if (isa<clang::VarDecl>(D) &&
|
||||
!cast<clang::VarDecl>(D)->getType().isConstQualified()) {
|
||||
S = AccessorDecl::createImplicit(Ctx, AccessorKind::Set, V, false, false,
|
||||
TypeLoc(), Ctx.TheEmptyTupleType, DC);
|
||||
S->setAccess(swift::AccessLevel::Public);
|
||||
S->setInterfaceType(SetterTy);
|
||||
S->setIsTransparent(true);
|
||||
S->setParameters(ParameterList::create(Ctx, {
|
||||
ParamDecl::createImplicit(Ctx, Identifier(), Ctx.getIdentifier("newValue"),
|
||||
Ty.getType(), DC)
|
||||
}));
|
||||
|
||||
DeclRefExpr *LHS =
|
||||
new (Ctx) DeclRefExpr(ConcreteDeclRef(VD), {}, /*Implicit*/true,
|
||||
AccessSemantics::Ordinary, Ty.getType());
|
||||
DeclRefExpr *RHS =
|
||||
new (Ctx) DeclRefExpr(S->getParameters()->get(0), {}, /*Implicit*/true,
|
||||
AccessSemantics::Ordinary, Ty.getType());
|
||||
AssignExpr *AE = new (Ctx) AssignExpr(LHS, SourceLoc(), RHS, true);
|
||||
AE->setType(Ctx.TheEmptyTupleType);
|
||||
S->setBody(BraceStmt::createImplicit(Ctx, {AE}),
|
||||
AbstractFunctionDecl::BodyKind::TypeChecked);
|
||||
}
|
||||
|
||||
/* Bind */
|
||||
V->setImplInfo(S ? StorageImplInfo::getMutableComputed()
|
||||
: StorageImplInfo::getImmutableComputed());
|
||||
V->setAccessors(SourceLoc(), S ? ArrayRef{G,S} : ArrayRef{G}, SourceLoc());
|
||||
|
||||
return V;
|
||||
}
|
||||
}
|
||||
|
||||
static ValueDecl *importMacro(ClangImporter::Implementation &impl,
|
||||
llvm::SmallSet<StringRef, 4> &visitedMacros,
|
||||
DeclContext *DC, Identifier name,
|
||||
@@ -509,7 +609,14 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: If the identifier refers to a declaration, alias it?
|
||||
/* Create an alias for any Decl */
|
||||
clang::Sema &S = impl.getClangSema();
|
||||
clang::LookupResult R(S, {{tok.getIdentifierInfo()}, {}},
|
||||
clang::Sema::LookupAnyName);
|
||||
if (S.LookupName(R, S.TUScope))
|
||||
if (R.getResultKind() == clang::LookupResult::LookupResultKind::Found)
|
||||
if (const auto *VD = dyn_cast<clang::ValueDecl>(R.getFoundDecl()))
|
||||
return importDeclAlias(impl, DC, VD, name);
|
||||
}
|
||||
|
||||
// TODO(https://github.com/apple/swift/issues/57735): Seems rare to have a single token that is neither a literal nor an identifier, but add diagnosis.
|
||||
|
||||
@@ -105,9 +105,7 @@ public func testRenames(transform: CGAffineTransform, context: CGContext,
|
||||
blackHole(point.applying(transform))
|
||||
var rect = rect.applying(transform)
|
||||
blackHole(size.applying(transform))
|
||||
// CHECK: %{{.*}} = {{(tail )?}}call { double, double } @CGPointApplyAffineTransform(double %{{.*}}, double %{{.*}}, ptr {{.*}})
|
||||
// CHECK: call void @CGRectApplyAffineTransform(ptr {{.*}}, ptr {{.*}}, ptr {{.*}})
|
||||
// CHECK: %{{.*}} = {{(tail )?}}call { double, double } @CGSizeApplyAffineTransform(double %{{.*}}, double %{{.*}}, ptr {{.*}})
|
||||
|
||||
context.concatenate(transform)
|
||||
context.rotate(by: CGFloat.pi)
|
||||
|
||||
53
test/ClangImporter/Inputs/custom-modules/Aliases.h
Normal file
53
test/ClangImporter/Inputs/custom-modules/Aliases.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(UNICODE)
|
||||
#define F FW
|
||||
#define V VW
|
||||
#else
|
||||
#define F FA
|
||||
#define V VA
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define ALIASES_ABI /**/
|
||||
#else
|
||||
#define ALIASES_ABI __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
|
||||
extern ALIASES_ABI const unsigned int VA;
|
||||
extern ALIASES_ABI const unsigned long long VW;
|
||||
|
||||
ALIASES_ABI void FA(unsigned int);
|
||||
ALIASES_ABI void FW(unsigned long long);
|
||||
|
||||
#define InvalidCall DoesNotExist
|
||||
|
||||
extern ALIASES_ABI float UIA;
|
||||
extern ALIASES_ABI double UIW;
|
||||
|
||||
#if defined(UNICODE)
|
||||
#define UI UIW
|
||||
#else
|
||||
#define UI UIA
|
||||
#endif
|
||||
|
||||
enum {
|
||||
ALPHA = 0,
|
||||
#define ALPHA ALPHA
|
||||
BETA = 1,
|
||||
#define BETA BETA
|
||||
};
|
||||
|
||||
enum {
|
||||
_CLOCK_MONOTONIC __attribute__((__swift_name__("CLOCK_MONOTONIC"))),
|
||||
#define CLOCK_MONOTONIC _CLOCK_MONOTONIC
|
||||
} _clock_t;
|
||||
|
||||
enum : int {
|
||||
overloaded,
|
||||
};
|
||||
#define overload overloaded
|
||||
extern const int const_overloaded __attribute__((__swift_name__("overload")));
|
||||
|
||||
void variadic(int count, ...);
|
||||
#define aliased_variadic variadic
|
||||
@@ -275,3 +275,7 @@ module CommonName {
|
||||
module "Weird C Module" {
|
||||
header "WeirdCModule.h"
|
||||
}
|
||||
|
||||
module Aliases {
|
||||
header "Aliases.h"
|
||||
}
|
||||
|
||||
20
test/ClangImporter/alias-invalid.swift
Normal file
20
test/ClangImporter/alias-invalid.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-typecheck-verify-swift -I %S/Inputs/custom-modules
|
||||
|
||||
import Aliases
|
||||
|
||||
func f() {
|
||||
InvalidCall() // expected-error{{cannot find 'InvalidCall' in scope}}
|
||||
}
|
||||
|
||||
func g() {
|
||||
V = 32 // expected-error{{cannot assign to value: 'V' is a get-only property}}
|
||||
}
|
||||
|
||||
func h() {
|
||||
let _ = overload // expected-error{{ambiguous use of 'overload'}}
|
||||
}
|
||||
|
||||
func i() {
|
||||
aliased_variadic(0, 0) // expected-error{{cannot find 'aliased_variadic' in scope}}
|
||||
}
|
||||
64
test/ClangImporter/alias.swift
Normal file
64
test/ClangImporter/alias.swift
Normal file
@@ -0,0 +1,64 @@
|
||||
// RUN: %target-typecheck-verify-swift -I %S/Inputs/custom-modules %s
|
||||
// RUN: %target-swift-frontend -I %S/Inputs/custom-modules -parse-as-library -module-name Alias -Osize -emit-ir -o - %s | %FileCheck %s -check-prefix CHECK-ANSI-IR
|
||||
// RUN: %target-typecheck-verify-swift -I %S/Inputs/custom-modules %s -Xcc -DUNICODE
|
||||
// RUN: %target-swift-frontend -I %S/Inputs/custom-modules -parse-as-library -module-name Alias -Osize -emit-ir -o - %s -Xcc -DUNICODE | %FileCheck %s -check-prefix CHECK-UNICODE-IR
|
||||
// RUN: not %target-swift-frontend -I %S/Inputs/custom-modules -parse-as-library -module-name Alias -c %s -DINVALID -o /dev/null 2>&1 | %FileCheck --dry-run %s -check-prefix CHECK-INVALID
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
import Aliases
|
||||
|
||||
public func f() {
|
||||
F(V)
|
||||
}
|
||||
|
||||
public func g() {
|
||||
UI = 32
|
||||
}
|
||||
|
||||
// CHECK-ANSI-IR: @VA = external {{(dso_local )?}}local_unnamed_addr constant i32
|
||||
// CHECK-ANSI-IR: @UIA = external {{(dso_local )?}}local_unnamed_addr global float
|
||||
|
||||
// CHECK-ANSI-IR: define {{.*}}swiftcc void @"$s5Alias1fyyF"(){{.*}}{
|
||||
// CHECK-ANSI-IR: entry:
|
||||
// CHECK-ANSI-IR: %0 = load i32, ptr @VA
|
||||
// CHECK-ANSI-IR: tail call void @FA(i32 %0)
|
||||
// CHECK-ANSI-IR: ret void
|
||||
// CHECK-ANSI-IR: }
|
||||
|
||||
// CHECK-ANSI-IR: declare {{.*}}void @FA(i32 noundef)
|
||||
// CHECK-ANSI-IR-NOT: declare {{.*}}void @FW(i64 noundef)
|
||||
|
||||
// CHECK-ANSI-IR: define {{.*}}swiftcc void @"$s5Alias1gyyF"(){{.*}}{
|
||||
// CHECK-ANSI-IR: entry:
|
||||
// CHECK-ANSI-IR: store float 3.200000e+01, ptr @UIA
|
||||
// CHECK-ANSI-IR: ret void
|
||||
// CHECK-ANSI-IR: }
|
||||
|
||||
// CHECK-UNICODE-IR: @VW = external {{(dso_local )?}}local_unnamed_addr constant i64
|
||||
// CHECK-UNICODE-IR: @UIW = external {{(dso_local )?}}local_unnamed_addr global double
|
||||
|
||||
// CHECK-UNICODE-IR: define {{.*}}swiftcc void @"$s5Alias1fyyF"(){{.*}}{
|
||||
// CHECK-UNICODE-IR: entry:
|
||||
// CHECK-UNICODE-IR: %0 = load i64, ptr @VW
|
||||
// CHECK-UNICODE-IR: tail call void @FW(i64 %0)
|
||||
// CHECK-UNICODE-IR: ret void
|
||||
// CHECK-UNICODE-IR: }
|
||||
|
||||
// CHECK-UNICODE-IR: declare {{(dso_local )?}}void @FW(i64 noundef)
|
||||
// CHECK-UNICODE-IR-NOT: declare {{(dso_local )?}}void @FA(i32 noundef)
|
||||
|
||||
// CHECK-UNICODE-IR: define {{.*}}swiftcc void @"$s5Alias1gyyF"(){{.*}}{
|
||||
// CHECK-UNICODE-IR: entry:
|
||||
// CHECK-UNICODE-IR: store double 3.200000e+01, ptr @UIW
|
||||
// CHECK-UNICODE-IR: ret void
|
||||
// CHECK-UNICODE-IR: }
|
||||
|
||||
func h() {
|
||||
let _ = CLOCK_MONOTONIC
|
||||
}
|
||||
|
||||
#if INVALID
|
||||
let _ = ALPHA
|
||||
// CHECK-INVALID: error: global variable declaration does not bind any variables
|
||||
#endif
|
||||
Reference in New Issue
Block a user