Skip to content

Commit 15932ac

Browse files
jonmeowchandlerc
andauthored
Start filling in extern support on functions. (#3795)
Still needs more merge/redeclaration logic for import semantics, but this felt like a reasonable point to send a PR. --------- Co-authored-by: Chandler Carruth <[email protected]>
1 parent ab2c9dc commit 15932ac

File tree

10 files changed

+335
-18
lines changed

10 files changed

+335
-18
lines changed

toolchain/check/function.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,14 @@ auto CheckFunctionTypeMatches(Context& context,
191191
context.functions().Get(prev_function_id), substitutions);
192192
}
193193

194+
// TODO: Detect conflicting cross-file declarations, as well as uses of imported
195+
// declarations followed by a redeclaration.
194196
auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id,
195197
SemIR::Function& new_function,
196198
SemIR::FunctionId prev_function_id, bool is_definition)
197199
-> bool {
198200
auto& prev_function = context.functions().Get(prev_function_id);
199201

200-
// TODO: Disallow redeclarations within classes?
201202
if (!CheckRedecl(context, new_function, prev_function, {})) {
202203
return false;
203204
}
@@ -224,6 +225,17 @@ auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id,
224225
.Emit();
225226
// The second definition will be unused as a consequence of the error.
226227
return true;
228+
} else if (prev_function.is_extern) {
229+
CARBON_DIAGNOSTIC(FunctionDefiningExtern, Error,
230+
"Cannot define `extern` function `{0}`.", SemIR::NameId);
231+
CARBON_DIAGNOSTIC(FunctionPreviousExternDecl, Note,
232+
"Previously declared `extern` here.");
233+
context.emitter()
234+
.Build(node_id, FunctionDefiningExtern, prev_function.name_id)
235+
.Note(prev_function.decl_id, FunctionPreviousExternDecl)
236+
.Emit();
237+
// The diagnostic doesn't prevent a merge.
238+
return true;
227239
}
228240

229241
// Track the signature from the definition, so that IDs in the body

toolchain/check/handle_function.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "toolchain/check/context.h"
66
#include "toolchain/check/convert.h"
77
#include "toolchain/check/decl_name_stack.h"
8+
#include "toolchain/check/decl_state.h"
89
#include "toolchain/check/function.h"
910
#include "toolchain/check/interface.h"
1011
#include "toolchain/check/modifiers.h"
@@ -39,13 +40,25 @@ auto HandleReturnType(Context& context, Parse::ReturnTypeId node_id) -> bool {
3940
return true;
4041
}
4142

42-
static auto DiagnoseModifiers(Context& context,
43+
static auto DiagnoseModifiers(Context& context, bool is_definition,
4344
SemIR::NameScopeId target_scope_id)
4445
-> KeywordModifierSet {
4546
const Lex::TokenKind decl_kind = Lex::TokenKind::Fn;
4647
CheckAccessModifiersOnDecl(context, decl_kind, target_scope_id);
48+
if (is_definition) {
49+
ForbidExternModifierOnDefinition(context, decl_kind);
50+
}
51+
if (target_scope_id.is_valid()) {
52+
auto target_id = context.name_scopes().Get(target_scope_id).inst_id;
53+
if (target_id.is_valid() &&
54+
!context.insts().Is<SemIR::Namespace>(target_id)) {
55+
ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
56+
" that is a member");
57+
}
58+
}
4759
LimitModifiersOnDecl(context,
48-
KeywordModifierSet::Access | KeywordModifierSet::Method |
60+
KeywordModifierSet::Access | KeywordModifierSet::Extern |
61+
KeywordModifierSet::Method |
4962
KeywordModifierSet::Interface,
5063
decl_kind);
5164
CheckMethodModifiersOnFunction(context, target_scope_id);
@@ -96,17 +109,14 @@ static auto BuildFunctionDecl(Context& context,
96109
.PopAndDiscardSoloNodeId<Parse::NodeKind::FunctionIntroducer>();
97110

98111
// Process modifiers.
99-
auto modifiers = DiagnoseModifiers(context, name_context.target_scope_id);
112+
auto modifiers =
113+
DiagnoseModifiers(context, is_definition, name_context.target_scope_id);
100114
if (!!(modifiers & KeywordModifierSet::Access)) {
101115
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
102116
ModifierOrder::Access),
103117
"access modifier");
104118
}
105-
if (!!(modifiers & KeywordModifierSet::Extern)) {
106-
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
107-
ModifierOrder::Extern),
108-
"extern modifier");
109-
}
119+
bool is_extern = !!(modifiers & KeywordModifierSet::Extern);
110120
if (!!(modifiers & KeywordModifierSet::Method)) {
111121
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
112122
ModifierOrder::Decl),
@@ -132,7 +142,8 @@ static auto BuildFunctionDecl(Context& context,
132142
.implicit_param_refs_id = implicit_param_refs_id,
133143
.param_refs_id = param_refs_id,
134144
.return_type_id = return_type_id,
135-
.return_slot_id = return_slot_id};
145+
.return_slot_id = return_slot_id,
146+
.is_extern = is_extern};
136147
if (is_definition) {
137148
function_info.definition_id = function_info.decl_id;
138149
}

toolchain/check/modifiers.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ auto CheckMethodModifiersOnFunction(Context& context,
136136
" outside of a class");
137137
}
138138

139+
auto ForbidExternModifierOnDefinition(Context& context,
140+
Lex::TokenKind decl_kind) -> void {
141+
ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
142+
" that provides a definition");
143+
}
144+
139145
auto RequireDefaultFinalOnlyInInterfaces(Context& context,
140146
Lex::TokenKind decl_kind,
141147
SemIR::NameScopeId target_scope_id)

toolchain/check/modifiers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ auto CheckMethodModifiersOnFunction(Context& context,
2828
// Like `LimitModifiersOnDecl`, except says which modifiers are forbidden, and a
2929
// `context_string` (and optional `context_node`) specifying the context in
3030
// which those modifiers are forbidden.
31+
// TODO: Take another look at diagnostic phrasing for callers.
3132
auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
3233
Lex::TokenKind decl_kind,
3334
llvm::StringRef context_string,
@@ -42,6 +43,11 @@ inline auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
4243
ForbidModifiersOnDecl(context, ~allowed, decl_kind, "");
4344
}
4445

46+
// If the `extern` modifier is present, diagnoses and updates the declaration
47+
// state to remove it. Only called for declarations with definitions.
48+
auto ForbidExternModifierOnDefinition(Context& context,
49+
Lex::TokenKind decl_kind) -> void;
50+
4551
// Report a diagonostic if `default` and `final` modifiers are used on
4652
// declarations where they are not allowed. Right now they are only allowed
4753
// inside interfaces.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
7+
// --- basic.carbon
8+
9+
library "basic" api;
10+
11+
extern fn F();
12+
13+
// --- fail_redecl.carbon
14+
15+
library "redecl" api;
16+
17+
extern fn F();
18+
// CHECK:STDERR: fail_redecl.carbon:[[@LINE+6]]:1: ERROR: Redundant redeclaration of function F.
19+
// CHECK:STDERR: extern fn F();
20+
// CHECK:STDERR: ^~~~~~~~~~~~~~
21+
// CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: Previously declared here.
22+
// CHECK:STDERR: extern fn F();
23+
// CHECK:STDERR: ^~~~~~~~~~~~~~
24+
extern fn F();
25+
26+
// --- fail_redecl_extern.carbon
27+
28+
library "redecl_extern" api;
29+
30+
extern fn F();
31+
// CHECK:STDERR: fail_redecl_extern.carbon:[[@LINE+6]]:1: ERROR: Redundant redeclaration of function F.
32+
// CHECK:STDERR: fn F();
33+
// CHECK:STDERR: ^~~~~~~
34+
// CHECK:STDERR: fail_redecl_extern.carbon:[[@LINE-4]]:1: Previously declared here.
35+
// CHECK:STDERR: extern fn F();
36+
// CHECK:STDERR: ^~~~~~~~~~~~~~
37+
fn F();
38+
39+
// --- fail_member_extern.carbon
40+
41+
class C {
42+
// CHECK:STDERR: fail_member_extern.carbon:[[@LINE+3]]:3: ERROR: `extern` not allowed on `fn` declaration that is a member.
43+
// CHECK:STDERR: extern fn F();
44+
// CHECK:STDERR: ^~~~~~
45+
extern fn F();
46+
// CHECK:STDERR: fail_member_extern.carbon:[[@LINE+3]]:3: ERROR: `extern` not allowed on `fn` declaration that is a member.
47+
// CHECK:STDERR: extern fn G[self: Self]();
48+
// CHECK:STDERR: ^~~~~~
49+
extern fn G[self: Self]();
50+
}
51+
52+
// CHECK:STDOUT: --- basic.carbon
53+
// CHECK:STDOUT:
54+
// CHECK:STDOUT: file {
55+
// CHECK:STDOUT: package: <namespace> = namespace [template] {
56+
// CHECK:STDOUT: .F = %F
57+
// CHECK:STDOUT: }
58+
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {}
59+
// CHECK:STDOUT: }
60+
// CHECK:STDOUT:
61+
// CHECK:STDOUT: fn @F();
62+
// CHECK:STDOUT:
63+
// CHECK:STDOUT: --- fail_redecl.carbon
64+
// CHECK:STDOUT:
65+
// CHECK:STDOUT: file {
66+
// CHECK:STDOUT: package: <namespace> = namespace [template] {
67+
// CHECK:STDOUT: .F = %F.loc4
68+
// CHECK:STDOUT: }
69+
// CHECK:STDOUT: %F.loc4: <function> = fn_decl @F [template] {}
70+
// CHECK:STDOUT: %F.loc11: <function> = fn_decl @F [template] {}
71+
// CHECK:STDOUT: }
72+
// CHECK:STDOUT:
73+
// CHECK:STDOUT: fn @F();
74+
// CHECK:STDOUT:
75+
// CHECK:STDOUT: --- fail_redecl_extern.carbon
76+
// CHECK:STDOUT:
77+
// CHECK:STDOUT: file {
78+
// CHECK:STDOUT: package: <namespace> = namespace [template] {
79+
// CHECK:STDOUT: .F = %F.loc4
80+
// CHECK:STDOUT: }
81+
// CHECK:STDOUT: %F.loc4: <function> = fn_decl @F [template] {}
82+
// CHECK:STDOUT: %F.loc11: <function> = fn_decl @F [template] {}
83+
// CHECK:STDOUT: }
84+
// CHECK:STDOUT:
85+
// CHECK:STDOUT: fn @F();
86+
// CHECK:STDOUT:
87+
// CHECK:STDOUT: --- fail_member_extern.carbon
88+
// CHECK:STDOUT:
89+
// CHECK:STDOUT: constants {
90+
// CHECK:STDOUT: %C: type = class_type @C [template]
91+
// CHECK:STDOUT: %.1: type = struct_type {} [template]
92+
// CHECK:STDOUT: }
93+
// CHECK:STDOUT:
94+
// CHECK:STDOUT: file {
95+
// CHECK:STDOUT: package: <namespace> = namespace [template] {
96+
// CHECK:STDOUT: .C = %C.decl
97+
// CHECK:STDOUT: }
98+
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
99+
// CHECK:STDOUT: }
100+
// CHECK:STDOUT:
101+
// CHECK:STDOUT: class @C {
102+
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {}
103+
// CHECK:STDOUT: %G: <function> = fn_decl @G [template] {
104+
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [template = constants.%C]
105+
// CHECK:STDOUT: %self.loc10_15.1: C = param self
106+
// CHECK:STDOUT: %self.loc10_15.2: C = bind_name self, %self.loc10_15.1
107+
// CHECK:STDOUT: }
108+
// CHECK:STDOUT:
109+
// CHECK:STDOUT: !members:
110+
// CHECK:STDOUT: .Self = constants.%C
111+
// CHECK:STDOUT: .F = %F
112+
// CHECK:STDOUT: .G = %G
113+
// CHECK:STDOUT: }
114+
// CHECK:STDOUT:
115+
// CHECK:STDOUT: fn @F();
116+
// CHECK:STDOUT:
117+
// CHECK:STDOUT: fn @G[@C.%self.loc10_15.2: C]();
118+
// CHECK:STDOUT:

toolchain/check/testdata/function/declaration/fail_modifiers.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ base fn InvalidModifier();
7070
// CHECK:STDERR: ^~~~~~~
7171
default final virtual fn ModifiersConflict2() {}
7272

73-
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:1: ERROR: `extern` not allowed on `fn` declaration.
73+
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:1: ERROR: `extern` not allowed on `fn` declaration that provides a definition.
7474
// CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
7575
// CHECK:STDERR: ^~~~~~
7676
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:8: ERROR: `private` must appear before `extern`.

0 commit comments

Comments
 (0)